Skip to content

Commit 2784b15

Browse files
committed
Flesh out the redirect tests to cover the main use cases as well as error cases
1 parent b6080a8 commit 2784b15

File tree

9 files changed

+372
-80
lines changed

9 files changed

+372
-80
lines changed

packages-exp/auth-exp/test/integration/webdriver/anonymous.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
// eslint-disable-next-line import/no-extraneous-dependencies
1919
import { OperationType, UserCredential } from '@firebase/auth-exp';
2020
import { expect } from 'chai';
21-
import { TestFunction } from './util/auth_driver';
21+
import { AnonFunction } from './util/functions';
2222
import { browserDescribe } from './util/test_runner';
2323

2424
/**
@@ -29,7 +29,7 @@ import { browserDescribe } from './util/test_runner';
2929
browserDescribe('WebDriver anonymous auth test', driver => {
3030
it('basic sign in is possible', async () => {
3131
const cred: UserCredential = await driver.call(
32-
TestFunction.SIGN_IN_ANONYMOUSLY
32+
AnonFunction.SIGN_IN_ANONYMOUSLY
3333
);
3434
expect(cred).not.to.be.null;
3535
expect(cred.user.isAnonymous).to.be.true;
@@ -39,7 +39,7 @@ browserDescribe('WebDriver anonymous auth test', driver => {
3939

4040
it('same user persists after refresh and sign in', async () => {
4141
const { user: before }: UserCredential = await driver.call(
42-
TestFunction.SIGN_IN_ANONYMOUSLY
42+
AnonFunction.SIGN_IN_ANONYMOUSLY
4343
);
4444
await driver.refresh();
4545

@@ -48,7 +48,7 @@ browserDescribe('WebDriver anonymous auth test', driver => {
4848

4949
// Then, sign in again and check
5050
const { user: after }: UserCredential = await driver.call(
51-
TestFunction.SIGN_IN_ANONYMOUSLY
51+
AnonFunction.SIGN_IN_ANONYMOUSLY
5252
);
5353
expect(after.uid).to.eq(before.uid);
5454
});

packages-exp/auth-exp/test/integration/webdriver/redirect.test.ts

Lines changed: 191 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,22 @@
1616
*/
1717

1818
// eslint-disable-next-line import/no-extraneous-dependencies
19-
import { OperationType, UserCredential } from '@firebase/auth-exp';
20-
import { expect } from 'chai';
21-
import { TestFunction } from './util/auth_driver';
19+
import { OperationType, UserCredential, User, OAuthCredential } from '@firebase/auth-exp';
20+
import { expect, use } from 'chai';
2221
import { IdPPage } from './util/idp_page';
22+
import * as chaiAsPromised from 'chai-as-promised';
2323
import { browserDescribe } from './util/test_runner';
24+
import { AnonFunction, CoreFunction, RedirectFunction } from './util/functions';
25+
26+
use(chaiAsPromised);
2427

