11import React , { useContext } from 'react' ;
2- import { act , render , screen } from '@testing-library/react' ;
3- import { initialize } from '@googlemaps/jest-mocks' ;
2+ import { act , cleanup , render , screen , waitFor } from '@testing-library/react' ;
3+ import {
4+ importLibrary as importLibraryMock ,
5+ initialize
6+ } from '@googlemaps/jest-mocks' ;
47import '@testing-library/jest-dom' ;
58
6- // FIXME: this should no longer be needed with the next version of @googlemaps /jest-mocks
7- import { importLibraryMock } from '../../libraries/__mocks__/lib/import-library-mock' ;
8-
99import { VERSION } from '../../version' ;
1010import {
11+ __resetModuleState as resetAPIProviderState ,
1112 APIProvider ,
1213 APIProviderContext ,
1314 APIProviderContextValue
1415} from '../api-provider' ;
15- import { ApiParams } from '../../libraries/google-maps-api-loader' ;
16+
1617import { useApiIsLoaded } from '../../hooks/use-api-is-loaded' ;
1718import { APILoadingStatus } from '../../libraries/api-loading-status' ;
1819
19- const apiLoadSpy = jest . fn ( ) ;
20- const apiUnloadSpy = jest . fn ( ) ;
20+ type ImportLibraryResult = Awaited <
21+ ReturnType < typeof google . maps . importLibrary >
22+ > ;
23+
24+ let importLibraryPromise : Promise < ImportLibraryResult > ;
25+ let resolveImportLibrary : ( value : ImportLibraryResult ) => void ;
26+ let rejectImportLibrary : ( reason ?: unknown ) => void ;
27+
28+ const resetImportLibraryPromise = ( ) => {
29+ ( {
30+ promise : importLibraryPromise ,
31+ resolve : resolveImportLibrary ,
32+ reject : rejectImportLibrary
33+ } = Promise . withResolvers < ImportLibraryResult > ( ) ) ;
34+ } ;
35+
36+ const triggerMapsApiLoaded = ( ) => {
37+ resolveImportLibrary ( { } as google . maps . CoreLibrary ) ;
38+ } ;
39+
40+ const triggerLoadingFailed = ( ) => {
41+ rejectImportLibrary ( new Error ( 'loading failed' ) ) ;
42+ } ;
43+
44+ const setOptionsSpy = jest . fn ( ) ;
45+
46+ jest . mock ( '@googlemaps/js-api-loader' , ( ) => {
47+ return {
48+ setOptions : jest . fn ( ( options : Record < string , unknown > ) => {
49+ setOptionsSpy ( options ) ;
50+ } ) ,
51+ importLibrary : jest . fn ( async ( name : string ) => {
52+ await importLibraryPromise ;
53+
54+ return importLibraryMock ( name ) ;
55+ } )
56+ } ;
57+ } ) ;
2158
2259const ContextSpyComponent = ( ) => {
2360 const context = useContext ( APIProviderContext ) ;
@@ -27,47 +64,20 @@ const ContextSpyComponent = () => {
2764} ;
2865ContextSpyComponent . spy = jest . fn ( ) ;
2966
30- let triggerMapsApiLoaded : ( ) => void ;
31- let triggerLoadingFailed : ( ) => void ;
32-
33- jest . mock ( '../../libraries/google-maps-api-loader' , ( ) => {
34- class GoogleMapsApiLoader {
35- static async load (
36- params : ApiParams ,
37- onLoadingStatusChange : ( s : APILoadingStatus ) => void
38- ) : Promise < void > {
39- apiLoadSpy ( params ) ;
40- onLoadingStatusChange ( APILoadingStatus . LOADING ) ;
41-
42- google . maps . importLibrary = importLibraryMock ;
43-
44- return new Promise ( ( resolve , reject ) => {
45- triggerLoadingFailed = ( ) => {
46- reject ( ) ;
47- onLoadingStatusChange ( APILoadingStatus . FAILED ) ;
48- } ;
49-
50- triggerMapsApiLoaded = ( ) => {
51- resolve ( ) ;
52- onLoadingStatusChange ( APILoadingStatus . LOADED ) ;
53- } ;
54- } ) ;
55- }
56-
57- static unload ( ) {
58- apiUnloadSpy ( ) ;
59- }
60- }
61-
62- return { __esModule : true , GoogleMapsApiLoader} ;
63- } ) ;
64-
6567beforeEach ( ( ) => {
6668 initialize ( ) ;
6769 jest . clearAllMocks ( ) ;
70+ // @ts -expect-error - accessing mock implementation
71+ window . google . maps . importLibrary = undefined ;
72+ resetAPIProviderState ( ) ;
73+ resetImportLibraryPromise ( ) ;
6874} ) ;
6975
70- test ( 'passes parameters to GoogleMapsAPILoader' , ( ) => {
76+ afterEach ( ( ) => {
77+ cleanup ( ) ;
78+ } ) ;
79+
80+ test ( 'passes parameters to GoogleMapsAPILoader' , async ( ) => {
7181 render (
7282 < APIProvider
7383 apiKey = { 'apikey' }
@@ -79,9 +89,11 @@ test('passes parameters to GoogleMapsAPILoader', () => {
7989 authReferrerPolicy = { 'origin' } > </ APIProvider >
8090 ) ;
8191
82- expect ( apiLoadSpy . mock . lastCall [ 0 ] ) . toMatchObject ( {
92+ await waitFor ( ( ) => expect ( setOptionsSpy ) . toHaveBeenCalled ( ) ) ;
93+
94+ expect ( setOptionsSpy . mock . lastCall [ 0 ] ) . toMatchObject ( {
8395 key : 'apikey' ,
84- libraries : 'places, marker' ,
96+ libraries : [ 'places' , ' marker'] ,
8597 v : 'beta' ,
8698 language : 'en' ,
8799 region : 'us' ,
@@ -90,24 +102,30 @@ test('passes parameters to GoogleMapsAPILoader', () => {
90102 } ) ;
91103} ) ;
92104
93- test ( 'passes parameters to GoogleMapsAPILoader' , ( ) => {
105+ test ( 'passes parameters to GoogleMapsAPILoader' , async ( ) => {
94106 render ( < APIProvider apiKey = { 'apikey' } version = { 'version' } > </ APIProvider > ) ;
95107
96- const actual = apiLoadSpy . mock . lastCall [ 0 ] ;
108+ await waitFor ( ( ) => expect ( setOptionsSpy ) . toHaveBeenCalled ( ) ) ;
109+
110+ const actual = setOptionsSpy . mock . lastCall [ 0 ] ;
97111 expect ( actual ) . toMatchObject ( { key : 'apikey' , v : 'version' } ) ;
98112} ) ;
99113
100- test ( 'uses default solutionChannel' , ( ) => {
114+ test ( 'uses default solutionChannel' , async ( ) => {
101115 render ( < APIProvider apiKey = { 'apikey' } > </ APIProvider > ) ;
102116
103- const actual = apiLoadSpy . mock . lastCall [ 0 ] ;
117+ await waitFor ( ( ) => expect ( setOptionsSpy ) . toHaveBeenCalled ( ) ) ;
118+
119+ const actual = setOptionsSpy . mock . lastCall [ 0 ] ;
104120 expect ( actual . solutionChannel ) . toBe ( 'GMP_visgl_rgmlibrary_v1_default' ) ;
105121} ) ;
106122
107- test ( "doesn't set solutionChannel when specified as empty string" , ( ) => {
123+ test ( "doesn't set solutionChannel when specified as empty string" , async ( ) => {
108124 render ( < APIProvider apiKey = { 'apikey' } solutionChannel = { '' } > </ APIProvider > ) ;
109125
110- const actual = apiLoadSpy . mock . lastCall [ 0 ] ;
126+ await waitFor ( ( ) => expect ( setOptionsSpy ) . toHaveBeenCalled ( ) ) ;
127+
128+ const actual = setOptionsSpy . mock . lastCall [ 0 ] ;
111129 expect ( actual ) . not . toHaveProperty ( 'solutionChannel' ) ;
112130} ) ;
113131
@@ -125,7 +143,9 @@ test('renders inner components', async () => {
125143
126144 expect ( screen . getByText ( 'not loaded' ) ) . toBeInTheDocument ( ) ;
127145
128- await act ( ( ) => triggerMapsApiLoaded ( ) ) ;
146+ await act ( async ( ) => {
147+ triggerMapsApiLoaded ( ) ;
148+ } ) ;
129149
130150 expect ( screen . getByText ( 'loaded' ) ) . toBeInTheDocument ( ) ;
131151} ) ;
@@ -145,7 +165,10 @@ test('provides context values', async () => {
145165 expect ( actualContext . mapInstances ) . toEqual ( { } ) ;
146166
147167 contextSpy . mockReset ( ) ;
148- await act ( ( ) => triggerMapsApiLoaded ( ) ) ;
168+
169+ await act ( async ( ) => {
170+ triggerMapsApiLoaded ( ) ;
171+ } ) ;
149172
150173 expect ( contextSpy ) . toHaveBeenCalled ( ) ;
151174
@@ -192,9 +215,9 @@ test('calls onError when loading the Google Maps JavaScript API fails', async ()
192215
193216 render ( < APIProvider apiKey = { 'apikey' } onError = { onErrorMock } > </ APIProvider > ) ;
194217
195- await act ( ( ) => triggerLoadingFailed ( ) ) ;
218+ triggerLoadingFailed ( ) ;
196219
197- expect ( onErrorMock ) . toHaveBeenCalled ( ) ;
220+ await waitFor ( ( ) => expect ( onErrorMock ) . toHaveBeenCalled ( ) ) ;
198221} ) ;
199222
200223describe ( 'internalUsageAttributionIds' , ( ) => {
0 commit comments