16
16
*/
17
17
import '../test/setup' ;
18
18
import { expect } from 'chai' ;
19
- import { stub } from 'sinon' ;
20
- import { setTokenAutoRefreshEnabled , initializeAppCheck } from './api' ;
21
- import { FAKE_SITE_KEY , getFullApp , getFakeApp } from '../test/util' ;
22
- import { getState } from './state' ;
19
+ import { match , spy , stub } from 'sinon' ;
20
+ import {
21
+ setTokenAutoRefreshEnabled ,
22
+ initializeAppCheck ,
23
+ getToken ,
24
+ onTokenChanged
25
+ } from './api' ;
26
+ import {
27
+ FAKE_SITE_KEY ,
28
+ getFullApp ,
29
+ getFakeApp ,
30
+ getFakeGreCAPTCHA ,
31
+ getFakeAppCheck ,
32
+ getFakePlatformLoggingProvider ,
33
+ removegreCAPTCHAScriptsOnPage
34
+ } from '../test/util' ;
35
+ import { clearState , getState } from './state' ;
23
36
import * as reCAPTCHA from './recaptcha' ;
37
+ import * as util from './util' ;
38
+ import * as logger from './logger' ;
39
+ import * as client from './client' ;
40
+ import * as storage from './storage' ;
41
+ import * as internalApi from './internal-api' ;
24
42
import { deleteApp , FirebaseApp } from '@firebase/app-exp' ;
25
43
import { ReCaptchaV3Provider } from './providers' ;
26
44
@@ -29,9 +47,12 @@ describe('api', () => {
29
47
30
48
beforeEach ( ( ) => {
31
49
app = getFullApp ( ) ;
50
+ stub ( util , 'getRecaptcha' ) . returns ( getFakeGreCAPTCHA ( ) ) ;
32
51
} ) ;
33
52
34
53
afterEach ( ( ) => {
54
+ clearState ( ) ;
55
+ removegreCAPTCHAScriptsOnPage ( ) ;
35
56
return deleteApp ( app ) ;
36
57
} ) ;
37
58
@@ -88,8 +109,167 @@ describe('api', () => {
88
109
describe ( 'setTokenAutoRefreshEnabled()' , ( ) => {
89
110
it ( 'sets isTokenAutoRefreshEnabled correctly' , ( ) => {
90
111
const app = getFakeApp ( { automaticDataCollectionEnabled : false } ) ;
91
- setTokenAutoRefreshEnabled ( app , true ) ;
112
+ const appCheck = getFakeAppCheck ( app ) ;
113
+ setTokenAutoRefreshEnabled ( appCheck , true ) ;
92
114
expect ( getState ( app ) . isTokenAutoRefreshEnabled ) . to . equal ( true ) ;
93
115
} ) ;
94
116
} ) ;
117
+ describe ( 'getToken()' , ( ) => {
118
+ it ( 'getToken() calls the internal getToken() function' , async ( ) => {
119
+ const app = getFakeApp ( { automaticDataCollectionEnabled : true } ) ;
120
+ const appCheck = getFakeAppCheck ( app ) ;
121
+ const internalGetToken = stub ( internalApi , 'getToken' ) . resolves ( {
122
+ token : 'a-token-string'
123
+ } ) ;
124
+ await getToken ( appCheck , true ) ;
125
+ expect ( internalGetToken ) . to . be . calledWith (
126
+ appCheck . app ,
127
+ match . any , // platformLoggerProvider
128
+ true
129
+ ) ;
130
+ } ) ;
131
+ it ( 'getToken() throws errors returned with token' , async ( ) => {
132
+ const app = getFakeApp ( { automaticDataCollectionEnabled : true } ) ;
133
+ const appCheck = getFakeAppCheck ( app ) ;
134
+ // If getToken() errors, it returns a dummy token with an error field
135
+ // instead of throwing.
136
+ stub ( internalApi , 'getToken' ) . resolves ( {
137
+ token : 'a-dummy-token' ,
138
+ error : Error ( 'there was an error' )
139
+ } ) ;
140
+ await expect ( getToken ( appCheck , true ) ) . to . be . rejectedWith (
141
+ 'there was an error'
142
+ ) ;
143
+ } ) ;
144
+ } ) ;
145
+ describe ( 'onTokenChanged()' , ( ) => {
146
+ it ( 'Listeners work when using top-level parameters pattern' , async ( ) => {
147
+ const appCheck = initializeAppCheck ( app , {
148
+ provider : new ReCaptchaV3Provider ( FAKE_SITE_KEY ) ,
149
+ isTokenAutoRefreshEnabled : true
150
+ } ) ;
151
+ const fakeRecaptchaToken = 'fake-recaptcha-token' ;
152
+ const fakeRecaptchaAppCheckToken = {
153
+ token : 'fake-recaptcha-app-check-token' ,
154
+ expireTimeMillis : 123 ,
155
+ issuedAtTimeMillis : 0
156
+ } ;
157
+ stub ( reCAPTCHA , 'getToken' ) . returns ( Promise . resolve ( fakeRecaptchaToken ) ) ;
158
+ stub ( client , 'exchangeToken' ) . returns (
159
+ Promise . resolve ( fakeRecaptchaAppCheckToken )
160
+ ) ;
161
+ stub ( storage , 'writeTokenToStorage' ) . returns ( Promise . resolve ( undefined ) ) ;
162
+
163
+ const listener1 = ( ) : void => {
164
+ throw new Error ( ) ;
165
+ } ;
166
+ const listener2 = spy ( ) ;
167
+
168
+ const errorFn1 = spy ( ) ;
169
+ const errorFn2 = spy ( ) ;
170
+
171
+ const unsubscribe1 = onTokenChanged ( appCheck , listener1 , errorFn1 ) ;
172
+ const unsubscribe2 = onTokenChanged ( appCheck , listener2 , errorFn2 ) ;
173
+
174
+ expect ( getState ( app ) . tokenObservers . length ) . to . equal ( 2 ) ;
175
+
176
+ await internalApi . getToken (
177
+ appCheck . app ,
178
+ getFakePlatformLoggingProvider ( )
179
+ ) ;
180
+
181
+ expect ( listener2 ) . to . be . calledWith ( {
182
+ token : fakeRecaptchaAppCheckToken . token
183
+ } ) ;
184
+ // onError should not be called on listener errors.
185
+ expect ( errorFn1 ) . to . not . be . called ;
186
+ expect ( errorFn2 ) . to . not . be . called ;
187
+ unsubscribe1 ( ) ;
188
+ unsubscribe2 ( ) ;
189
+ expect ( getState ( app ) . tokenObservers . length ) . to . equal ( 0 ) ;
190
+ } ) ;
191
+
192
+ it ( 'Listeners work when using Observer pattern' , async ( ) => {
193
+ const appCheck = initializeAppCheck ( app , {
194
+ provider : new ReCaptchaV3Provider ( FAKE_SITE_KEY ) ,
195
+ isTokenAutoRefreshEnabled : true
196
+ } ) ;
197
+ const fakePlatformLoggingProvider = getFakePlatformLoggingProvider ( ) ;
198
+ const fakeRecaptchaToken = 'fake-recaptcha-token' ;
199
+ const fakeRecaptchaAppCheckToken = {
200
+ token : 'fake-recaptcha-app-check-token' ,
201
+ expireTimeMillis : 123 ,
202
+ issuedAtTimeMillis : 0
203
+ } ;
204
+ stub ( reCAPTCHA , 'getToken' ) . returns ( Promise . resolve ( fakeRecaptchaToken ) ) ;
205
+ stub ( client , 'exchangeToken' ) . returns (
206
+ Promise . resolve ( fakeRecaptchaAppCheckToken )
207
+ ) ;
208
+ stub ( storage , 'writeTokenToStorage' ) . returns ( Promise . resolve ( undefined ) ) ;
209
+
210
+ const listener1 = ( ) : void => {
211
+ throw new Error ( ) ;
212
+ } ;
213
+ const listener2 = spy ( ) ;
214
+
215
+ const errorFn1 = spy ( ) ;
216
+ const errorFn2 = spy ( ) ;
217
+
218
+ /**
219
+ * Reverse the order of adding the failed and successful handler, for extra
220
+ * testing.
221
+ */
222
+ const unsubscribe2 = onTokenChanged ( appCheck , {
223
+ next : listener2 ,
224
+ error : errorFn2
225
+ } ) ;
226
+ const unsubscribe1 = onTokenChanged ( appCheck , {
227
+ next : listener1 ,
228
+ error : errorFn1
229
+ } ) ;
230
+
231
+ expect ( getState ( app ) . tokenObservers . length ) . to . equal ( 2 ) ;
232
+
233
+ await internalApi . getToken ( appCheck . app , fakePlatformLoggingProvider ) ;
234
+
235
+ expect ( listener2 ) . to . be . calledWith ( {
236
+ token : fakeRecaptchaAppCheckToken . token
237
+ } ) ;
238
+ // onError should not be called on listener errors.
239
+ expect ( errorFn1 ) . to . not . be . called ;
240
+ expect ( errorFn2 ) . to . not . be . called ;
241
+ unsubscribe1 ( ) ;
242
+ unsubscribe2 ( ) ;
243
+ expect ( getState ( app ) . tokenObservers . length ) . to . equal ( 0 ) ;
244
+ } ) ;
245
+
246
+ it ( 'onError() catches token errors' , async ( ) => {
247
+ stub ( logger . logger , 'error' ) ;
248
+ const appCheck = initializeAppCheck ( app , {
249
+ provider : new ReCaptchaV3Provider ( FAKE_SITE_KEY ) ,
250
+ isTokenAutoRefreshEnabled : false
251
+ } ) ;
252
+ const fakePlatformLoggingProvider = getFakePlatformLoggingProvider ( ) ;
253
+ const fakeRecaptchaToken = 'fake-recaptcha-token' ;
254
+ stub ( reCAPTCHA , 'getToken' ) . returns ( Promise . resolve ( fakeRecaptchaToken ) ) ;
255
+ stub ( client , 'exchangeToken' ) . rejects ( 'exchange error' ) ;
256
+ stub ( storage , 'writeTokenToStorage' ) . returns ( Promise . resolve ( undefined ) ) ;
257
+
258
+ const listener1 = spy ( ) ;
259
+
260
+ const errorFn1 = spy ( ) ;
261
+
262
+ const unsubscribe1 = onTokenChanged ( appCheck , listener1 , errorFn1 ) ;
263
+
264
+ await internalApi . getToken ( app , fakePlatformLoggingProvider ) ;
265
+
266
+ expect ( getState ( app ) . tokenObservers . length ) . to . equal ( 1 ) ;
267
+
268
+ expect ( errorFn1 ) . to . be . calledOnce ;
269
+ expect ( errorFn1 . args [ 0 ] [ 0 ] . name ) . to . include ( 'exchange error' ) ;
270
+
271
+ unsubscribe1 ( ) ;
272
+ expect ( getState ( app ) . tokenObservers . length ) . to . equal ( 0 ) ;
273
+ } ) ;
274
+ } ) ;
95
275
} ) ;
0 commit comments