Skip to content

Commit 0ac48bb

Browse files
authored
[Auth] Add popup WebDriver tests (duplicated from the redirect tests) (#4602)
* Duplicate redirect tests for popup * Formatting * Revert change to mocha settings * Fix tests after rebase
1 parent 1de07ba commit 0ac48bb

File tree

6 files changed

+442
-9
lines changed

6 files changed

+442
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
/**
2+
* @license
3+
* Copyright 2021 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 {
19+
OperationType,
20+
UserCredential,
21+
User,
22+
OAuthCredential
23+
// eslint-disable-next-line import/no-extraneous-dependencies
24+
} from '@firebase/auth-exp';
25+
import { expect, use } from 'chai';
26+
import { IdPPage } from './util/idp_page';
27+
import * as chaiAsPromised from 'chai-as-promised';
28+
import { browserDescribe } from './util/test_runner';
29+
import { AnonFunction, CoreFunction, PopupFunction } from './util/functions';
30+
31+
use(chaiAsPromised);
32+
33+
browserDescribe('Popup IdP tests', driver => {
34+
it('allows users to sign in', async () => {
35+
await driver.callNoWait(PopupFunction.IDP_POPUP);
36+
await driver.selectPopupWindow();
37+
const widget = new IdPPage(driver.webDriver);
38+
39+
// We're now on the widget page; wait for load
40+
await widget.pageLoad();
41+
await widget.clickAddAccount();
42+
await widget.fillEmail('[email protected]');
43+
await widget.fillDisplayName('Bob Test');
44+
await widget.fillScreenName('bob.test');
45+
await widget.fillProfilePhoto('http://bob.test/bob.png');
46+
await widget.clickSignIn();
47+
48+
await driver.selectMainWindow();
49+
const result: UserCredential = await driver.call(
50+
PopupFunction.POPUP_RESULT
51+
);
52+
const currentUser = await driver.getUserSnapshot();
53+
expect(currentUser.email).to.eq('[email protected]');
54+
expect(currentUser.displayName).to.eq('Bob Test');
55+
expect(currentUser.photoURL).to.eq('http://bob.test/bob.png');
56+
57+
expect(result.operationType).to.eq(OperationType.SIGN_IN);
58+
expect(result.user).to.eql(currentUser);
59+
});
60+
61+
it('can link with another account account', async () => {
62+
// First, sign in anonymously
63+
const { user: anonUser }: UserCredential = await driver.call(
64+
AnonFunction.SIGN_IN_ANONYMOUSLY
65+
);
66+
67+
// Then, link with popup
68+
await driver.callNoWait(PopupFunction.IDP_LINK_POPUP);
69+
await driver.selectPopupWindow();
70+
const widget = new IdPPage(driver.webDriver);
71+
await widget.pageLoad();
72+
await widget.clickAddAccount();
73+
await widget.fillEmail('[email protected]');
74+
await widget.clickSignIn();
75+
76+
await driver.selectMainWindow();
77+
// Back on main page; check for the current user matching the anonymous
78+
// account as well as the new IdP account
79+
const user: User = await driver.getUserSnapshot();
80+
expect(user.uid).to.eq(anonUser.uid);
81+
expect(user.email).to.eq('[email protected]');
82+
});
83+
84+
it('can be converted to a credential', async () => {
85+
// Start with popup
86+
await driver.callNoWait(PopupFunction.IDP_POPUP);
87+
await driver.selectPopupWindow();
88+
const widget = new IdPPage(driver.webDriver);
89+
await widget.pageLoad();
90+
await widget.clickAddAccount();
91+
await widget.fillEmail('[email protected]');
92+
await widget.clickSignIn();
93+
94+
// Generate a credential, then store it on the window before logging out
95+
await driver.selectMainWindow();
96+
const first = await driver.getUserSnapshot();
97+
const cred: OAuthCredential = await driver.call(
98+
PopupFunction.GENERATE_CREDENTIAL_FROM_RESULT
99+
);
100+
expect(cred.accessToken).to.be.a('string');
101+
expect(cred.idToken).to.be.a('string');
102+
expect(cred.signInMethod).to.eq('google.com');
103+
104+
// We've now generated that credential. Sign out and sign back in using it
105+
await driver.call(CoreFunction.SIGN_OUT);
106+
const { user: second }: UserCredential = await driver.call(
107+
PopupFunction.SIGN_IN_WITH_POPUP_CREDENTIAL
108+
);
109+
expect(second.uid).to.eq(first.uid);
110+
expect(second.providerData).to.eql(first.providerData);
111+
});
112+
113+
it('handles account exists different credential errors', async () => {
114+
// Start with popup and a verified account
115+
await driver.callNoWait(PopupFunction.IDP_POPUP);
116+
await driver.selectPopupWindow();
117+
const widget = new IdPPage(driver.webDriver);
118+
await widget.pageLoad();
119+
await widget.clickAddAccount();
120+
await widget.fillEmail('[email protected]');
121+
await widget.clickSignIn();
122+
123+
await driver.selectMainWindow();
124+
const original = await driver.getUserSnapshot();
125+
expect(original.emailVerified).to.be.true;
126+
127+
// Try to sign in with an unverified Facebook account
128+
// TODO: Convert this to the widget once unverified accounts work
129+
// Come back and verify error / prepare for link
130+
await expect(
131+
driver.call(PopupFunction.TRY_TO_SIGN_IN_UNVERIFIED, '[email protected]')
132+
).to.be.rejected.and.eventually.have.property(
133+
'code',
134+
'auth/account-exists-with-different-credential'
135+
);
136+
137+
// Now do the link
138+
await driver.call(PopupFunction.LINK_WITH_ERROR_CREDENTIAL);
139+
140+
// Check the user for both providers
141+
const user = await driver.getUserSnapshot();
142+
expect(user.uid).to.eq(original.uid);
143+
expect(user.providerData.map(d => d.providerId)).to.have.members([
144+
'google.com',
145+
'facebook.com'
146+
]);
147+
});
148+
149+
context('with existing user', () => {
150+
let user1: User;
151+
let user2: User;
152+
153+
beforeEach(async () => {
154+
// Create a couple existing users
155+
let cred: UserCredential = await driver.call(
156+
PopupFunction.CREATE_FAKE_GOOGLE_USER,
157+
158+
);
159+
user1 = cred.user;
160+
cred = await driver.call(
161+
PopupFunction.CREATE_FAKE_GOOGLE_USER,
162+
163+
);
164+
user2 = cred.user;
165+
await driver.call(CoreFunction.SIGN_OUT);
166+
});
167+
168+
it('a user can sign in again', async () => {
169+
// Sign in using pre-poulated user
170+
await driver.callNoWait(PopupFunction.IDP_POPUP);
171+
await driver.selectPopupWindow();
172+
173+
// This time, select an existing account
174+
const widget = new IdPPage(driver.webDriver);
175+
await widget.pageLoad();
176+
await widget.selectExistingAccountByEmail(user1.email!);
177+
178+
// Double check the new sign in matches the old
179+
await driver.selectMainWindow();
180+
const user = await driver.getUserSnapshot();
181+
expect(user.uid).to.eq(user1.uid);
182+
expect(user.email).to.eq(user1.email);
183+
});
184+
185+
it('reauthenticate works for the correct user', async () => {
186+
// Sign in using pre-poulated user
187+
await driver.callNoWait(PopupFunction.IDP_POPUP);
188+
await driver.selectPopupWindow();
189+
190+
const widget = new IdPPage(driver.webDriver);
191+
await widget.pageLoad();
192+
await widget.selectExistingAccountByEmail(user1.email!);
193+
194+
// Double check the new sign in matches the old
195+
await driver.selectMainWindow();
196+
let user = await driver.getUserSnapshot();
197+
expect(user.uid).to.eq(user1.uid);
198+
expect(user.email).to.eq(user1.email);
199+
200+
// Reauthenticate specifically
201+
await driver.callNoWait(PopupFunction.IDP_REAUTH_POPUP);
202+
await driver.selectPopupWindow();
203+
await widget.pageLoad();
204+
await widget.selectExistingAccountByEmail(user1.email!);
205+
206+
await driver.selectMainWindow();
207+
user = await driver.getUserSnapshot();
208+
expect(user.uid).to.eq(user1.uid);
209+
expect(user.email).to.eq(user1.email);
210+
});
211+
212+
it('reauthenticate throws for wrong user', async () => {
213+
// Sign in using pre-poulated user
214+
await driver.callNoWait(PopupFunction.IDP_POPUP);
215+
await driver.selectPopupWindow();
216+
217+
const widget = new IdPPage(driver.webDriver);
218+
await widget.pageLoad();
219+
await widget.selectExistingAccountByEmail(user1.email!);
220+
221+
// Immediately reauth but with the wrong user
222+
await driver.selectMainWindow();
223+
await driver.callNoWait(PopupFunction.IDP_REAUTH_POPUP);
224+
await driver.selectPopupWindow();
225+
await widget.pageLoad();
226+
await widget.selectExistingAccountByEmail(user2.email!);
227+
228+
await driver.selectMainWindow();
229+
await expect(
230+
driver.call(PopupFunction.POPUP_RESULT)
231+
).to.be.rejected.and.eventually.have.property(
232+
'code',
233+
'auth/user-mismatch'
234+
);
235+
});
236+
237+
it('handles aborted sign ins', async () => {
238+
await driver.callNoWait(PopupFunction.IDP_POPUP);
239+
await driver.selectPopupWindow();
240+
const widget = new IdPPage(driver.webDriver);
241+
242+
// Don't actually sign in; go back to the previous page
243+
await widget.pageLoad();
244+
await driver.closePopup();
245+
await expect(
246+
driver.call(PopupFunction.POPUP_RESULT)
247+
).to.be.rejected.and.eventually.have.property(
248+
'code',
249+
'auth/popup-closed-by-user'
250+
);
251+
expect(await driver.getUserSnapshot()).to.be.null;
252+
253+
// Now do sign in
254+
await driver.callNoWait(PopupFunction.IDP_POPUP);
255+
await driver.selectPopupWindow();
256+
// Use user1
257+
await widget.pageLoad();
258+
await widget.selectExistingAccountByEmail(user1.email!);
259+
260+
// Ensure the user was signed in...
261+
await driver.selectMainWindow();
262+
let user = await driver.getUserSnapshot();
263+
expect(user.uid).to.eq(user1.uid);
264+
expect(user.email).to.eq(user1.email);
265+
266+
// Now open another sign in, but return
267+
await driver.callNoWait(PopupFunction.IDP_REAUTH_POPUP);
268+
await driver.selectPopupWindow();
269+
await widget.pageLoad();
270+
await driver.closePopup();
271+
await expect(
272+
driver.call(PopupFunction.POPUP_RESULT)
273+
).to.be.rejected.and.eventually.have.property(
274+
'code',
275+
'auth/popup-closed-by-user'
276+
);
277+
278+
// Make sure state remained
279+
user = await driver.getUserSnapshot();
280+
expect(user.uid).to.eq(user1.uid);
281+
expect(user.email).to.eq(user1.email);
282+
}).timeout(12_000); // Test takes a while due to the closed-by-user errors
283+
});
284+
});

packages-exp/auth-exp/test/integration/webdriver/static/index.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818
import * as redirect from './redirect';
1919
import * as anonymous from './anonymous';
2020
import * as core from './core';
21+
import * as popup from './popup';
2122
import { initializeApp } from '@firebase/app-exp';
2223
import { getAuth, useAuthEmulator } from '@firebase/auth-exp';
2324

24-
window.core = core;
25-
window.anonymous = anonymous;
26-
window.redirect = redirect;
25+
window.core = { ...core };
26+
window.anonymous = { ...anonymous };
27+
window.redirect = { ...redirect };
28+
window.popup = { ...popup };
2729

2830
// The config and emulator URL are injected by the test. The test framework
2931
// calls this function after that injection.

0 commit comments

Comments
 (0)