1
+ /**
2
+ * @license
3
+ * Copyright 2020 Google LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ import { expect , use } from 'chai' ;
19
+ import * as chaiAsPromised from 'chai-as-promised' ;
20
+ import * as sinon from 'sinon' ;
21
+ import * as sinonChai from 'sinon-chai' ;
22
+
23
+ import { OperationType } from '@firebase/auth-types-exp' ;
24
+ import { FirebaseError } from '@firebase/util' ;
25
+
26
+ import { delay } from '../../../test/delay' ;
27
+ import { authEvent , BASE_AUTH_EVENT } from '../../../test/iframe_event' ;
28
+ import { testAuth , testUser } from '../../../test/mock_auth' ;
29
+ import { makeMockPopupRedirectResolver } from '../../../test/mock_popup_redirect_resolver' ;
30
+ import { Auth } from '../../model/auth' ;
31
+ import {
32
+ AuthEvent , AuthEventType , EventManager , PopupRedirectResolver
33
+ } from '../../model/popup_redirect' ;
34
+ import { AuthEventManager } from '../auth/auth_event_manager' ;
35
+ import { AUTH_ERROR_FACTORY , AuthErrorCode } from '../errors' ;
36
+ import { UserCredentialImpl } from '../user/user_credential_impl' ;
37
+ import { _getInstance } from '../util/instantiator' ;
38
+ import { AbstractPopupRedirectAction } from './abstract_popup_redirect_action' ;
39
+ import * as idp from './idp' ;
40
+
41
+ use ( sinonChai ) ;
42
+ use ( chaiAsPromised ) ;
43
+
44
+ const ERROR = AUTH_ERROR_FACTORY . create ( AuthErrorCode . INTERNAL_ERROR , {
45
+ appName : 'test'
46
+ } ) ;
47
+
48
+ /**
49
+ * A real class is needed to instantiate the action
50
+ */
51
+ class WrapperAction extends AbstractPopupRedirectAction {
52
+ eventId = '100' ;
53
+ onExecution = sinon . stub ( ) . returns ( Promise . resolve ( ) ) ;
54
+ cleanUp = sinon . stub ( ) ;
55
+ }
56
+
57
+ describe ( 'src/core/strategies/abstract_popup_redirect_action' , ( ) => {
58
+ let auth : Auth ;
59
+ let resolver : PopupRedirectResolver ;
60
+ let eventManager : EventManager ;
61
+ let idpStubs : sinon . SinonStubbedInstance < typeof idp > ;
62
+
63
+ beforeEach ( async ( ) => {
64
+ auth = await testAuth ( ) ;
65
+ eventManager = new AuthEventManager ( auth . name ) ;
66
+ resolver = _getInstance ( makeMockPopupRedirectResolver ( eventManager ) ) ;
67
+ idpStubs = sinon . stub ( idp ) ;
68
+ } ) ;
69
+
70
+ afterEach ( ( ) => {
71
+ sinon . restore ( ) ;
72
+ } ) ;
73
+
74
+ context ( '#execute' , ( ) => {
75
+ let action : WrapperAction ;
76
+
77
+ beforeEach ( ( ) => {
78
+ action = new WrapperAction ( auth , AuthEventType . LINK_VIA_POPUP , resolver ) ;
79
+ idpStubs . _signIn . returns ( Promise . resolve ( new UserCredentialImpl ( testUser ( auth , 'uid' ) , null , OperationType . SIGN_IN ) ) ) ;
80
+ } ) ;
81
+
82
+ /** Finishes out the promise */
83
+ function finishPromise ( outcome : AuthEvent | FirebaseError ) : void {
84
+ delay ( ( ) : void => {
85
+ if ( outcome instanceof FirebaseError ) {
86
+ action . onError ( outcome ) ;
87
+ } else {
88
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
89
+ action . onAuthEvent ( outcome ) ;
90
+ }
91
+ } ) ;
92
+ }
93
+
94
+ it ( 'initializes the resolver' , async ( ) => {
95
+ sinon . spy ( resolver , '_initialize' ) ;
96
+ const promise = action . execute ( ) ;
97
+ finishPromise ( authEvent ( ) ) ;
98
+ await promise ;
99
+ expect ( resolver . _initialize ) . to . have . been . calledWith ( auth ) ;
100
+ } ) ;
101
+
102
+ it ( 'calls subclass onExecution' , async ( ) => {
103
+ finishPromise ( authEvent ( ) ) ;
104
+ await action . execute ( ) ;
105
+ expect ( action . onExecution ) . to . have . been . called ;
106
+ } ) ;
107
+
108
+ it ( 'registers and unregisters itself with the event manager' , async ( ) => {
109
+ sinon . spy ( eventManager , 'registerConsumer' ) ;
110
+ sinon . spy ( eventManager , 'unregisterConsumer' ) ;
111
+ finishPromise ( authEvent ( ) ) ;
112
+ await action . execute ( ) ;
113
+ expect ( eventManager . registerConsumer ) . to . have . been . calledWith ( action ) ;
114
+ expect ( eventManager . unregisterConsumer ) . to . have . been . calledWith ( action ) ;
115
+ } ) ;
116
+
117
+ it ( 'unregisters itself in case of error' , async ( ) => {
118
+ sinon . spy ( eventManager , 'unregisterConsumer' ) ;
119
+ finishPromise ( ERROR ) ;
120
+ try { await action . execute ( ) ; } catch { }
121
+ expect ( eventManager . unregisterConsumer ) . to . have . been . calledWith ( action ) ;
122
+ } ) ;
123
+
124
+ it ( 'emits the user credential returned from idp task' , async ( ) => {
125
+ finishPromise ( authEvent ( ) ) ;
126
+ const cred = await action . execute ( ) ;
127
+ expect ( cred . user . uid ) . to . eq ( 'uid' ) ;
128
+ expect ( cred . credential ) . to . be . null ;
129
+ expect ( cred . operationType ) . to . eq ( OperationType . SIGN_IN ) ;
130
+ } ) ;
131
+
132
+ it ( 'bubbles up any error' , async ( done ) => {
133
+ finishPromise ( ERROR ) ;
134
+ try {
135
+ await action . execute ( ) ;
136
+ } catch ( e ) {
137
+ expect ( e ) . to . eq ( ERROR ) ;
138
+ done ( ) ;
139
+ }
140
+ } ) ;
141
+
142
+ context ( 'idp tasks' , ( ) => {
143
+ function updateFilter ( type : AuthEventType ) {
144
+ ( action as unknown as Record < string , unknown > ) . filter = type ;
145
+ }
146
+
147
+ const expectedIdpTaskParams : idp . IdpTaskParams = {
148
+ auth,
149
+ requestUri : BASE_AUTH_EVENT . urlResponse ! ,
150
+ sessionId : BASE_AUTH_EVENT . sessionId ! ,
151
+ tenantId : BASE_AUTH_EVENT . tenantId || undefined ,
152
+ postBody : BASE_AUTH_EVENT . postBody || undefined ,
153
+ } ;
154
+
155
+ it ( 'routes signInWithPopup' , async ( ) => {
156
+ const type = AuthEventType . SIGN_IN_VIA_POPUP ;
157
+ updateFilter ( type ) ;
158
+ finishPromise ( authEvent ( { type} ) ) ;
159
+ await action . execute ( ) ;
160
+ expect ( idp . _signIn ) . to . have . been . calledWith ( expectedIdpTaskParams ) ;
161
+ } ) ;
162
+ } ) ;
163
+ } ) ;
164
+ } ) ;
0 commit comments