Skip to content

Commit fb0f566

Browse files
committed
Duplicate redirect tests for popup
1 parent d48a44c commit fb0f566

File tree

7 files changed

+416
-10
lines changed

7 files changed

+416
-10
lines changed

config/mocharc.node.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
const config = {
2626
require: 'ts-node/register',
2727
timeout: 5000,
28-
retries: 5,
28+
retries: 0,
2929
exit: true
3030
};
3131

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

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

Lines changed: 5 additions & 3 deletions
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.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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 {
19+
FacebookAuthProvider,
20+
GoogleAuthProvider,
21+
linkWithCredential,
22+
linkWithPopup,
23+
OAuthProvider,
24+
reauthenticateWithPopup,
25+
signInWithCredential,
26+
signInWithPopup
27+
} from '@firebase/auth-exp';
28+
29+
// These functions are a little funky: WebDriver relies on callbacks to
30+
// pass data back to the main Node process. Because of that setup, we can't
31+
// return the popup tasks as pending promises as they won't resolve until
32+
// the WebDriver is allowed to do other stuff. Instead, we'll store the
33+
// promises on the window and provide a way to retrieve them later, unblocking
34+
// the WebDriver process.
35+
36+
export function idpPopup(optProvider) {
37+
const provider = optProvider
38+
? new OAuthProvider(optProvider)
39+
: new GoogleAuthProvider();
40+
window.popup.popupPromise = signInWithPopup(auth, provider);
41+
}
42+
43+
export function idpReauthPopup() {
44+
window.popup.popupPromise =
45+
reauthenticateWithPopup(auth.currentUser, new GoogleAuthProvider());
46+
}
47+
48+
export function idpLinkPopup() {
49+
window.popup.popupPromise = linkWithPopup(auth.currentUser, new GoogleAuthProvider());
50+
}
51+
52+
export function popupResult() {
53+
return window.popup.popupPromise;
54+
}
55+
56+
export async function generateCredentialFromResult() {
57+
const result = await window.popup.popupPromise;
58+
window.popup.popupCred = GoogleAuthProvider.credentialFromResult(result);
59+
return window.popup.popupCred;
60+
}
61+
62+
export async function signInWithPopupCredential() {
63+
return signInWithCredential(auth, window.popup.popupCred);
64+
}
65+
66+
export async function linkWithErrorCredential() {
67+
await linkWithCredential(auth.currentUser, window.popup.errorCred);
68+
}
69+
70+
// These below are not technically popup functions but they're helpers for
71+
// the popup tests.
72+
73+
export function createFakeGoogleUser(email) {
74+
return signInWithCredential(
75+
auth,
76+
GoogleAuthProvider.credential(
77+
`{"sub": "__${email}__", "email": "${email}", "email_verified": true}`
78+
)
79+
);
80+
}
81+
82+
export async function tryToSignInUnverified(email) {
83+
try {
84+
await signInWithCredential(
85+
auth,
86+
FacebookAuthProvider.credential(
87+
`{"sub": "$$${email}$$", "email": "${email}", "email_verified": false}`
88+
)
89+
);
90+
} catch (e) {
91+
window.popup.errorCred = FacebookAuthProvider.credentialFromError(e);
92+
throw e;
93+
}
94+
}

0 commit comments

Comments
 (0)