2528
browserDescribe('WebDriver redirect IdP test', driver => {
26-
it('allows users to sign in', async () => {
29+
beforeEach(async () => {
2730
await driver.pause(200); // Race condition on auth init
28-
await driver.callNoWait(TestFunction.IDP_REDIRECT);
31+
});
32+
33+
it('allows users to sign in', async () => {
34+
await driver.callNoWait(RedirectFunction.IDP_REDIRECT);
2935
const widget = new IdPPage(driver.webDriver);
3036

3137
// We're now on the widget page; wait for load
@@ -38,16 +44,194 @@ browserDescribe('WebDriver redirect IdP test', driver => {
3844
await widget.clickSignIn();
3945

4046
await driver.reinitOnRedirect();
41-
4247
const currentUser = await driver.getUserSnapshot();
4348
expect(currentUser.email).to.eq('[email protected]');
4449
expect(currentUser.displayName).to.eq('Bob Test');
4550
expect(currentUser.photoURL).to.eq('http://bob.test/bob.png');
4651

4752
const redirectResult: UserCredential = await driver.call(
48-
TestFunction.REDIRECT_RESULT
53+
RedirectFunction.REDIRECT_RESULT
4954
);
5055
expect(redirectResult.operationType).to.eq(OperationType.SIGN_IN);
5156
expect(redirectResult.user).to.eql(currentUser);
5257
});
58+
59+
60+
it('can link with another account account', async () => {
61+
// First, sign in anonymously
62+
const {user: anonUser}: UserCredential = await driver.call(AnonFunction.SIGN_IN_ANONYMOUSLY);
63+
64+
// Then, link with redirect
65+
driver.callNoWait(RedirectFunction.IDP_LINK_REDIRECT);
66+
const widget = new IdPPage(driver.webDriver);
67+
await widget.pageLoad();
68+
await widget.clickAddAccount();
69+
await widget.fillEmail('[email protected]');
70+
await widget.clickSignIn();
71+
72+
await driver.reinitOnRedirect();
73+
// Back on page; check for the current user matching the anonymous account
74+
// as well as the new IdP account
75+
const user: User = await driver.getUserSnapshot();
76+
expect(user.uid).to.eq(anonUser.uid);
77+
expect(user.email).to.eq('[email protected]');
78+
});
79+
80+
it('can be converted to a credential', async () => {
81+
// Start with redirect
82+
driver.callNoWait(RedirectFunction.IDP_REDIRECT);
83+
const widget = new IdPPage(driver.webDriver);
84+
await widget.pageLoad();
85+
await widget.clickAddAccount();
86+
await widget.fillEmail('[email protected]');
87+
await widget.clickSignIn();
88+
89+
// Generate a credential, then store it on the window before logging out
90+
await driver.reinitOnRedirect();
91+
const first = await driver.getUserSnapshot();
92+
const cred: OAuthCredential = await driver.call(RedirectFunction.GENERATE_CREDENTIAL_FROM_RESULT);
93+
expect(cred.accessToken).to.be.a('string');
94+
expect(cred.idToken).to.be.a('string');
95+
expect(cred.signInMethod).to.eq('google.com');
96+
97+
// We've now generated that credential. Sign out and sign back in using it
98+
await driver.call(CoreFunction.SIGN_OUT);
99+
const {user: second}: UserCredential = await driver.call(RedirectFunction.SIGN_IN_WITH_REDIRECT_CREDENTIAL);
100+
expect(second.uid).to.eq(first.uid);
101+
expect(second.providerData).to.eql(first.providerData);
102+
});
103+
104+
it('handles account exists different credential errors', async () => {
105+
// Start with redirect and a verified account
106+
driver.callNoWait(RedirectFunction.IDP_REDIRECT);
107+
const widget = new IdPPage(driver.webDriver);
108+
await widget.pageLoad();
109+
await widget.clickAddAccount();
110+
await widget.fillEmail('[email protected]');
111+
await widget.clickSignIn();
112+
await driver.reinitOnRedirect();
113+
114+
const original = await driver.getUserSnapshot();
115+
expect(original.emailVerified).to.be.true;
116+
117+
// Try to sign in with an unverified Facebook account
118+
// TODO: Convert this to the widget once unverified accounts work
119+
// Come back and verify error / prepare for link
120+
await expect(driver.call(RedirectFunction.TRY_TO_SIGN_IN_UNVERIFIED, '"[email protected]"')).to.be.rejected.and.eventually.have.property('code', 'auth/account-exists-with-different-credential');
121+
122+
// Now do the link
123+
await driver.call(RedirectFunction.LINK_WITH_ERROR_CREDENTIAL);
124+
125+
// Check the user for both providers
126+
const user = await driver.getUserSnapshot();
127+
expect(user.uid).to.eq(original.uid);
128+
expect(user.providerData.map(d => d.providerId)).to.have.members(['google.com', 'facebook.com']);
129+
});
130+
131+
context('with existing user', () => {
132+
let user1: User;
133+
let user2: User;
134+
135+
beforeEach(async () => {
136+
// Create a couple existing users
137+
let cred: UserCredential = await driver.call(RedirectFunction.CREATE_FAKE_GOOGLE_USER, '"[email protected]"');
138+
user1 = cred.user;
139+
cred = await driver.call(RedirectFunction.CREATE_FAKE_GOOGLE_USER, '"[email protected]"');
140+
user2 = cred.user;
141+
await driver.call(CoreFunction.SIGN_OUT);
142+
});
143+
144+
it('a user can sign in again', async () => {
145+
// Sign in using pre-poulated user
146+
await driver.callNoWait(RedirectFunction.IDP_REDIRECT);
147+
148+
// This time, select an existing account
149+
const widget = new IdPPage(driver.webDriver);
150+
await widget.pageLoad();
151+
await widget.selectExistingAccountByEmail(user1.email!);
152+
153+
// Double check the new sign in matches the old
154+
await driver.reinitOnRedirect();
155+
const user = await driver.getUserSnapshot();
156+
expect(user.uid).to.eq(user1.uid);
157+
expect(user.email).to.eq(user1.email);
158+
});
159+
160+
it('reauthenticate works for the correct user', async() => {
161+
// Sign in using pre-poulated user
162+
await driver.callNoWait(RedirectFunction.IDP_REDIRECT);
163+
164+
const widget = new IdPPage(driver.webDriver);
165+
await widget.pageLoad();
166+
await widget.selectExistingAccountByEmail(user1.email!);
167+
168+
// Double check the new sign in matches the old
169+
await driver.reinitOnRedirect();
170+
let user = await driver.getUserSnapshot();
171+
expect(user.uid).to.eq(user1.uid);
172+
expect(user.email).to.eq(user1.email);
173+
174+
// Reauthenticate specifically
175+
await driver.callNoWait(RedirectFunction.IDP_REAUTH_REDIRECT);
176+
await widget.pageLoad();
177+
await widget.selectExistingAccountByEmail(user1.email!);
178+
179+
await driver.reinitOnRedirect();
180+
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 throws for wrong user', async () => {
186+
// Sign in using pre-poulated user
187+
await driver.callNoWait(RedirectFunction.IDP_REDIRECT);
188+
189+
const widget = new IdPPage(driver.webDriver);
190+
await widget.pageLoad();
191+
await widget.selectExistingAccountByEmail(user1.email!);
192+
193+
// Immediately reauth but with the wrong user
194+
await driver.reinitOnRedirect();
195+
await driver.callNoWait(RedirectFunction.IDP_REAUTH_REDIRECT);
196+
await widget.pageLoad();
197+
await widget.selectExistingAccountByEmail(user2.email!);
198+
199+
await driver.reinitOnRedirect();
200+
await expect(driver.call(RedirectFunction.REDIRECT_RESULT)).to.be.rejected.and.eventually.have.property('code', 'auth/user-mismatch');
201+
});
202+
203+
it('handles aborted sign ins', async () => {
204+
await driver.callNoWait(RedirectFunction.IDP_REDIRECT);
205+
const widget = new IdPPage(driver.webDriver);
206+
207+
// Don't actually sign in; go back to the previous page
208+
await widget.pageLoad();
209+
await driver.goToTestPage();
210+
await driver.reinitOnRedirect();
211+
expect(await driver.getUserSnapshot()).to.be.null;
212+
213+
// Now do sign in
214+
await driver.callNoWait(RedirectFunction.IDP_REDIRECT);
215+
// Use user1
216+
await widget.pageLoad();
217+
await widget.selectExistingAccountByEmail(user1.email!);
218+
219+
// Ensure the user was signed in...
220+
await driver.reinitOnRedirect();
221+
let user = await driver.getUserSnapshot();
222+
expect(user.uid).to.eq(user1.uid);
223+
expect(user.email).to.eq(user1.email);
224+
225+
// Now open another sign in, but return
226+
await driver.callNoWait(RedirectFunction.IDP_REAUTH_REDIRECT);
227+
await widget.pageLoad();
228+
await driver.goToTestPage();
229+
await driver.reinitOnRedirect();
230+
231+
// Make sure state remained
232+
user = await driver.getUserSnapshot();
233+
expect(user.uid).to.eq(user1.uid);
234+
expect(user.email).to.eq(user1.email);
235+
});
236+
});
53237
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import {
2+
signInAnonymously,
3+
} from '@firebase/auth-exp';
4+
5+
export async function anonymous() {
6+
const userCred = await signInAnonymously(auth);
7+
return userCred;
8+
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
export function reset() {
2+
sessionStorage.clear();
3+
localStorage.clear();
4+
const del = indexedDB.deleteDatabase('firebaseLocalStorageDb');
5+
6+
return new Promise(resolve => {
7+
del.addEventListener('success', () => resolve());
8+
del.addEventListener('error', () => resolve());
9+
del.addEventListener('blocked', () => resolve());
10+
});
11+
};
12+
13+
export function authInit() {
14+
return new Promise(resolve => {
15+
auth.onAuthStateChanged(() => resolve());
16+
});
17+
};
18+
19+
export async function userSnap() {
20+
return auth.currentUser;
21+
}
22+
23+
export async function authSnap() {
24+
return auth;
25+
}
26+
27+
export function signOut() {
28+
return auth.signOut();
29+
}

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

Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,53 +15,18 @@
1515
* limitations under the License.
1616
*/
1717

18+
import * as redirect from './redirect'
19+
import * as anonymous from './anonymous';
20+
import * as core from './core';
1821
import { initializeApp } from '@firebase/app-exp';
1922
import {
2023
getAuth,
21-
getRedirectResult,
22-
GoogleAuthProvider,
23-
signInAnonymously,
24-
signInWithRedirect,
2524
useAuthEmulator
2625
} from '@firebase/auth-exp';
2726

28-
let auth;
29-
30-
// Helper functions for tests
31-
window.anonymous = async () => {
32-
const userCred = await signInAnonymously(auth);
33-
return userCred;
34-
};
35-
36-
window.reset = () => {
37-
sessionStorage.clear();
38-
localStorage.clear();
39-
const del = indexedDB.deleteDatabase('firebaseLocalStorageDb');
40-
41-
return new Promise(resolve => {
42-
del.addEventListener('success', () => resolve());
43-
del.addEventListener('error', () => resolve());
44-
del.addEventListener('blocked', () => resolve());
45-
});
46-
};
47-
48-
window.authInit = () => {
49-
return new Promise(resolve => {
50-
auth.onAuthStateChanged(() => resolve());
51-
});
52-
};
53-
54-
window.userSnap = async () => auth.currentUser;
55-
56-
window.authSnap = async () => auth;
57-
58-
window.idpRedirect = () => {
59-
signInWithRedirect(auth, new GoogleAuthProvider());
60-
};
61-
62-
window.redirectResult = () => {
63-
return getRedirectResult(auth);
64-
};
27+
window.core = core;
28+
window.anonymous = anonymous;
29+
window.redirect = redirect;
6530

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

0 commit comments

Comments
 (0)