diff --git a/src/cdk/testing/webdriver/webdriver-element.ts b/src/cdk/testing/webdriver/webdriver-element.ts index 998f45e9725a..ca8087bde8b9 100644 --- a/src/cdk/testing/webdriver/webdriver-element.ts +++ b/src/cdk/testing/webdriver/webdriver-element.ts @@ -15,66 +15,15 @@ import { TestKey, TextOptions } from '@angular/cdk/testing'; -import {Button, By, Key, WebElement} from 'selenium-webdriver'; - -/** - * Maps the `TestKey` constants to WebDriver's `Key` constants. - * See https://github.com/SeleniumHQ/selenium/blob/trunk/javascript/webdriver/key.js#L29 - */ -const keyMap = { - [TestKey.BACKSPACE]: Key.BACK_SPACE, - [TestKey.TAB]: Key.TAB, - [TestKey.ENTER]: Key.ENTER, - [TestKey.SHIFT]: Key.SHIFT, - [TestKey.CONTROL]: Key.CONTROL, - [TestKey.ALT]: Key.ALT, - [TestKey.ESCAPE]: Key.ESCAPE, - [TestKey.PAGE_UP]: Key.PAGE_UP, - [TestKey.PAGE_DOWN]: Key.PAGE_DOWN, - [TestKey.END]: Key.END, - [TestKey.HOME]: Key.HOME, - [TestKey.LEFT_ARROW]: Key.ARROW_LEFT, - [TestKey.UP_ARROW]: Key.ARROW_UP, - [TestKey.RIGHT_ARROW]: Key.ARROW_RIGHT, - [TestKey.DOWN_ARROW]: Key.ARROW_DOWN, - [TestKey.INSERT]: Key.INSERT, - [TestKey.DELETE]: Key.DELETE, - [TestKey.F1]: Key.F1, - [TestKey.F2]: Key.F2, - [TestKey.F3]: Key.F3, - [TestKey.F4]: Key.F4, - [TestKey.F5]: Key.F5, - [TestKey.F6]: Key.F6, - [TestKey.F7]: Key.F7, - [TestKey.F8]: Key.F8, - [TestKey.F9]: Key.F9, - [TestKey.F10]: Key.F10, - [TestKey.F11]: Key.F11, - [TestKey.F12]: Key.F12, - [TestKey.META]: Key.META -}; - -/** Converts a `ModifierKeys` object to a list of Protractor `Key`s. */ -function toWebDriverModifierKeys(modifiers: ModifierKeys): string[] { - const result: string[] = []; - if (modifiers.control) { - result.push(Key.CONTROL); - } - if (modifiers.alt) { - result.push(Key.ALT); - } - if (modifiers.shift) { - result.push(Key.SHIFT); - } - if (modifiers.meta) { - result.push(Key.META); - } - return result; -} +import { + getWebDriverModifierKeys, + webDriverKeyMap +} from '@angular/cdk/testing/webdriver/webdriver-keys'; +import * as webdriver from 'selenium-webdriver'; /** A `TestElement` implementation for WebDriver. */ -export class WebdriverElement implements TestElement { - constructor(private readonly _webElement: () => WebElement) {} +export class WebDriverElement implements TestElement { + constructor(private readonly _webElement: () => webdriver.WebElement) {} async blur(): Promise { return this._executeScript(((element: HTMLElement) => element.blur()), this._webElement()); @@ -86,12 +35,12 @@ export class WebdriverElement implements TestElement { async click(...args: [ModifierKeys?] | ['center', ModifierKeys?] | [number, number, ModifierKeys?]): Promise { - await this._dispatchClickEventSequence(args, Button.LEFT); + await this._dispatchClickEventSequence(args, webdriver.Button.LEFT); } async rightClick(...args: [ModifierKeys?] | ['center', ModifierKeys?] | [number, number, ModifierKeys?]): Promise { - await this._dispatchClickEventSequence(args, Button.RIGHT); + await this._dispatchClickEventSequence(args, webdriver.Button.RIGHT); } async focus(): Promise { @@ -124,12 +73,12 @@ export class WebdriverElement implements TestElement { rest = modifiersAndKeys; } - const modifierKeys = toWebDriverModifierKeys(modifiers); - const keys = rest.map(k => typeof k === 'string' ? k.split('') : [keyMap[k]]) + const modifierKeys = getWebDriverModifierKeys(modifiers); + const keys = rest.map(k => typeof k === 'string' ? k.split('') : [webDriverKeyMap[k]]) .reduce((arr, k) => arr.concat(k), []) - // Key.chord doesn't work well with geckodriver (mozilla/geckodriver#1502), + // webdriver.Key.chord doesn't work well with geckodriver (mozilla/geckodriver#1502), // so avoid it if no modifier keys are required. - .map(k => modifierKeys.length > 0 ? Key.chord(...modifierKeys, k) : k); + .map(k => modifierKeys.length > 0 ? webdriver.Key.chord(...modifierKeys, k) : k); return this._webElement().sendKeys(...keys); } @@ -171,7 +120,7 @@ export class WebdriverElement implements TestElement { } async selectOptions(...optionIndexes: number[]): Promise { - const options = await this._webElement().findElements(By.css('option')); + const options = await this._webElement().findElements(webdriver.By.css('option')); const indexes = new Set(optionIndexes); // Convert to a set to remove duplicates. if (options.length && indexes.size) { @@ -183,9 +132,9 @@ export class WebdriverElement implements TestElement { if (indexes.has(i)) { // We have to hold the control key while clicking on options so that multiple can be // selected in multi-selection mode. The key doesn't do anything for single selection. - await this._actions().keyDown(Key.CONTROL).perform(); + await this._actions().keyDown(webdriver.Key.CONTROL).perform(); await options[i].click(); - await this._actions().keyUp(Key.CONTROL).perform(); + await this._actions().keyUp(webdriver.Key.CONTROL).perform(); } } } @@ -199,12 +148,12 @@ export class WebdriverElement implements TestElement { } async isFocused(): Promise { - return WebElement.equals( + return webdriver.WebElement.equals( this._webElement(), this._webElement().getDriver().switchTo().activeElement()); } async dispatchEvent(name: string, data?: Record): Promise { - return this._executeScript(_dispatchEvent, name, this._webElement(), data); + return this._executeScript(dispatchEvent, name, this._webElement(), data); } private _actions() { @@ -223,7 +172,7 @@ export class WebdriverElement implements TestElement { if (args.length && typeof args[args.length - 1] === 'object') { modifiers = args.pop() as ModifierKeys; } - const modifierKeys = toWebDriverModifierKeys(modifiers); + const modifierKeys = getWebDriverModifierKeys(modifiers); // Omitting the offset argument to mouseMove results in clicking the center. // This is the default behavior we want, so we use an empty array of offsetArgs if @@ -246,18 +195,13 @@ export class WebdriverElement implements TestElement { } /** - * Dispatches an event with a particular name and data to an element. - * Note that this needs to be a pure function, because it gets stringified by - * Protractor and is executed inside the browser. + * Dispatches an event with a particular name and data to an element. Note that this needs to be a + * pure function, because it gets stringified by WebDriver and is executed inside the browser. */ -function _dispatchEvent(name: string, element: Element, data?: Record) { +function dispatchEvent(name: string, element: Element, data?: Record) { const event = document.createEvent('Event'); event.initEvent(name); - - if (data) { - // tslint:disable-next-line:ban Have to use `Object.assign` to preserve the original object. - Object.assign(event, data); - } - + // tslint:disable-next-line:ban Have to use `Object.assign` to preserve the original object. + Object.assign(event, data || {}); element.dispatchEvent(event); } diff --git a/src/cdk/testing/webdriver/webdriver-harness-environment.ts b/src/cdk/testing/webdriver/webdriver-harness-environment.ts index f10ffb54923e..66666b3cb1aa 100644 --- a/src/cdk/testing/webdriver/webdriver-harness-environment.ts +++ b/src/cdk/testing/webdriver/webdriver-harness-environment.ts @@ -7,18 +7,18 @@ */ import {HarnessEnvironment, HarnessLoader, TestElement} from '@angular/cdk/testing'; -import {By, WebDriver, WebElement} from 'selenium-webdriver'; -import {WebdriverElement} from './webdriver-element'; +import * as webdriver from 'selenium-webdriver'; +import {WebDriverElement} from './webdriver-element'; -/** A `HarnessEnvironment` implementation for Protractor. */ -export class WebdriverHarnessEnvironment extends HarnessEnvironment<() => WebElement> { - protected constructor(rawRootElement: () => WebElement) { +/** A `HarnessEnvironment` implementation for WebDriver. */ +export class WebDriverHarnessEnvironment extends HarnessEnvironment<() => webdriver.WebElement> { + protected constructor(rawRootElement: () => webdriver.WebElement) { super(rawRootElement); } /** Creates a `HarnessLoader` rooted at the document root. */ - static loader(driver: WebDriver): HarnessLoader { - return new WebdriverHarnessEnvironment(() => driver.findElement(By.css('body'))); + static loader(driver: webdriver.WebDriver): HarnessLoader { + return new WebDriverHarnessEnvironment(() => driver.findElement(webdriver.By.css('body'))); } async forceStabilize(): Promise { @@ -31,22 +31,23 @@ export class WebdriverHarnessEnvironment extends HarnessEnvironment<() => WebEle // https://github.com/angular/components/issues/17412 } - protected getDocumentRoot(): () => WebElement { - return () => this.rawRootElement().getDriver().findElement(By.css('body')); + protected getDocumentRoot(): () => webdriver.WebElement { + return () => this.rawRootElement().getDriver().findElement(webdriver.By.css('body')); } - protected createTestElement(element: () => WebElement): TestElement { - return new WebdriverElement(element); + protected createTestElement(element: () => webdriver.WebElement): TestElement { + return new WebDriverElement(element); } - protected createEnvironment(element: () => WebElement): HarnessEnvironment<() => WebElement> { - return new WebdriverHarnessEnvironment(element); + protected createEnvironment(element: () => webdriver.WebElement): + HarnessEnvironment<() => webdriver.WebElement> { + return new WebDriverHarnessEnvironment(element); } // TODO(mmalerba): I'm not so sure about this... - // it feels like maybe the return type should be `() => Promise` instead. - protected async getAllRawElements(selector: string): Promise<(() => WebElement)[]> { - const els = await this.rawRootElement().findElements(By.css(selector)); - return els.map((x: WebElement) => () => x); + // it feels like maybe the return type should be `() => Promise` instead. + protected async getAllRawElements(selector: string): Promise<(() => webdriver.WebElement)[]> { + const els = await this.rawRootElement().findElements(webdriver.By.css(selector)); + return els.map((x: webdriver.WebElement) => () => x); } } diff --git a/src/cdk/testing/webdriver/webdriver-keys.ts b/src/cdk/testing/webdriver/webdriver-keys.ts new file mode 100644 index 000000000000..b8a148deed4a --- /dev/null +++ b/src/cdk/testing/webdriver/webdriver-keys.ts @@ -0,0 +1,65 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {ModifierKeys, TestKey} from '@angular/cdk/testing'; +import * as webdriver from 'selenium-webdriver'; + +/** + * Maps the `TestKey` constants to WebDriver's `webdriver.Key` constants. + * See https://github.com/SeleniumHQ/selenium/blob/trunk/javascript/webdriver/key.js#L29 + */ +export const webDriverKeyMap = { + [TestKey.BACKSPACE]: webdriver.Key.BACK_SPACE, + [TestKey.TAB]: webdriver.Key.TAB, + [TestKey.ENTER]: webdriver.Key.ENTER, + [TestKey.SHIFT]: webdriver.Key.SHIFT, + [TestKey.CONTROL]: webdriver.Key.CONTROL, + [TestKey.ALT]: webdriver.Key.ALT, + [TestKey.ESCAPE]: webdriver.Key.ESCAPE, + [TestKey.PAGE_UP]: webdriver.Key.PAGE_UP, + [TestKey.PAGE_DOWN]: webdriver.Key.PAGE_DOWN, + [TestKey.END]: webdriver.Key.END, + [TestKey.HOME]: webdriver.Key.HOME, + [TestKey.LEFT_ARROW]: webdriver.Key.ARROW_LEFT, + [TestKey.UP_ARROW]: webdriver.Key.ARROW_UP, + [TestKey.RIGHT_ARROW]: webdriver.Key.ARROW_RIGHT, + [TestKey.DOWN_ARROW]: webdriver.Key.ARROW_DOWN, + [TestKey.INSERT]: webdriver.Key.INSERT, + [TestKey.DELETE]: webdriver.Key.DELETE, + [TestKey.F1]: webdriver.Key.F1, + [TestKey.F2]: webdriver.Key.F2, + [TestKey.F3]: webdriver.Key.F3, + [TestKey.F4]: webdriver.Key.F4, + [TestKey.F5]: webdriver.Key.F5, + [TestKey.F6]: webdriver.Key.F6, + [TestKey.F7]: webdriver.Key.F7, + [TestKey.F8]: webdriver.Key.F8, + [TestKey.F9]: webdriver.Key.F9, + [TestKey.F10]: webdriver.Key.F10, + [TestKey.F11]: webdriver.Key.F11, + [TestKey.F12]: webdriver.Key.F12, + [TestKey.META]: webdriver.Key.META +}; + +/** Gets a list of WebDriver `Key`s for the given `ModifierKeys`. */ +export function getWebDriverModifierKeys(modifiers: ModifierKeys): string[] { + const result: string[] = []; + if (modifiers.control) { + result.push(webdriver.Key.CONTROL); + } + if (modifiers.alt) { + result.push(webdriver.Key.ALT); + } + if (modifiers.shift) { + result.push(webdriver.Key.SHIFT); + } + if (modifiers.meta) { + result.push(webdriver.Key.META); + } + return result; +}