Skip to content

Commit

Permalink
Add some redirect webdriver tests (#4589)
Browse files Browse the repository at this point in the history
* Add some redirect webdriver tests

* Formatting

* Fix linter issue

* PR feedback
  • Loading branch information
sam-gc committed Mar 8, 2021
1 parent b6caa3f commit 82ea467
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 3 deletions.
2 changes: 1 addition & 1 deletion packages-exp/auth-exp/scripts/run-node-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var argv = yargs.options({
}).argv;
var nyc = path_1.resolve(__dirname, '../../../node_modules/.bin/nyc');
var mocha = path_1.resolve(__dirname, '../../../node_modules/.bin/mocha');
process.env.TS_NODE_COMPILER_OPTIONS = '{"module":"commonjs"}';
process.env.TS_NODE_COMPILER_OPTIONS = '{"module":"commonjs", "target": "es6"}';
var testConfig = [
'src/!(platform_browser|platform_react_native|platform_cordova)/**/*.test.ts',
'--file',
Expand Down
2 changes: 1 addition & 1 deletion packages-exp/auth-exp/scripts/run-node-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const argv = yargs.options({
const nyc = resolve(__dirname, '../../../node_modules/.bin/nyc');
const mocha = resolve(__dirname, '../../../node_modules/.bin/mocha');

process.env.TS_NODE_COMPILER_OPTIONS = '{"module":"commonjs"}';
process.env.TS_NODE_COMPILER_OPTIONS = '{"module":"commonjs", "target": "es6"}';

let testConfig = [
'src/!(platform_browser|platform_react_native|platform_cordova)/**/*.test.ts',
Expand Down
53 changes: 53 additions & 0 deletions packages-exp/auth-exp/test/integration/webdriver/redirect.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* @license
* Copyright 2021 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.
*/

// 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 { IdPPage } from './util/idp_page';
import { browserDescribe } from './util/test_runner';

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

// We're now on the widget page; wait for load
await widget.pageLoad();
await widget.clickAddAccount();
await widget.fillEmail('bob@bob.test');
await widget.fillDisplayName('Bob Test');
await widget.fillScreenName('bob.test');
await widget.fillProfilePhoto('http://bob.test/bob.png');
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
);
expect(redirectResult.operationType).to.eq(OperationType.SIGN_IN);
expect(redirectResult.user).to.eql(currentUser);
});
});
11 changes: 11 additions & 0 deletions packages-exp/auth-exp/test/integration/webdriver/static/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
import { initializeApp } from '@firebase/app-exp';
import {
getAuth,
getRedirectResult,
GoogleAuthProvider,
signInAnonymously,
signInWithRedirect,
useAuthEmulator
} from '@firebase/auth-exp';

Expand Down Expand Up @@ -52,6 +55,14 @@ window.userSnap = async () => auth.currentUser;

window.authSnap = async () => auth;

window.idpRedirect = () => {
signInWithRedirect(auth, new GoogleAuthProvider());
};

window.redirectResult = () => {
return getRedirectResult(auth);
};

// The config and emulator URL are injected by the test. The test framework
// calls this function after that injection.
window.startAuth = async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
PROJECT_ID,
USE_EMULATOR
} from '../../../helpers/integration/settings';
import { JsLoadCondition } from './js_load_condition';
import { authTestServer } from './test_server';

/** Available functions within the browser. See static/index.js */
Expand All @@ -33,7 +34,9 @@ export enum TestFunction {
AWAIT_AUTH_INIT = 'authInit',
USER_SNAPSHOT = 'userSnap',
AUTH_SNAPSHOT = 'authSnap',
START_AUTH = 'startAuth'
START_AUTH = 'startAuth',
IDP_REDIRECT = 'idpRedirect',
REDIRECT_RESULT = 'redirectResult'
}

/** Helper wraper around the WebDriver object */
Expand Down Expand Up @@ -67,6 +70,10 @@ export class AuthDriver {
return JSON.parse(result as string) as T;
}

async callNoWait(fn: TestFunction): Promise<void> {
return this.webDriver.executeScript(`${fn}()`);
}

async getAuthSnapshot(): Promise<Auth> {
return this.call(TestFunction.AUTH_SNAPSHOT);
}
Expand All @@ -85,6 +92,21 @@ export class AuthDriver {
return this.call(TestFunction.AWAIT_AUTH_INIT);
}

async reinitOnRedirect(): Promise<void> {
// In this unique case we don't know when the page is back; check for the
// presence of the init function
// await this.webDriver.wait(this.webDriver.executeScript('typeof authInit !== "undefined"'));
await this.webDriver.wait(new JsLoadCondition(TestFunction.START_AUTH));
await this.injectConfigAndInitAuth();
await this.waitForAuthInit();
}

pause(ms: number): Promise<void> {
return new Promise(resolve => {
setTimeout(() => resolve(), ms);
});
}

async refresh(): Promise<void> {
await this.webDriver.navigate().refresh();
await this.injectConfigAndInitAuth();
Expand Down
72 changes: 72 additions & 0 deletions packages-exp/auth-exp/test/integration/webdriver/util/idp_page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* @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 { By, until, WebDriver } from 'selenium-webdriver';
import { JsLoadCondition } from './js_load_condition';

const ADD_ACCOUNT_BUTTON = By.css('.js-new-account');
const SIGN_IN_BUTTON = By.id('sign-in');
const EMAIL_INPUT = By.id('email-input');
const DISPLAY_NAME_INPUT = By.id('display-name-input');
const SCREEN_NAME_INPUT = By.id('screen-name-input');
const PROFILE_PHOTO_INPUT = By.id('profile-photo-input');

export class IdPPage {
static PAGE_TITLE = 'Auth Emulator IDP Login Widget';

constructor(private readonly driver: WebDriver) {}

async pageLoad(): Promise<void> {
await this.driver.wait(until.titleContains('Auth Emulator'));
await this.driver.wait(new JsLoadCondition('toggleForm'));
}

async clickAddAccount(): Promise<void> {
await this.driver.wait(until.elementLocated(ADD_ACCOUNT_BUTTON));
await this.driver.findElement(ADD_ACCOUNT_BUTTON).click();
}

async clickSignIn(): Promise<void> {
await this.driver.wait(until.elementLocated(SIGN_IN_BUTTON));
const button = await this.driver.findElement(SIGN_IN_BUTTON);
await this.driver.wait(until.elementIsEnabled(button));
await button.click();
}

fillEmail(email: string): Promise<void> {
return this.fillInput(EMAIL_INPUT, email);
}

fillDisplayName(displayName: string): Promise<void> {
return this.fillInput(DISPLAY_NAME_INPUT, displayName);
}

fillScreenName(screenName: string): Promise<void> {
return this.fillInput(SCREEN_NAME_INPUT, screenName);
}

fillProfilePhoto(prophilePhoto: string): Promise<void> {
return this.fillInput(PROFILE_PHOTO_INPUT, prophilePhoto);
}

private async fillInput(input: By, text: string): Promise<void> {
await this.driver.wait(until.elementLocated(input));
const el = await this.driver.findElement(input);
await el.click();
await el.sendKeys(text);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* @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 { Condition } from 'selenium-webdriver';

/**
* A condition that looks for the presence of a specified function. This is
* used with WebDriver .wait() as a proxy for determining when the JS has
* finished loading in a page.
*/
export class JsLoadCondition extends Condition<boolean> {
constructor(globalValue: string) {
super(`Waiting for global value ${globalValue}`, driver => {
return driver.executeScript(
`return typeof ${globalValue} !== 'undefined';`
);
});
}
}

0 comments on commit 82ea467

Please sign in to comment.