UNPKG

voluptasmollitia

Version:
237 lines (208 loc) 7.38 kB
/** * @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. */ // eslint-disable-next-line import/no-extraneous-dependencies import { Auth, User, Persistence } from '@firebase/auth-exp'; import { Builder, Condition, WebDriver } from 'selenium-webdriver'; import { resetEmulator } from '../../../helpers/integration/emulator_rest_helpers'; import { getEmulatorUrl, PROJECT_ID, USE_EMULATOR } from '../../../helpers/integration/settings'; import { CoreFunction } from './functions'; import { JsLoadCondition } from './js_load_condition'; import { authTestServer } from './test_server'; export const START_FUNCTION = 'startAuth'; const START_LEGACY_SDK_FUNCTION = 'startLegacySDK'; const PASSED_ARGS = '...Array.prototype.slice.call(arguments, 0, -1)'; type DriverCallResult = | { type: 'success'; value: string /* JSON stringified */ } | { type: 'error'; fields: string /* JSON stringified */; message: string; stack: string; }; /** Helper wraper around the WebDriver object */ export class AuthDriver { webDriver!: WebDriver; async start(browser: string): Promise<void> { await authTestServer.start(); this.webDriver = await new Builder().forBrowser(browser).build(); await this.goToTestPage(); } async stop(): Promise<void> { authTestServer.stop(); if (process.env.WEBDRIVER_BROWSER_LOGS) { await this.webDriver .manage() .logs() .get('browser') .then( logs => { for (const { level, message } of logs) { console.log(level.name, message); } }, () => console.log( 'Failed to dump browser logs (this is normal for Firefox).' ) ); } await this.webDriver.quit(); } async call<T>(fn: string, ...args: unknown[]): Promise<T> { // When running on firefox we can't just return result immediately. For // some reason, the binding ends up causing a cycle dependency issue during // serialization which blows up the whole thing. It's okay though; this is // an integration test: we don't care about the internal (hidden) values of // these objects. const result = await this.webDriver.executeAsyncScript<DriverCallResult>( ` var callback = arguments[arguments.length - 1]; ${fn}(${PASSED_ARGS}).then(result => { callback({type: 'success', value: JSON.stringify(result)}); }).catch(e => { callback({type: 'error', message: e.message, stack: e.stack, fields: JSON.stringify(e)}); }); `, ...args ); if (result.type === 'success') { return JSON.parse(result.value) as T; } else { const e = new Error(result.message); const stack = e.stack; Object.assign(e, JSON.parse(result.fields)); const trimmedDriverStack = result.stack.split('at eval (')[0]; e.stack = `${trimmedDriverStack}\nfrom WebDriver call ${fn}(...)\n${stack}`; throw e; } } async callNoWait(fn: string, ...args: unknown[]): Promise<void> { return this.webDriver.executeScript(`${fn}(...arguments)`, ...args); } async getAuthSnapshot(): Promise<Auth> { return this.call(CoreFunction.AUTH_SNAPSHOT); } async getUserSnapshot(): Promise<User> { return this.call(CoreFunction.USER_SNAPSHOT); } async reset(): Promise<void> { await resetEmulator(); await this.goToTestPage(); return this.call(CoreFunction.RESET); } async goToTestPage(): Promise<void> { await this.webDriver.get(authTestServer.address!); } async waitForAuthInit(): Promise<void> { return this.call(CoreFunction.AWAIT_AUTH_INIT); } async waitForLegacyAuthInit(): Promise<void> { return this.call(CoreFunction.AWAIT_LEGACY_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 core module await this.webDriver.wait(new JsLoadCondition(START_FUNCTION)); 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(); await this.waitForAuthInit(); } private async injectConfig(): Promise<void> { if (!USE_EMULATOR) { throw new Error( 'Local testing against emulator requested, but ' + 'GCLOUD_PROJECT and FIREBASE_AUTH_EMULATOR_HOST env variables ' + 'are missing' ); } await this.webDriver.executeScript(` window.firebaseConfig = { apiKey: 'emulator-api-key', projectId: '${PROJECT_ID}', authDomain: 'localhost/emulator', }; window.emulatorUrl = '${getEmulatorUrl()}'; `); } async injectConfigAndInitAuth(): Promise<void> { await this.injectConfig(); await this.call(START_FUNCTION); } async injectConfigAndInitLegacySDK( persistence: Persistence['type'] = 'LOCAL' ): Promise<void> { await this.injectConfig(); await this.call(START_LEGACY_SDK_FUNCTION, persistence); } async selectPopupWindow(): Promise<void> { const currentWindowHandle = await this.webDriver.getWindowHandle(); const condition = new Condition( 'Waiting for popup to open', async driver => { return (await driver.getAllWindowHandles()).length > 1; } ); await this.webDriver.wait(condition); const handles = await this.webDriver.getAllWindowHandles(); return this.webDriver .switchTo() .window(handles.find(h => h !== currentWindowHandle)!); } async selectMainWindow(options: { noWait?: boolean } = {}): Promise<void> { if (!options.noWait) { const condition = new Condition( 'Waiting for popup to close', async driver => { return (await driver.getAllWindowHandles()).length === 1; } ); await this.webDriver.wait(condition); } const handles = await this.webDriver.getAllWindowHandles(); return this.webDriver.switchTo().window(handles[0]); } async closePopup(): Promise<void> { // This assumes the current driver is already the popup await this.webDriver.close(); return this.selectMainWindow(); } async closeExtraWindows(): Promise<void> { const handles = await this.webDriver.getAllWindowHandles(); await this.webDriver.switchTo().window(handles[handles.length - 1]); while (handles.length > 1) { await this.webDriver.close(); handles.pop(); await this.webDriver.switchTo().window(handles[handles.length - 1]); } } isCompatLayer(): boolean { return process.env.COMPAT_LAYER === 'true'; } }