@@ -35,6 +35,7 @@ import { findGtagScriptOnPage } from './src/helpers';
35
35
import { removeGtagScript } from './testing/gtag-script-util' ;
36
36
import { Deferred } from '@firebase/util' ;
37
37
import { AnalyticsError } from './src/errors' ;
38
+ import { FirebaseInstallations } from '@firebase/installations-types' ;
38
39
39
40
let analyticsInstance : FirebaseAnalytics = { } as FirebaseAnalytics ;
40
41
const fakeMeasurementId = 'abcd-efgh' ;
@@ -44,6 +45,15 @@ const customGtagName = 'customGtag';
44
45
const customDataLayerName = 'customDataLayer' ;
45
46
let clock : sinon . SinonFakeTimers ;
46
47
48
+ // Fake indexedDB.open() request
49
+ let fakeRequest = {
50
+ onsuccess : ( ) => { } ,
51
+ result : {
52
+ close : ( ) => { }
53
+ }
54
+ } ;
55
+ let idbOpenStub = stub ( ) ;
56
+
47
57
function stubFetch ( status : number , body : object ) : void {
48
58
fetchStub = stub ( window , 'fetch' ) ;
49
59
const mockResponse = new Response ( JSON . stringify ( body ) , {
@@ -52,6 +62,18 @@ function stubFetch(status: number, body: object): void {
52
62
fetchStub . returns ( Promise . resolve ( mockResponse ) ) ;
53
63
}
54
64
65
+ // Stub indexedDB.open() because sinon's clock does not know
66
+ // how to wait for the real indexedDB callbacks to resolve.
67
+ function stubIdbOpen ( ) : void {
68
+ ( fakeRequest = {
69
+ onsuccess : ( ) => { } ,
70
+ result : {
71
+ close : ( ) => { }
72
+ }
73
+ } ) ,
74
+ ( idbOpenStub = stub ( indexedDB , 'open' ) . returns ( fakeRequest as any ) ) ;
75
+ }
76
+
55
77
describe ( 'FirebaseAnalytics instance tests' , ( ) => {
56
78
describe ( 'Initialization' , ( ) => {
57
79
beforeEach ( ( ) => resetGlobalVars ( ) ) ;
@@ -83,65 +105,6 @@ describe('FirebaseAnalytics instance tests', () => {
83
105
) ;
84
106
warnStub . restore ( ) ;
85
107
} ) ;
86
- it ( 'Throws if cookies are not enabled' , ( ) => {
87
- const cookieStub = stub ( navigator , 'cookieEnabled' ) . value ( false ) ;
88
- const app = getFakeApp ( {
89
- appId : fakeAppParams . appId ,
90
- apiKey : fakeAppParams . apiKey
91
- } ) ;
92
- const installations = getFakeInstallations ( ) ;
93
- expect ( ( ) => analyticsFactory ( app , installations ) ) . to . throw (
94
- AnalyticsError . COOKIES_NOT_ENABLED
95
- ) ;
96
- cookieStub . restore ( ) ;
97
- } ) ;
98
- it ( 'Throws if browser extension environment' , ( ) => {
99
- window . chrome = { runtime : { id : 'blah' } } ;
100
- const app = getFakeApp ( {
101
- appId : fakeAppParams . appId ,
102
- apiKey : fakeAppParams . apiKey
103
- } ) ;
104
- const installations = getFakeInstallations ( ) ;
105
- expect ( ( ) => analyticsFactory ( app , installations ) ) . to . throw (
106
- AnalyticsError . INVALID_ANALYTICS_CONTEXT
107
- ) ;
108
- window . chrome = undefined ;
109
- } ) ;
110
- it ( 'Throws if indexedDB does not exist' , ( ) => {
111
- const idbStub = stub ( window , 'indexedDB' ) . value ( undefined ) ;
112
- const app = getFakeApp ( {
113
- appId : fakeAppParams . appId ,
114
- apiKey : fakeAppParams . apiKey
115
- } ) ;
116
- const installations = getFakeInstallations ( ) ;
117
- expect ( ( ) => analyticsFactory ( app , installations ) ) . to . throw (
118
- AnalyticsError . INDEXED_DB_UNSUPPORTED
119
- ) ;
120
- idbStub . restore ( ) ;
121
- } ) ;
122
- it ( 'Warns eventually if indexedDB.open() does not work' , async ( ) => {
123
- clock = useFakeTimers ( ) ;
124
- stubFetch ( 200 , { measurementId : fakeMeasurementId } ) ;
125
- const warnStub = stub ( console , 'warn' ) ;
126
- const idbOpenStub = stub ( indexedDB , 'open' ) . throws (
127
- 'idb open throw message'
128
- ) ;
129
- const app = getFakeApp ( {
130
- appId : fakeAppParams . appId ,
131
- apiKey : fakeAppParams . apiKey
132
- } ) ;
133
- const installations = getFakeInstallations ( ) ;
134
- analyticsFactory ( app , installations ) ;
135
- await clock . runAllAsync ( ) ;
136
- expect ( warnStub . args [ 0 ] [ 1 ] ) . to . include (
137
- AnalyticsError . INVALID_INDEXED_DB_CONTEXT
138
- ) ;
139
- expect ( warnStub . args [ 0 ] [ 1 ] ) . to . include ( 'idb open throw message' ) ;
140
- warnStub . restore ( ) ;
141
- idbOpenStub . restore ( ) ;
142
- fetchStub . restore ( ) ;
143
- clock . restore ( ) ;
144
- } ) ;
145
108
it ( 'Throws if creating an instance with already-used appId' , ( ) => {
146
109
const app = getFakeApp ( fakeAppParams ) ;
147
110
const installations = getFakeInstallations ( ) ;
@@ -166,6 +129,7 @@ describe('FirebaseAnalytics instance tests', () => {
166
129
window [ 'gtag' ] = gtagStub ;
167
130
window [ 'dataLayer' ] = [ ] ;
168
131
stubFetch ( 200 , { measurementId : fakeMeasurementId } ) ;
132
+ stubIdbOpen ( ) ;
169
133
analyticsInstance = analyticsFactory ( app , installations ) ;
170
134
} ) ;
171
135
after ( ( ) => {
@@ -174,6 +138,7 @@ describe('FirebaseAnalytics instance tests', () => {
174
138
removeGtagScript ( ) ;
175
139
fetchStub . restore ( ) ;
176
140
clock . restore ( ) ;
141
+ idbOpenStub . restore ( ) ;
177
142
} ) ;
178
143
it ( 'Contains reference to parent app' , ( ) => {
179
144
expect ( analyticsInstance . app ) . to . equal ( app ) ;
@@ -182,6 +147,8 @@ describe('FirebaseAnalytics instance tests', () => {
182
147
analyticsInstance . logEvent ( EventName . ADD_PAYMENT_INFO , {
183
148
currency : 'USD'
184
149
} ) ;
150
+ // Successfully resolves fake IDB open request.
151
+ fakeRequest . onsuccess ( ) ;
185
152
// Clear promise chain started by logEvent.
186
153
await clock . runAllAsync ( ) ;
187
154
expect ( gtagStub ) . to . have . been . calledWith ( 'js' ) ;
@@ -219,6 +186,94 @@ describe('FirebaseAnalytics instance tests', () => {
219
186
} ) ;
220
187
} ) ;
221
188
189
+ describe ( 'Standard app, mismatched environment' , ( ) => {
190
+ let app : FirebaseApp = { } as FirebaseApp ;
191
+ let installations : FirebaseInstallations = { } as FirebaseInstallations ;
192
+ const gtagStub : SinonStub = stub ( ) ;
193
+ let fidDeferred : Deferred < void > ;
194
+ let warnStub : SinonStub ;
195
+ let cookieStub : SinonStub ;
196
+ beforeEach ( ( ) => {
197
+ clock = useFakeTimers ( ) ;
198
+ resetGlobalVars ( ) ;
199
+ app = getFakeApp ( fakeAppParams ) ;
200
+ fidDeferred = new Deferred < void > ( ) ;
201
+ installations = getFakeInstallations ( 'fid-1234' , ( ) =>
202
+ fidDeferred . resolve ( )
203
+ ) ;
204
+ window [ 'gtag' ] = gtagStub ;
205
+ window [ 'dataLayer' ] = [ ] ;
206
+ stubFetch ( 200 , { measurementId : fakeMeasurementId } ) ;
207
+ warnStub = stub ( console , 'warn' ) ;
208
+ stubIdbOpen ( ) ;
209
+ } ) ;
210
+ afterEach ( ( ) => {
211
+ delete window [ 'gtag' ] ;
212
+ delete window [ 'dataLayer' ] ;
213
+ fetchStub . restore ( ) ;
214
+ clock . restore ( ) ;
215
+ warnStub . restore ( ) ;
216
+ idbOpenStub . restore ( ) ;
217
+ gtagStub . resetHistory ( ) ;
218
+ } ) ;
219
+ it ( 'Warns on initialization if cookies not available' , async ( ) => {
220
+ cookieStub = stub ( navigator , 'cookieEnabled' ) . value ( false ) ;
221
+ analyticsInstance = analyticsFactory ( app , installations ) ;
222
+ expect ( warnStub . args [ 0 ] [ 1 ] ) . to . include (
223
+ AnalyticsError . INVALID_ANALYTICS_CONTEXT
224
+ ) ;
225
+ expect ( warnStub . args [ 0 ] [ 1 ] ) . to . include ( 'Cookies' ) ;
226
+ cookieStub . restore ( ) ;
227
+ } ) ;
228
+ it ( 'Warns on initialization if in browser extension' , async ( ) => {
229
+ window . chrome = { runtime : { id : 'blah' } } ;
230
+ analyticsInstance = analyticsFactory ( app , installations ) ;
231
+ expect ( warnStub . args [ 0 ] [ 1 ] ) . to . include (
232
+ AnalyticsError . INVALID_ANALYTICS_CONTEXT
233
+ ) ;
234
+ expect ( warnStub . args [ 0 ] [ 1 ] ) . to . include ( 'browser extension' ) ;
235
+ window . chrome = undefined ;
236
+ } ) ;
237
+ it ( 'Warns on logEvent if indexedDB API not available' , async ( ) => {
238
+ const idbStub = stub ( window , 'indexedDB' ) . value ( undefined ) ;
239
+ analyticsInstance = analyticsFactory ( app , installations ) ;
240
+ analyticsInstance . logEvent ( EventName . ADD_PAYMENT_INFO , {
241
+ currency : 'USD'
242
+ } ) ;
243
+ // Clear promise chain started by logEvent.
244
+ await clock . runAllAsync ( ) ;
245
+ // gtag config call omits FID
246
+ expect ( gtagStub ) . to . be . calledWith ( 'config' , 'abcd-efgh' , {
247
+ update : true ,
248
+ origin : 'firebase'
249
+ } ) ;
250
+ expect ( warnStub . args [ 0 ] [ 1 ] ) . to . include (
251
+ AnalyticsError . INDEXEDDB_UNAVAILABLE
252
+ ) ;
253
+ expect ( warnStub . args [ 0 ] [ 1 ] ) . to . include ( 'IndexedDB is not available' ) ;
254
+ idbStub . restore ( ) ;
255
+ } ) ;
256
+ it ( 'Warns on logEvent if indexedDB.open() not allowed' , async ( ) => {
257
+ idbOpenStub . restore ( ) ;
258
+ idbOpenStub = stub ( indexedDB , 'open' ) . throws ( 'idb open error test' ) ;
259
+ analyticsInstance = analyticsFactory ( app , installations ) ;
260
+ analyticsInstance . logEvent ( EventName . ADD_PAYMENT_INFO , {
261
+ currency : 'USD'
262
+ } ) ;
263
+ // Clear promise chain started by logEvent.
264
+ await clock . runAllAsync ( ) ;
265
+ // gtag config call omits FID
266
+ expect ( gtagStub ) . to . be . calledWith ( 'config' , 'abcd-efgh' , {
267
+ update : true ,
268
+ origin : 'firebase'
269
+ } ) ;
270
+ expect ( warnStub . args [ 0 ] [ 1 ] ) . to . include (
271
+ AnalyticsError . INDEXEDDB_UNAVAILABLE
272
+ ) ;
273
+ expect ( warnStub . args [ 0 ] [ 1 ] ) . to . include ( 'idb open error test' ) ;
274
+ } ) ;
275
+ } ) ;
276
+
222
277
describe ( 'Page has user gtag script with custom gtag and dataLayer names' , ( ) => {
223
278
let app : FirebaseApp = { } as FirebaseApp ;
224
279
let fidDeferred : Deferred < void > ;
@@ -237,6 +292,7 @@ describe('FirebaseAnalytics instance tests', () => {
237
292
dataLayerName : customDataLayerName ,
238
293
gtagName : customGtagName
239
294
} ) ;
295
+ stubIdbOpen ( ) ;
240
296
stubFetch ( 200 , { measurementId : fakeMeasurementId } ) ;
241
297
analyticsInstance = analyticsFactory ( app , installations ) ;
242
298
} ) ;
@@ -246,11 +302,14 @@ describe('FirebaseAnalytics instance tests', () => {
246
302
removeGtagScript ( ) ;
247
303
fetchStub . restore ( ) ;
248
304
clock . restore ( ) ;
305
+ idbOpenStub . restore ( ) ;
249
306
} ) ;
250
307
it ( 'Calls gtag correctly on logEvent (instance)' , async ( ) => {
251
308
analyticsInstance . logEvent ( EventName . ADD_PAYMENT_INFO , {
252
309
currency : 'USD'
253
310
} ) ;
311
+ // Successfully resolves fake IDB open request.
312
+ fakeRequest . onsuccess ( ) ;
254
313
// Clear promise chain started by logEvent.
255
314
await clock . runAllAsync ( ) ;
256
315
expect ( gtagStub ) . to . have . been . calledWith ( 'js' ) ;
@@ -280,9 +339,12 @@ describe('FirebaseAnalytics instance tests', () => {
280
339
const app = getFakeApp ( fakeAppParams ) ;
281
340
const installations = getFakeInstallations ( ) ;
282
341
stubFetch ( 200 , { } ) ;
342
+ stubIdbOpen ( ) ;
283
343
analyticsInstance = analyticsFactory ( app , installations ) ;
284
344
285
345
const { initializationPromisesMap } = getGlobalVars ( ) ;
346
+ // Successfully resolves fake IDB open request.
347
+ fakeRequest . onsuccess ( ) ;
286
348
await initializationPromisesMap [ fakeAppParams . appId ] ;
287
349
expect ( findGtagScriptOnPage ( ) ) . to . not . be . null ;
288
350
expect ( typeof window [ 'gtag' ] ) . to . equal ( 'function' ) ;
@@ -292,6 +354,7 @@ describe('FirebaseAnalytics instance tests', () => {
292
354
delete window [ 'dataLayer' ] ;
293
355
removeGtagScript ( ) ;
294
356
fetchStub . restore ( ) ;
357
+ idbOpenStub . restore ( ) ;
295
358
} ) ;
296
359
} ) ;
297
360
} ) ;
0 commit comments