@@ -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