@@ -25,13 +25,21 @@ import { SingletonInstantiator } from '../../core/util/instantiator';
25
25
import {
26
26
AuthEvent ,
27
27
AuthEventType ,
28
+ EventManager ,
28
29
PopupRedirectResolver
29
30
} from '../../model/popup_redirect' ;
30
- import { cordovaPopupRedirectResolver } from './popup_redirect' ;
31
+ import {
32
+ CordovaAuthEventManager ,
33
+ cordovaPopupRedirectResolver
34
+ } from './popup_redirect' ;
31
35
import { GoogleAuthProvider } from '../../core/providers/google' ;
32
36
import * as utils from './utils' ;
33
37
import * as events from './events' ;
34
38
import { FirebaseError } from '@firebase/util' ;
39
+ import {
40
+ stubSingleTimeout ,
41
+ TimerTripFn
42
+ } from '../../../test/helpers/timeout_stub' ;
35
43
36
44
use ( chaiAsPromised ) ;
37
45
use ( sinonChai ) ;
@@ -85,6 +93,194 @@ describe('platform_cordova/popup_redirect/popup_redirect', () => {
85
93
} ) ;
86
94
} ) ;
87
95
96
+ describe ( '_initialize' , ( ) => {
97
+ const NO_EVENT_TIMER_ID = 10001 ;
98
+ const PACKAGE_NAME = 'my.package' ;
99
+ const NOT_PACKAGE_NAME = 'not.my.package' ;
100
+ let universalLinksCb :
101
+ | ( ( eventData : Record < string , string > | null ) => unknown )
102
+ | null ;
103
+ let tripNoEventTimer : TimerTripFn ;
104
+
105
+ beforeEach ( ( ) => {
106
+ tripNoEventTimer = stubSingleTimeout ( NO_EVENT_TIMER_ID ) ;
107
+ window . universalLinks = {
108
+ subscribe ( _unused , cb ) {
109
+ universalLinksCb = cb ;
110
+ }
111
+ } ;
112
+ window . BuildInfo = {
113
+ packageName : PACKAGE_NAME ,
114
+ displayName : ''
115
+ } ;
116
+ sinon . stub ( window , 'clearTimeout' ) ;
117
+ } ) ;
118
+
119
+ afterEach ( ( ) => {
120
+ universalLinksCb = null ;
121
+ const win = ( window as unknown ) as Record < string , unknown > ;
122
+ delete win . universalLinks ;
123
+ delete win . BuildInfo ;
124
+ } ) ;
125
+
126
+ function event ( manager : EventManager ) : Promise < AuthEvent > {
127
+ return new Promise ( resolve => {
128
+ ( manager as CordovaAuthEventManager ) . addPassiveListener ( resolve ) ;
129
+ } ) ;
130
+ }
131
+
132
+ context ( 'when no event is present' , ( ) => {
133
+ it ( 'clears local storage and dispatches no-event event' , async ( ) => {
134
+ const promise = event ( await resolver . _initialize ( auth ) ) ;
135
+ tripNoEventTimer ( ) ;
136
+ const { error, ...rest } = await promise ;
137
+
138
+ expect ( error )
139
+ . to . be . instanceOf ( FirebaseError )
140
+ . with . property ( 'code' , 'auth/no-auth-event' ) ;
141
+ expect ( rest ) . to . eql ( {
142
+ type : AuthEventType . UNKNOWN ,
143
+ eventId : null ,
144
+ sessionId : null ,
145
+ urlResponse : null ,
146
+ postBody : null ,
147
+ tenantId : null
148
+ } ) ;
149
+ expect ( events . _getAndRemoveEvent ) . to . have . been . called ;
150
+ } ) ;
151
+ } ) ;
152
+
153
+ context ( 'when an event is present' , ( ) => {
154
+ it ( 'clears the no event timeout' , async ( ) => {
155
+ await resolver . _initialize ( auth ) ;
156
+ await universalLinksCb ! ( { } ) ;
157
+ expect ( window . clearTimeout ) . to . have . been . calledWith ( NO_EVENT_TIMER_ID ) ;
158
+ } ) ;
159
+
160
+ it ( 'signals no event if no url in event data' , async ( ) => {
161
+ const promise = event ( await resolver . _initialize ( auth ) ) ;
162
+ await universalLinksCb ! ( { } ) ;
163
+ const { error, ...rest } = await promise ;
164
+
165
+ expect ( error )
166
+ . to . be . instanceOf ( FirebaseError )
167
+ . with . property ( 'code' , 'auth/no-auth-event' ) ;
168
+ expect ( rest ) . to . eql ( {
169
+ type : AuthEventType . UNKNOWN ,
170
+ eventId : null ,
171
+ sessionId : null ,
172
+ urlResponse : null ,
173
+ postBody : null ,
174
+ tenantId : null
175
+ } ) ;
176
+ } ) ;
177
+
178
+ it ( 'signals no event if partial parse turns up null' , async ( ) => {
179
+ const promise = event ( await resolver . _initialize ( auth ) ) ;
180
+ eventsStubs . _eventFromPartialAndUrl . returns ( null ) ;
181
+ eventsStubs . _getAndRemoveEvent . returns (
182
+ Promise . resolve ( {
183
+ type : AuthEventType . REAUTH_VIA_REDIRECT
184
+ } as AuthEvent )
185
+ ) ;
186
+ await universalLinksCb ! ( { url : 'foo-bar' } ) ;
187
+ const { error, ...rest } = await promise ;
188
+
189
+ expect ( error )
190
+ . to . be . instanceOf ( FirebaseError )
191
+ . with . property ( 'code' , 'auth/no-auth-event' ) ;
192
+ expect ( rest ) . to . eql ( {
193
+ type : AuthEventType . UNKNOWN ,
194
+ eventId : null ,
195
+ sessionId : null ,
196
+ urlResponse : null ,
197
+ postBody : null ,
198
+ tenantId : null
199
+ } ) ;
200
+ } ) ;
201
+
202
+ it ( 'signals the final event if partial expansion success' , async ( ) => {
203
+ const finalEvent = {
204
+ type : AuthEventType . REAUTH_VIA_REDIRECT ,
205
+ postBody : 'foo'
206
+ } ;
207
+ eventsStubs . _getAndRemoveEvent . returns (
208
+ Promise . resolve ( {
209
+ type : AuthEventType . REAUTH_VIA_REDIRECT
210
+ } as AuthEvent )
211
+ ) ;
212
+
213
+ const promise = event ( await resolver . _initialize ( auth ) ) ;
214
+ eventsStubs . _eventFromPartialAndUrl . returns ( finalEvent as AuthEvent ) ;
215
+ await universalLinksCb ! ( { url : 'foo-bar' } ) ;
216
+ expect ( await promise ) . to . eq ( finalEvent ) ;
217
+ expect ( events . _eventFromPartialAndUrl ) . to . have . been . calledWith (
218
+ { type : AuthEventType . REAUTH_VIA_REDIRECT } ,
219
+ 'foo-bar'
220
+ ) ;
221
+ } ) ;
222
+ } ) ;
223
+
224
+ context ( 'when using global handleOpenUrl callback' , ( ) => {
225
+ it ( 'ignores inbound callbacks that are not for this app' , async ( ) => {
226
+ await resolver . _initialize ( auth ) ;
227
+ handleOpenUrl ( `${ NOT_PACKAGE_NAME } ://foo` ) ;
228
+
229
+ // Clear timeout is called in the handler so we can check that
230
+ expect ( window . clearTimeout ) . not . to . have . been . called ;
231
+ } ) ;
232
+
233
+ it ( 'passes through callback if package name matches' , async ( ) => {
234
+ await resolver . _initialize ( auth ) ;
235
+ handleOpenUrl ( `${ PACKAGE_NAME } ://foo` ) ;
236
+ expect ( window . clearTimeout ) . to . have . been . calledWith ( NO_EVENT_TIMER_ID ) ;
237
+ } ) ;
238
+
239
+ it ( 'signals the final event if partial expansion success' , async ( ) => {
240
+ const finalEvent = {
241
+ type : AuthEventType . REAUTH_VIA_REDIRECT ,
242
+ postBody : 'foo'
243
+ } ;
244
+ eventsStubs . _getAndRemoveEvent . returns (
245
+ Promise . resolve ( {
246
+ type : AuthEventType . REAUTH_VIA_REDIRECT
247
+ } as AuthEvent )
248
+ ) ;
249
+
250
+ const promise = event ( await resolver . _initialize ( auth ) ) ;
251
+ eventsStubs . _eventFromPartialAndUrl . returns ( finalEvent as AuthEvent ) ;
252
+ handleOpenUrl ( `${ PACKAGE_NAME } ://foo` ) ;
253
+ expect ( await promise ) . to . eq ( finalEvent ) ;
254
+ expect ( events . _eventFromPartialAndUrl ) . to . have . been . calledWith (
255
+ { type : AuthEventType . REAUTH_VIA_REDIRECT } ,
256
+ `${ PACKAGE_NAME } ://foo`
257
+ ) ;
258
+ } ) ;
259
+
260
+ it ( 'calls the dev existing handleOpenUrl function' , async ( ) => {
261
+ const oldHandleOpenUrl = sinon . stub ( ) ;
262
+ window . handleOpenUrl = oldHandleOpenUrl ;
263
+
264
+ await resolver . _initialize ( auth ) ;
265
+ handleOpenUrl ( `${ PACKAGE_NAME } ://foo` ) ;
266
+ expect ( oldHandleOpenUrl ) . to . have . been . calledWith (
267
+ `${ PACKAGE_NAME } ://foo`
268
+ ) ;
269
+ } ) ;
270
+
271
+ it ( 'calls the dev existing handleOpenUrl function for other package' , async ( ) => {
272
+ const oldHandleOpenUrl = sinon . stub ( ) ;
273
+ window . handleOpenUrl = oldHandleOpenUrl ;
274
+
275
+ await resolver . _initialize ( auth ) ;
276
+ handleOpenUrl ( `${ NOT_PACKAGE_NAME } ://foo` ) ;
277
+ expect ( oldHandleOpenUrl ) . to . have . been . calledWith (
278
+ `${ NOT_PACKAGE_NAME } ://foo`
279
+ ) ;
280
+ } ) ;
281
+ } ) ;
282
+ } ) ;
283
+
88
284
describe ( '_openPopup' , ( ) => {
89
285
it ( 'throws an error' , ( ) => {
90
286
expect ( ( ) =>
0 commit comments