Skip to content

Commit

Permalink
Add more redirect tests so that almost all the main use cases are cov…
Browse files Browse the repository at this point in the history
…ered (#4597)

* Flesh out the redirect tests to cover the main use cases as well as error cases

* Formatting, licenses

* Linter

* PR feedback

* Formatting

* PR feedback
  • Loading branch information
sam-gc committed Mar 9, 2021
1 parent b6080a8 commit 56030a0
Show file tree
Hide file tree
Showing 9 changed files with 485 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { OperationType, UserCredential } from '@firebase/auth-exp';
import { expect } from 'chai';
import { TestFunction } from './util/auth_driver';
import { AnonFunction } from './util/functions';
import { browserDescribe } from './util/test_runner';

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

it('same user persists after refresh and sign in', async () => {
const { user: before }: UserCredential = await driver.call(
TestFunction.SIGN_IN_ANONYMOUSLY
AnonFunction.SIGN_IN_ANONYMOUSLY
);
await driver.refresh();

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

// Then, sign in again and check
const { user: after }: UserCredential = await driver.call(
TestFunction.SIGN_IN_ANONYMOUSLY
AnonFunction.SIGN_IN_ANONYMOUSLY
);
expect(after.uid).to.eq(before.uid);
});
Expand Down
229 changes: 221 additions & 8 deletions packages-exp/auth-exp/test/integration/webdriver/redirect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,28 @@
* limitations under the License.
*/

// eslint-disable-next-line import/no-extraneous-dependencies
import { OperationType, UserCredential } from '@firebase/auth-exp';
import { expect } from 'chai';
import { TestFunction } from './util/auth_driver';
import {
OperationType,
UserCredential,
User,
OAuthCredential
// eslint-disable-next-line import/no-extraneous-dependencies
} from '@firebase/auth-exp';
import { expect, use } from 'chai';
import { IdPPage } from './util/idp_page';
import * as chaiAsPromised from 'chai-as-promised';
import { browserDescribe } from './util/test_runner';
import { AnonFunction, CoreFunction, RedirectFunction } from './util/functions';

use(chaiAsPromised);

browserDescribe('WebDriver redirect IdP test', driver => {
it('allows users to sign in', async () => {
beforeEach(async () => {
await driver.pause(200); // Race condition on auth init
await driver.callNoWait(TestFunction.IDP_REDIRECT);
});

it('allows users to sign in', async () => {
await driver.callNoWait(RedirectFunction.IDP_REDIRECT);
const widget = new IdPPage(driver.webDriver);

// We're now on the widget page; wait for load
Expand All @@ -38,16 +49,218 @@ browserDescribe('WebDriver redirect IdP test', driver => {
await widget.clickSignIn();

await driver.reinitOnRedirect();

const currentUser = await driver.getUserSnapshot();
expect(currentUser.email).to.eq('bob@bob.test');
expect(currentUser.displayName).to.eq('Bob Test');
expect(currentUser.photoURL).to.eq('http://bob.test/bob.png');

const redirectResult: UserCredential = await driver.call(
TestFunction.REDIRECT_RESULT
RedirectFunction.REDIRECT_RESULT
);
expect(redirectResult.operationType).to.eq(OperationType.SIGN_IN);
expect(redirectResult.user).to.eql(currentUser);
});

it('can link with another account account', async () => {
// First, sign in anonymously
const { user: anonUser }: UserCredential = await driver.call(
AnonFunction.SIGN_IN_ANONYMOUSLY
);

// Then, link with redirect
await driver.callNoWait(RedirectFunction.IDP_LINK_REDIRECT);
const widget = new IdPPage(driver.webDriver);
await widget.pageLoad();
await widget.clickAddAccount();
await widget.fillEmail('bob@bob.test');
await widget.clickSignIn();

await driver.reinitOnRedirect();
// Back on page; check for the current user matching the anonymous account
// as well as the new IdP account
const user: User = await driver.getUserSnapshot();
expect(user.uid).to.eq(anonUser.uid);
expect(user.email).to.eq('bob@bob.test');
});

it('can be converted to a credential', async () => {
// Start with redirect
await driver.callNoWait(RedirectFunction.IDP_REDIRECT);
const widget = new IdPPage(driver.webDriver);
await widget.pageLoad();
await widget.clickAddAccount();
await widget.fillEmail('bob@bob.test');
await widget.clickSignIn();

// Generate a credential, then store it on the window before logging out
await driver.reinitOnRedirect();
const first = await driver.getUserSnapshot();
const cred: OAuthCredential = await driver.call(
RedirectFunction.GENERATE_CREDENTIAL_FROM_RESULT
);
expect(cred.accessToken).to.be.a('string');
expect(cred.idToken).to.be.a('string');
expect(cred.signInMethod).to.eq('google.com');

// We've now generated that credential. Sign out and sign back in using it
await driver.call(CoreFunction.SIGN_OUT);
const { user: second }: UserCredential = await driver.call(
RedirectFunction.SIGN_IN_WITH_REDIRECT_CREDENTIAL
);
expect(second.uid).to.eq(first.uid);
expect(second.providerData).to.eql(first.providerData);
});

it('handles account exists different credential errors', async () => {
// Start with redirect and a verified account
await driver.callNoWait(RedirectFunction.IDP_REDIRECT);
const widget = new IdPPage(driver.webDriver);
await widget.pageLoad();
await widget.clickAddAccount();
await widget.fillEmail('bob@bob.test');
await widget.clickSignIn();
await driver.reinitOnRedirect();

const original = await driver.getUserSnapshot();
expect(original.emailVerified).to.be.true;

// Try to sign in with an unverified Facebook account
// TODO: Convert this to the widget once unverified accounts work
// Come back and verify error / prepare for link
await expect(
driver.call(RedirectFunction.TRY_TO_SIGN_IN_UNVERIFIED, 'bob@bob.test')
).to.be.rejected.and.eventually.have.property(
'code',
'auth/account-exists-with-different-credential'
);

// Now do the link
await driver.call(RedirectFunction.LINK_WITH_ERROR_CREDENTIAL);

// Check the user for both providers
const user = await driver.getUserSnapshot();
expect(user.uid).to.eq(original.uid);
expect(user.providerData.map(d => d.providerId)).to.have.members([
'google.com',
'facebook.com'
]);
});

context('with existing user', () => {
let user1: User;
let user2: User;

beforeEach(async () => {
// Create a couple existing users
let cred: UserCredential = await driver.call(
RedirectFunction.CREATE_FAKE_GOOGLE_USER,
'bob@bob.test'
);
user1 = cred.user;
cred = await driver.call(
RedirectFunction.CREATE_FAKE_GOOGLE_USER,
'sally@sally.test'
);
user2 = cred.user;
await driver.call(CoreFunction.SIGN_OUT);
});

it('a user can sign in again', async () => {
// Sign in using pre-poulated user
await driver.callNoWait(RedirectFunction.IDP_REDIRECT);

// This time, select an existing account
const widget = new IdPPage(driver.webDriver);
await widget.pageLoad();
await widget.selectExistingAccountByEmail(user1.email!);

// Double check the new sign in matches the old
await driver.reinitOnRedirect();
const user = await driver.getUserSnapshot();
expect(user.uid).to.eq(user1.uid);
expect(user.email).to.eq(user1.email);
});

it('reauthenticate works for the correct user', async () => {
// Sign in using pre-poulated user
await driver.callNoWait(RedirectFunction.IDP_REDIRECT);

const widget = new IdPPage(driver.webDriver);
await widget.pageLoad();
await widget.selectExistingAccountByEmail(user1.email!);

// Double check the new sign in matches the old
await driver.reinitOnRedirect();
let user = await driver.getUserSnapshot();
expect(user.uid).to.eq(user1.uid);
expect(user.email).to.eq(user1.email);

// Reauthenticate specifically
await driver.callNoWait(RedirectFunction.IDP_REAUTH_REDIRECT);
await widget.pageLoad();
await widget.selectExistingAccountByEmail(user1.email!);

await driver.reinitOnRedirect();
user = await driver.getUserSnapshot();
expect(user.uid).to.eq(user1.uid);
expect(user.email).to.eq(user1.email);
});

it('reauthenticate throws for wrong user', async () => {
// Sign in using pre-poulated user
await driver.callNoWait(RedirectFunction.IDP_REDIRECT);

const widget = new IdPPage(driver.webDriver);
await widget.pageLoad();
await widget.selectExistingAccountByEmail(user1.email!);

// Immediately reauth but with the wrong user
await driver.reinitOnRedirect();
await driver.callNoWait(RedirectFunction.IDP_REAUTH_REDIRECT);
await widget.pageLoad();
await widget.selectExistingAccountByEmail(user2.email!);

await driver.reinitOnRedirect();
await expect(
driver.call(RedirectFunction.REDIRECT_RESULT)
).to.be.rejected.and.eventually.have.property(
'code',
'auth/user-mismatch'
);
});

it('handles aborted sign ins', async () => {
await driver.callNoWait(RedirectFunction.IDP_REDIRECT);
const widget = new IdPPage(driver.webDriver);

// Don't actually sign in; go back to the previous page
await widget.pageLoad();
await driver.goToTestPage();
await driver.reinitOnRedirect();
expect(await driver.getUserSnapshot()).to.be.null;

// Now do sign in
await driver.callNoWait(RedirectFunction.IDP_REDIRECT);
// Use user1
await widget.pageLoad();
await widget.selectExistingAccountByEmail(user1.email!);

// Ensure the user was signed in...
await driver.reinitOnRedirect();
let user = await driver.getUserSnapshot();
expect(user.uid).to.eq(user1.uid);
expect(user.email).to.eq(user1.email);

// Now open another sign in, but return
await driver.callNoWait(RedirectFunction.IDP_REAUTH_REDIRECT);
await widget.pageLoad();
await driver.goToTestPage();
await driver.reinitOnRedirect();

// Make sure state remained
user = await driver.getUserSnapshot();
expect(user.uid).to.eq(user1.uid);
expect(user.email).to.eq(user1.email);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { signInAnonymously } from '@firebase/auth-exp';

export async function anonymousSignIn() {
const userCred = await signInAnonymously(auth);
return userCred;
}
46 changes: 46 additions & 0 deletions packages-exp/auth-exp/test/integration/webdriver/static/core.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export function reset() {
sessionStorage.clear();
localStorage.clear();
const del = indexedDB.deleteDatabase('firebaseLocalStorageDb');

return new Promise(resolve => {
del.addEventListener('success', () => resolve());
del.addEventListener('error', () => resolve());
del.addEventListener('blocked', () => resolve());
});
}

export function authInit() {
return new Promise(resolve => {
auth.onAuthStateChanged(() => resolve());
});
}

export async function userSnap() {
return auth.currentUser;
}

export async function authSnap() {
return auth;
}

export function signOut() {
return auth.signOut();
}
Loading

0 comments on commit 56030a0

Please sign in to comment.