Skip to content

Commit 01ff081

Browse files
committed
(#321) Added findAll to Screen
1 parent 5798a84 commit 01ff081

File tree

2 files changed

+337
-33
lines changed

2 files changed

+337
-33
lines changed

lib/screen.class.spec.ts

+254-4
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,10 @@ describe("Screen.", () => {
108108
it("should reject with insufficient confidence.", async () => {
109109

110110
// GIVEN
111-
const matchResult = new MatchResult(0.8, searchRegion);
112-
const findMatchMock = jest.fn(() => Promise.resolve(matchResult));
111+
const minConfidence = 0.95;
112+
const failingConfidence = 0.8;
113+
const expectedReason = `No match with required confidence ${minConfidence}. Best match: ${failingConfidence}`;
114+
const findMatchMock = jest.fn(() => Promise.reject(expectedReason));
113115
providerRegistryMock.getImageFinder = jest.fn(() => mockPartial<ImageFinderInterface>({
114116
findMatch: findMatchMock
115117
}));
@@ -119,12 +121,12 @@ describe("Screen.", () => {
119121
const needle = new Image(100, 100, Buffer.from([]), 3, id);
120122

121123
// WHEN
122-
const resultRegion = SUT.find(needle);
124+
const resultRegion = SUT.find(needle, {confidence: minConfidence});
123125

124126
// THEN
125127
await expect(resultRegion)
126128
.rejects
127-
.toEqual(`No match for ${id}. Required: ${SUT.config.confidence}, given: ${matchResult.confidence}`);
129+
.toEqual(`Searching for ${id} failed. Reason: '${expectedReason}'`);
128130
});
129131

130132
it("should reject when search fails.", async () => {
@@ -327,6 +329,254 @@ describe("Screen.", () => {
327329
})
328330
});
329331

332+
describe("findAll", () => {
333+
it("should call registered hook before resolve", async () => {
334+
// GIVEN
335+
const matchResult = new MatchResult(0.99, searchRegion);
336+
const matchResults = [matchResult, matchResult, matchResult];
337+
const findMatchesMock = jest.fn(() => Promise.resolve(matchResults));
338+
providerRegistryMock.getImageFinder = jest.fn(() => mockPartial<ImageFinderInterface>({
339+
findMatches: findMatchesMock
340+
}));
341+
342+
const SUT = new ScreenClass(providerRegistryMock);
343+
const testCallback = jest.fn(() => Promise.resolve());
344+
const needle = new Image(100, 100, Buffer.from([]), 3, "needle_image");
345+
SUT.on(needle, testCallback);
346+
347+
// WHEN
348+
await SUT.findAll(needle);
349+
350+
// THEN
351+
expect(testCallback).toBeCalledTimes(matchResults.length);
352+
expect(testCallback).toBeCalledWith(matchResult);
353+
});
354+
355+
it("should call multiple registered hooks before resolve", async () => {
356+
// GIVEN
357+
const matchResult = new MatchResult(0.99, searchRegion);
358+
const matchResults = [matchResult, matchResult, matchResult];
359+
const findMatchesMock = jest.fn(() => Promise.resolve(matchResults));
360+
providerRegistryMock.getImageFinder = jest.fn(() => mockPartial<ImageFinderInterface>({
361+
findMatches: findMatchesMock
362+
}));
363+
364+
const SUT = new ScreenClass(providerRegistryMock);
365+
const testCallback = jest.fn(() => Promise.resolve());
366+
const secondCallback = jest.fn(() => Promise.resolve());
367+
const needle = new Image(100, 100, Buffer.from([]), 3, "needle_image");
368+
SUT.on(needle, testCallback);
369+
SUT.on(needle, secondCallback);
370+
371+
// WHEN
372+
await SUT.findAll(needle);
373+
374+
// THEN
375+
for (const callback of [testCallback, secondCallback]) {
376+
expect(callback).toBeCalledTimes(matchResults.length);
377+
expect(callback).toBeCalledWith(matchResult);
378+
}
379+
});
380+
381+
it("should reject when search fails.", async () => {
382+
383+
// GIVEN
384+
const rejectionReason = "Search failed.";
385+
const findMatchesMock = jest.fn(() => Promise.reject(rejectionReason));
386+
providerRegistryMock.getImageFinder = jest.fn(() => mockPartial<ImageFinderInterface>({
387+
findMatches: findMatchesMock
388+
}));
389+
390+
const SUT = new ScreenClass(providerRegistryMock);
391+
const id = "needle_image";
392+
const needle = new Image(100, 100, Buffer.from([]), 3, id);
393+
394+
// WHEN
395+
const resultRegion = SUT.findAll(needle);
396+
397+
// THEN
398+
await expect(resultRegion)
399+
.rejects
400+
.toEqual(`Searching for ${id} failed. Reason: '${rejectionReason}'`);
401+
});
402+
403+
it("should override default confidence value with parameter.", async () => {
404+
// GIVEN
405+
const minMatch = 0.8;
406+
const matchResult = new MatchResult(minMatch, searchRegion);
407+
408+
const findMatchesMock = jest.fn(() => Promise.resolve([matchResult]));
409+
providerRegistryMock.getImageFinder = jest.fn(() => mockPartial<ImageFinderInterface>({
410+
findMatches: findMatchesMock
411+
}));
412+
413+
const SUT = new ScreenClass(providerRegistryMock);
414+
415+
const needle = new Image(100, 100, Buffer.from([]), 3, "needle_image");
416+
const parameters = new LocationParameters(undefined, minMatch);
417+
418+
// WHEN
419+
const [resultRegion] = await SUT.findAll(needle, parameters);
420+
421+
// THEN
422+
expect(resultRegion).toEqual(matchResult.location);
423+
const matchRequest = new MatchRequest(
424+
expect.any(Image),
425+
needle,
426+
minMatch,
427+
true);
428+
expect(findMatchesMock).toHaveBeenCalledWith(matchRequest);
429+
});
430+
431+
it("should override default search region with parameter.", async () => {
432+
// GIVEN
433+
const customSearchRegion = new Region(10, 10, 90, 90);
434+
const matchResult = new MatchResult(0.99, searchRegion);
435+
436+
const findMatchesMock = jest.fn(() => Promise.resolve([matchResult]));
437+
providerRegistryMock.getImageFinder = jest.fn(() => mockPartial<ImageFinderInterface>({
438+
findMatches: findMatchesMock
439+
}));
440+
441+
const SUT = new ScreenClass(providerRegistryMock);
442+
443+
const needle = new Image(100, 100, Buffer.from([]), 3, "needle_image");
444+
const parameters = new LocationParameters(customSearchRegion);
445+
const expectedMatchRequest = new MatchRequest(
446+
expect.any(Image),
447+
needle,
448+
SUT.config.confidence,
449+
true);
450+
451+
// WHEN
452+
await SUT.findAll(needle, parameters);
453+
454+
// THEN
455+
expect(findMatchesMock).toHaveBeenCalledWith(expectedMatchRequest);
456+
});
457+
458+
it("should override searchMultipleScales with parameter.", async () => {
459+
// GIVEN
460+
const matchResult = new MatchResult(0.99, searchRegion);
461+
const findMatchesMock = jest.fn(() => Promise.resolve([matchResult]));
462+
providerRegistryMock.getImageFinder = jest.fn(() => mockPartial<ImageFinderInterface>({
463+
findMatches: findMatchesMock
464+
}));
465+
466+
const SUT = new ScreenClass(providerRegistryMock);
467+
const needle = new Image(100, 100, Buffer.from([]), 3, "needle_image");
468+
469+
const parameters = new LocationParameters(searchRegion, undefined, false);
470+
const expectedMatchRequest = new MatchRequest(
471+
expect.any(Image),
472+
needle,
473+
SUT.config.confidence,
474+
false);
475+
476+
// WHEN
477+
await SUT.findAll(needle, parameters);
478+
479+
// THEN
480+
expect(findMatchesMock).toHaveBeenCalledWith(expectedMatchRequest);
481+
});
482+
483+
it("should override both confidence and search region with parameter.", async () => {
484+
// GIVEN
485+
const minMatch = 0.8;
486+
const customSearchRegion = new Region(10, 10, 90, 90);
487+
const matchResult = new MatchResult(minMatch, searchRegion);
488+
const findMatchesMock = jest.fn(() => Promise.resolve([matchResult]));
489+
providerRegistryMock.getImageFinder = jest.fn(() => mockPartial<ImageFinderInterface>({
490+
findMatches: findMatchesMock
491+
}));
492+
493+
const SUT = new ScreenClass(providerRegistryMock);
494+
const needle = new Image(100, 100, Buffer.from([]), 3, "needle_image");
495+
const parameters = new LocationParameters(customSearchRegion, minMatch);
496+
const expectedMatchRequest = new MatchRequest(
497+
expect.any(Image),
498+
needle,
499+
minMatch,
500+
true);
501+
502+
// WHEN
503+
await SUT.findAll(needle, parameters);
504+
505+
// THEN
506+
expect(findMatchesMock).toHaveBeenCalledWith(expectedMatchRequest);
507+
});
508+
509+
it("should add search region offset to result image location", async () => {
510+
// GIVEN
511+
const limitedSearchRegion = new Region(100, 200, 300, 400);
512+
const resultRegion = new Region(50, 100, 150, 200);
513+
const matchResult = new MatchResult(0.99, resultRegion);
514+
515+
const expectedMatchRegion = new Region(
516+
limitedSearchRegion.left + resultRegion.left,
517+
limitedSearchRegion.top + resultRegion.top,
518+
resultRegion.width,
519+
resultRegion.height);
520+
521+
const findMatchesMock = jest.fn(() => Promise.resolve([matchResult]));
522+
providerRegistryMock.getImageFinder = jest.fn(() => mockPartial<ImageFinderInterface>({
523+
findMatches: findMatchesMock
524+
}));
525+
526+
const SUT = new ScreenClass(providerRegistryMock);
527+
// WHEN
528+
const [matchRegion] = await SUT.findAll(
529+
new Image(100, 100, Buffer.from([]), 3, "needle_image"),
530+
{
531+
searchRegion: limitedSearchRegion
532+
}
533+
);
534+
535+
// THEN
536+
expect(matchRegion).toEqual(expectedMatchRegion);
537+
})
538+
539+
it.each([
540+
["with negative x coordinate", new Region(-1, 0, 100, 100)],
541+
["with negative y coordinate", new Region(0, -1, 100, 100)],
542+
["with negative width", new Region(0, 0, -100, 100)],
543+
["with negative height", new Region(0, 0, 100, -100)],
544+
["with region outside screen on x axis", new Region(1100, 0, 100, 100)],
545+
["with region outside screen on y axis", new Region(0, 1100, 100, 100)],
546+
["with region bigger than screen on x axis", new Region(0, 0, 1100, 100)],
547+
["with region bigger than screen on y axis", new Region(0, 0, 1000, 1100)],
548+
["with region of 1 px width", new Region(0, 0, 1, 1100)],
549+
["with region of 1 px height", new Region(0, 0, 100, 1)],
550+
["with region leaving screen on x axis", new Region(600, 0, 500, 100)],
551+
["with region leaving screen on y axis", new Region(0, 500, 100, 600)],
552+
["with NaN x coordinate", new Region("a" as unknown as number, 0, 100, 100)],
553+
["with NaN y coordinate", new Region(0, "a" as unknown as number, 100, 600)],
554+
["with NaN on width", new Region(0, 0, "a" as unknown as number, 100)],
555+
["with NaN on height", new Region(0, 0, 100, "a" as unknown as number)],
556+
])("should reject search regions %s", async (_, region) => {
557+
// GIVEN
558+
const id = "needle_image";
559+
const needle = new Image(100, 100, Buffer.from([]), 3, id);
560+
const matchResult = new MatchResult(0.99, region);
561+
const findMatchesMock = jest.fn(() => Promise.resolve([matchResult]));
562+
providerRegistryMock.getImageFinder = jest.fn(() => mockPartial<ImageFinderInterface>({
563+
findMatches: findMatchesMock
564+
}));
565+
566+
const SUT = new ScreenClass(providerRegistryMock);
567+
568+
// WHEN
569+
const findPromise = SUT.findAll(
570+
needle,
571+
{
572+
searchRegion: region
573+
});
574+
575+
// THEN
576+
await expect(findPromise).rejects.toContain(`Searching for ${id} failed. Reason:`);
577+
})
578+
});
579+
330580
it("should return region to highlight for chaining", async () => {
331581
// GIVEN
332582
const highlightRegion = new Region(10, 20, 30, 40);

0 commit comments

Comments
 (0)