From aaa55cf74493bcda9222907d98749a38eea920b7 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Thu, 1 Dec 2016 20:54:22 +0100 Subject: [PATCH 1/7] test: add class with e2e test utilities * Adds a class that simplifies some common and hard to remember Protractor expressions. * Refactors the existing tests to use the new utilities. Fixes #2025. --- e2e/components/button/button.e2e.ts | 11 +- e2e/components/dialog/dialog.e2e.ts | 47 +++----- e2e/components/grid-list/grid-list.e2e.ts | 10 +- e2e/components/list/list.e2e.ts | 10 +- e2e/components/menu/menu-page.ts | 68 +++-------- e2e/components/menu/menu.e2e.ts | 106 ++++++++---------- .../progress-bar/progress-bar.e2e.ts | 17 ++- e2e/components/radio/radio.e2e.ts | 13 +-- e2e/components/tabs/tabs.e2e.ts | 23 ++-- e2e/utils.e2e.ts | 77 +++++++++++++ 10 files changed, 202 insertions(+), 180 deletions(-) create mode 100644 e2e/utils.e2e.ts diff --git a/e2e/components/button/button.e2e.ts b/e2e/components/button/button.e2e.ts index 8bbab6c135b9..caf33cbf8ed2 100644 --- a/e2e/components/button/button.e2e.ts +++ b/e2e/components/button/button.e2e.ts @@ -1,11 +1,10 @@ import {browser, by, element} from 'protractor'; -describe('button', function () { - describe('disabling behavior', function () { - beforeEach(function() { - browser.get('/button'); - }); - it('should prevent click handlers from executing when disabled', function () { +describe('button', () => { + describe('disabling behavior', () => { + beforeEach(() => browser.get('/button')); + + it('should prevent click handlers from executing when disabled', () => { element(by.id('test-button')).click(); expect(element(by.id('click-counter')).getText()).toEqual('1'); diff --git a/e2e/components/dialog/dialog.e2e.ts b/e2e/components/dialog/dialog.e2e.ts index 74f1b397c1aa..29873cff269b 100644 --- a/e2e/components/dialog/dialog.e2e.ts +++ b/e2e/components/dialog/dialog.e2e.ts @@ -1,11 +1,14 @@ -import {browser, by, element, Key, ProtractorBy} from 'protractor'; +import {browser, by, element, Key} from 'protractor'; +import {E2EUtils} from '../../utils.e2e'; describe('dialog', () => { + const utils = new E2EUtils(); + beforeEach(() => browser.get('/dialog')); it('should open a dialog', () => { element(by.id('default')).click(); - waitForDialog().then((isPresent: boolean) => expect(isPresent).toBe(true)); + utils.expectToExist('md-dialog-container'); }); it('should close by clicking on the backdrop', () => { @@ -13,7 +16,7 @@ describe('dialog', () => { waitForDialog().then(() => { clickOnBackrop(); - waitForDialog().then((isPresent: boolean) => expect(isPresent).toBe(false)); + utils.expectToExist('md-dialog-container', false); }); }); @@ -21,8 +24,8 @@ describe('dialog', () => { element(by.id('default')).click(); waitForDialog().then(() => { - pressEscape(); - waitForDialog().then((isPresent: boolean) => expect(isPresent).toBe(false)); + utils.pressKeys(Key.ESCAPE); + utils.expectToExist('md-dialog-container', false); }); }); @@ -31,7 +34,7 @@ describe('dialog', () => { waitForDialog().then(() => { element(by.id('close')).click(); - waitForDialog().then((isPresent: boolean) => expect(isPresent).toBe(false)); + utils.expectToExist('md-dialog-container', false); }); }); @@ -39,7 +42,7 @@ describe('dialog', () => { element(by.id('default')).click(); waitForDialog().then(() => { - expectFocusOn(element(by.css('md-dialog-container input'))); + utils.expectFocusOn('md-dialog-container input'); }); }); @@ -50,7 +53,7 @@ describe('dialog', () => { waitForDialog().then(() => { clickOnBackrop(); - expectFocusOn(openButton); + utils.expectFocusOn(openButton); }); }); @@ -60,8 +63,8 @@ describe('dialog', () => { waitForDialog().then(() => { let tab = Key.TAB; - browser.actions().sendKeys(tab, tab, tab).perform(); - expectFocusOn(element(by.id('close'))); + utils.pressKeys(tab, tab, tab); + utils.expectFocusOn('#close'); }); }); @@ -70,7 +73,7 @@ describe('dialog', () => { waitForDialog().then(() => { clickOnBackrop(); - waitForDialog().then((isPresent: boolean) => expect(isPresent).toBe(true)); + utils.expectToExist('md-dialog-container'); }); }); @@ -78,30 +81,16 @@ describe('dialog', () => { element(by.id('disabled')).click(); waitForDialog().then(() => { - pressEscape(); - waitForDialog().then((isPresent: boolean) => expect(isPresent).toBe(true)); + utils.pressKeys(Key.ESCAPE); + utils.expectToExist('md-dialog-container'); }); }); function waitForDialog() { - return browser.isElementPresent(by.css('md-dialog-container') as ProtractorBy); + return utils.waitForElement('md-dialog-container'); } function clickOnBackrop() { - browser.actions() - // We need to move the cursor to the top left so - // the dialog doesn't receive the click accidentally. - .mouseMove(element(by.css('.cdk-overlay-backdrop')).getWebElement(), { x: 0, y: 0 }) - .click() - .perform(); - } - - function pressEscape() { - browser.actions().sendKeys(Key.ESCAPE).perform(); - } - - // TODO(crisbeto): should be moved to a common util. copied from the menu e2e setup. - function expectFocusOn(el: any): void { - expect(browser.driver.switchTo().activeElement().getInnerHtml()).toBe(el.getInnerHtml()); + utils.clickElementAtPoint('.md-overlay-backdrop', { x: 0, y: 0 }); } }); diff --git a/e2e/components/grid-list/grid-list.e2e.ts b/e2e/components/grid-list/grid-list.e2e.ts index 64c4647fde5a..d9eee8c4dc2a 100644 --- a/e2e/components/grid-list/grid-list.e2e.ts +++ b/e2e/components/grid-list/grid-list.e2e.ts @@ -1,14 +1,16 @@ -import {browser, by, element} from 'protractor'; +import {browser} from 'protractor'; +import {E2EUtils} from '../../utils.e2e'; describe('grid-list', () => { + const utils = new E2EUtils(); + beforeEach(() => browser.get('/grid-list')); it('should render a grid list container', () => { - expect(element(by.css('md-grid-list')).isPresent()).toBe(true); + utils.expectToExist('md-grid-list'); }); it('should render list items inside the grid list container', () => { - let container = element(by.css('md-grid-list')); - expect(container.isElementPresent(by.css('md-grid-tile'))).toBe(true); + utils.expectToExist('md-grid-list md-grid-tile'); }); }); diff --git a/e2e/components/list/list.e2e.ts b/e2e/components/list/list.e2e.ts index 329f8a8dd19c..203290dfac67 100644 --- a/e2e/components/list/list.e2e.ts +++ b/e2e/components/list/list.e2e.ts @@ -1,14 +1,16 @@ -import {browser, by, element} from 'protractor'; +import {browser} from 'protractor'; +import {E2EUtils} from '../../utils.e2e'; describe('list', () => { + const utils = new E2EUtils(); + beforeEach(() => browser.get('/list')); it('should render a list container', () => { - expect(element(by.css('md-list')).isPresent()).toBe(true); + utils.expectToExist('md-list'); }); it('should render list items inside the list container', () => { - let container = element(by.css('md-list')); - expect(container.isElementPresent(by.css('md-list-item'))).toBe(true); + utils.expectToExist('md-list md-list-item'); }); }); diff --git a/e2e/components/menu/menu-page.ts b/e2e/components/menu/menu-page.ts index 7100f0d494f1..ffda502eba4f 100644 --- a/e2e/components/menu/menu-page.ts +++ b/e2e/components/menu/menu-page.ts @@ -1,69 +1,33 @@ -import {browser, by, element, ElementFinder, ProtractorBy} from 'protractor'; +import {browser, by, element, ElementFinder} from 'protractor'; export class MenuPage { + constructor() { browser.get('/menu'); } - constructor() { - browser.get('/menu'); - } + menu(): ElementFinder { return element(by.css('.md-menu-panel')); } - menu() { return element(by.css('.md-menu-panel')); } + start(): ElementFinder { return element(by.id('start')); } - start() { return element(by.id('start')); } + trigger(): ElementFinder { return element(by.id('trigger')); } - trigger() { return element(by.id('trigger')); } + triggerTwo(): ElementFinder { return element(by.id('trigger-two')); } - triggerTwo() { return element(by.id('trigger-two')); } + backdrop(): ElementFinder { return element(by.css('.cdk-overlay-backdrop')); } - backdrop() { return element(by.css('.cdk-overlay-backdrop')); } + items(index: number): ElementFinder { return element.all(by.css('[md-menu-item]')).get(index); } - items(index: number) { return element.all(by.css('[md-menu-item]')).get(index); } + textArea(): ElementFinder { return element(by.id('text')); } - textArea() { return element(by.id('text')); } + beforeTrigger(): ElementFinder { return element(by.id('before-t')); } - beforeTrigger() { return element(by.id('before-t')); } + aboveTrigger(): ElementFinder { return element(by.id('above-t')); } - aboveTrigger() { return element(by.id('above-t')); } + combinedTrigger(): ElementFinder { return element(by.id('combined-t')); } - combinedTrigger() { return element(by.id('combined-t')); } + beforeMenu(): ElementFinder { return element(by.css('.md-menu-panel.before')); } - beforeMenu() { return element(by.css('.md-menu-panel.before')); } + aboveMenu(): ElementFinder { return element(by.css('.md-menu-panel.above')); } - aboveMenu() { return element(by.css('.md-menu-panel.above')); } + combinedMenu(): ElementFinder { return element(by.css('.md-menu-panel.combined')); } - combinedMenu() { return element(by.css('.md-menu-panel.combined')); } - - // TODO(kara): move to common testing utility - pressKey(key: string): void { - browser.actions().sendKeys(key).perform(); - } - - // TODO(kara): move to common testing utility - expectFocusOn(el: any): void { - expect(browser.driver.switchTo().activeElement().getInnerHtml()) - .toBe(el.getInnerHtml()); - } - - expectMenuPresent(expected: boolean) { - return browser.isElementPresent(by.css('.md-menu-panel') as ProtractorBy) - .then((isPresent: boolean) => { - expect(isPresent).toBe(expected); - }); - } - - expectMenuLocation(el: ElementFinder, {x, y}: {x: number, y: number}) { - el.getLocation().then(loc => { - expect(loc.x).toEqual(x, 'Expect the x-position to be equal'); - expect(loc.y).toEqual(y, 'Expect the y-position to be equal'); - }); - } - - expectMenuAlignedWith(el: ElementFinder, id: string) { - element(by.id(id)).getLocation().then(loc => { - this.expectMenuLocation(el, {x: loc.x, y: loc.y}); - }); - } - - getResultText() { - return this.textArea().getText(); - } + getResultText() { return this.textArea().getText(); } } diff --git a/e2e/components/menu/menu.e2e.ts b/e2e/components/menu/menu.e2e.ts index 61e8e7fafb7f..7d2cf409da58 100644 --- a/e2e/components/menu/menu.e2e.ts +++ b/e2e/components/menu/menu.e2e.ts @@ -1,25 +1,26 @@ import {browser, Key, protractor} from 'protractor'; +import {E2EUtils} from '../../utils.e2e'; import {MenuPage} from './menu-page'; describe('menu', () => { + const menuSelector = '.md-menu-panel'; + const utils = new E2EUtils(); let page: MenuPage; - beforeEach(function() { - page = new MenuPage(); - }); + beforeEach(() => page = new MenuPage()); it('should open menu when the trigger is clicked', () => { - page.expectMenuPresent(false); + utils.expectToExist(menuSelector, false); page.trigger().click(); - page.expectMenuPresent(true); + utils.expectToExist(menuSelector); expect(page.menu().getText()).toEqual('One\nTwo\nThree\nFour'); }); it('should close menu when menu item is clicked', () => { page.trigger().click(); page.items(0).click(); - page.expectMenuPresent(false); + utils.expectToExist(menuSelector, false); }); it('should run click handlers on regular menu items', () => { @@ -40,18 +41,20 @@ describe('menu', () => { it('should support multiple triggers opening the same menu', () => { page.triggerTwo().click(); + expect(page.menu().getText()).toEqual('One\nTwo\nThree\nFour'); - page.expectMenuAlignedWith(page.menu(), 'trigger-two'); + utils.expectAlignedWith(page.menu(), '#trigger-two'); page.backdrop().click(); - page.expectMenuPresent(false); + utils.expectToExist(menuSelector, false); page.trigger().click(); + expect(page.menu().getText()).toEqual('One\nTwo\nThree\nFour'); - page.expectMenuAlignedWith(page.menu(), 'trigger'); + utils.expectAlignedWith(page.menu(), '#trigger'); page.backdrop().click(); - page.expectMenuPresent(false); + utils.expectToExist(menuSelector, false); }); it('should mirror classes on host to menu template in overlay', () => { @@ -65,82 +68,71 @@ describe('menu', () => { beforeEach(() => { // click start button to avoid tabbing past navigation page.start().click(); - page.pressKey(Key.TAB); + utils.pressKeys(Key.TAB); }); it('should auto-focus the first item when opened with ENTER', () => { - page.pressKey(Key.ENTER); - page.expectFocusOn(page.items(0)); + utils.pressKeys(Key.ENTER); + utils.expectFocusOn(page.items(0)); }); it('should auto-focus the first item when opened with SPACE', () => { - page.pressKey(Key.SPACE); - page.expectFocusOn(page.items(0)); + utils.pressKeys(Key.SPACE); + utils.expectFocusOn(page.items(0)); }); it('should not focus the first item when opened with mouse', () => { page.trigger().click(); - page.expectFocusOn(page.trigger()); + utils.expectFocusOn(page.trigger()); }); it('should focus subsequent items when down arrow is pressed', () => { - page.pressKey(Key.ENTER); - page.pressKey(Key.DOWN); - page.expectFocusOn(page.items(1)); + utils.pressKeys(Key.ENTER, Key.DOWN); + utils.expectFocusOn(page.items(1)); }); it('should focus previous items when up arrow is pressed', () => { - page.pressKey(Key.ENTER); - page.pressKey(Key.DOWN); - page.pressKey(Key.UP); - page.expectFocusOn(page.items(0)); + utils.pressKeys(Key.ENTER, Key.DOWN, Key.UP); + utils.expectFocusOn(page.items(0)); }); it('should skip disabled items using arrow keys', () => { - page.pressKey(Key.ENTER); - page.pressKey(Key.DOWN); - page.pressKey(Key.DOWN); - page.expectFocusOn(page.items(3)); + utils.pressKeys(Key.ENTER, Key.DOWN, Key.DOWN); + utils.expectFocusOn(page.items(3)); - page.pressKey(Key.UP); - page.expectFocusOn(page.items(1)); + utils.pressKeys(Key.UP); + utils.expectFocusOn(page.items(1)); }); it('should close the menu when tabbing past items', () => { - page.pressKey(Key.ENTER); - page.pressKey(Key.TAB); - page.expectMenuPresent(false); + utils.pressKeys(Key.ENTER, Key.TAB); + utils.expectToExist(menuSelector, false); - page.pressKey(Key.TAB); - page.pressKey(Key.ENTER); - page.expectMenuPresent(true); + utils.pressKeys(Key.TAB, Key.ENTER); + utils.expectToExist(menuSelector); - page.pressKey(protractor.Key.chord(Key.SHIFT, Key.TAB)); - page.expectMenuPresent(false); + utils.pressKeys(protractor.Key.chord(Key.SHIFT, Key.TAB)); + utils.expectToExist(menuSelector, false); }); it('should wrap back to menu when arrow keying past items', () => { - page.pressKey(Key.ENTER); - page.pressKey(Key.DOWN); - page.pressKey(Key.DOWN); - page.pressKey(Key.DOWN); - page.expectFocusOn(page.items(0)); - - page.pressKey(Key.UP); - page.expectFocusOn(page.items(3)); + let down = Key.DOWN; + utils.pressKeys(Key.ENTER, down, down, down); + utils.expectFocusOn(page.items(0)); + + utils.pressKeys(Key.UP); + utils.expectFocusOn(page.items(3)); }); it('should focus before and after trigger when tabbing past items', () => { - page.pressKey(Key.ENTER); - page.pressKey(Key.TAB); - page.expectFocusOn(page.triggerTwo()); + let shiftTab = protractor.Key.chord(Key.SHIFT, Key.TAB); - // navigate back to trigger - page.pressKey(protractor.Key.chord(protractor.Key.SHIFT, protractor.Key.TAB)); - page.pressKey(Key.ENTER); + utils.pressKeys(Key.ENTER, Key.TAB); + utils.expectFocusOn(page.triggerTwo()); - page.pressKey(protractor.Key.chord(protractor.Key.SHIFT, protractor.Key.TAB)); - page.expectFocusOn(page.start()); + // navigate back to trigger + utils.pressKeys(shiftTab, Key.ENTER, shiftTab); + utils.expectFocusOn(page.start()); }); }); @@ -151,7 +143,7 @@ describe('menu', () => { page.trigger().click(); // menu.x should equal trigger.x, menu.y should equal trigger.y - page.expectMenuAlignedWith(page.menu(), 'trigger'); + utils.expectAlignedWith(page.menu(), '#trigger'); }); it('should align overlay end to origin end when x-position is "before"', () => { @@ -162,7 +154,7 @@ describe('menu', () => { // menu = 112px wide. trigger = 60px wide. 112 - 60 = 52px of menu to the left of trigger. // trigger.x (left corner) - 52px (menu left of trigger) = expected menu.x (left corner) // menu.y should equal trigger.y because only x position has changed. - page.expectMenuLocation(page.beforeMenu(), {x: trigger.x - 52, y: trigger.y}); + utils.expectLocation(page.beforeMenu(), {x: trigger.x - 52, y: trigger.y}); }); }); @@ -174,7 +166,7 @@ describe('menu', () => { // menu.x should equal trigger.x because only y position has changed. // menu = 64px high. trigger = 20px high. 64 - 20 = 44px of menu extending up past trigger. // trigger.y (top corner) - 44px (menu above trigger) = expected menu.y (top corner) - page.expectMenuLocation(page.aboveMenu(), {x: trigger.x, y: trigger.y - 44}); + utils.expectLocation(page.aboveMenu(), {x: trigger.x, y: trigger.y - 44}); }); }); @@ -184,7 +176,7 @@ describe('menu', () => { // trigger.x (left corner) - 52px (menu left of trigger) = expected menu.x // trigger.y (top corner) - 44px (menu above trigger) = expected menu.y - page.expectMenuLocation(page.combinedMenu(), {x: trigger.x - 52, y: trigger.y - 44}); + utils.expectLocation(page.combinedMenu(), {x: trigger.x - 52, y: trigger.y - 44}); }); }); diff --git a/e2e/components/progress-bar/progress-bar.e2e.ts b/e2e/components/progress-bar/progress-bar.e2e.ts index 33c5898400b8..3b66dd2c5534 100644 --- a/e2e/components/progress-bar/progress-bar.e2e.ts +++ b/e2e/components/progress-bar/progress-bar.e2e.ts @@ -1,25 +1,24 @@ -import {browser, by, element} from 'protractor'; +import {browser} from 'protractor'; +import {E2EUtils} from '../../utils.e2e'; describe('progress-bar', () => { + const utils = new E2EUtils(); + beforeEach(() => browser.get('/progress-bar')); it('should render a determinate progress bar', () => { - shouldExist('md-progress-bar[mode="determinate"]'); + utils.expectToExist('md-progress-bar[mode="determinate"]'); }); it('should render a buffer progress bar', () => { - shouldExist('md-progress-bar[mode="buffer"]'); + utils.expectToExist('md-progress-bar[mode="buffer"]'); }); it('should render a query progress bar', () => { - shouldExist('md-progress-bar[mode="query"]'); + utils.expectToExist('md-progress-bar[mode="query"]'); }); it('should render a indeterminate progress bar', () => { - shouldExist('md-progress-bar[mode="indeterminate"]'); + utils.expectToExist('md-progress-bar[mode="indeterminate"]'); }); - - function shouldExist(selector: string): void { - expect(element(by.css(selector)).isPresent()).toBe(true); - } }); diff --git a/e2e/components/radio/radio.e2e.ts b/e2e/components/radio/radio.e2e.ts index 37c81509dc08..09781fbedb92 100644 --- a/e2e/components/radio/radio.e2e.ts +++ b/e2e/components/radio/radio.e2e.ts @@ -1,13 +1,10 @@ import {browser, by, element} from 'protractor'; -describe('radio', function () { +describe('radio', () => { + describe('disabling behavior', () => { + beforeEach(() => browser.get('/radio')); - describe('disabling behavior', function () { - beforeEach(function() { - browser.get('/radio'); - }); - - it('should be checked when clicked', function () { + it('should be checked when clicked', () => { element(by.id('water')).click(); element(by.id('water')).getAttribute('class').then((value: string) => { expect(value).toContain('md-radio-checked'); @@ -31,7 +28,7 @@ describe('radio', function () { }); }); - it('should be disabled when disable the radio group', function () { + it('should be disabled when disable the radio group', () => { element(by.id('toggle-disable')).click(); element(by.id('water')).click(); element(by.id('water')).getAttribute('class').then((value: string) => { diff --git a/e2e/components/tabs/tabs.e2e.ts b/e2e/components/tabs/tabs.e2e.ts index 9a9fd53a3032..cce3e98fbcf8 100644 --- a/e2e/components/tabs/tabs.e2e.ts +++ b/e2e/components/tabs/tabs.e2e.ts @@ -1,6 +1,9 @@ import {browser, by, element, ElementArrayFinder, ElementFinder, Key} from 'protractor'; +import {E2EUtils} from '../../utils.e2e'; describe('tabs', () => { + const utils = new E2EUtils(); + describe('basic behavior', () => { let tabGroup: ElementFinder; let tabLabels: ElementArrayFinder; @@ -24,35 +27,33 @@ describe('tabs', () => { }); it('should change focus with keyboard interaction', () => { + let right = Key.RIGHT; + let left = Key.LEFT; + tabLabels.get(0).click(); expect(getFocusStates(tabLabels)).toEqual([true, false, false]); - pressKey(Key.RIGHT); + utils.pressKeys(right); expect(getFocusStates(tabLabels)).toEqual([false, true, false]); - pressKey(Key.RIGHT); + utils.pressKeys(right); expect(getFocusStates(tabLabels)).toEqual([false, false, true]); - pressKey(Key.RIGHT); + utils.pressKeys(right); expect(getFocusStates(tabLabels)).toEqual([false, false, true]); - pressKey(Key.LEFT); + utils.pressKeys(left); expect(getFocusStates(tabLabels)).toEqual([false, true, false]); - pressKey(Key.LEFT); + utils.pressKeys(left); expect(getFocusStates(tabLabels)).toEqual([true, false, false]); - pressKey(Key.LEFT); + utils.pressKeys(left); expect(getFocusStates(tabLabels)).toEqual([true, false, false]); }); }); }); -/** A helper function to perform the sendKey action. */ -function pressKey(key: string) { - browser.actions().sendKeys(key).perform(); -} - /** * Returns an array of true/false that represents the focus states of the provided elements. */ diff --git a/e2e/utils.e2e.ts b/e2e/utils.e2e.ts new file mode 100644 index 000000000000..79e86871db52 --- /dev/null +++ b/e2e/utils.e2e.ts @@ -0,0 +1,77 @@ +/** + * A set of utility functions for writing E2E tests. + */ +export class E2EUtils { + /** + * Asserts that an element exists. + */ + expectToExist(selector: string, expected = true): webdriver.promise.Promise { + return this.waitForElement(selector).then(isPresent => { + expect(isPresent).toBe(expected, `Expected "${selector}"${expected ? '' : ' not'} to exist`); + }); + } + + /** + * Asserts that an element is focused. + */ + expectFocusOn(element: FinderResult, expected = true): void { + expect(browser.driver.switchTo().activeElement().getInnerHtml()).toBe( + this._getElement(element).getInnerHtml(), + `Expected element${expected ? '' : ' not'} to be focused.` + ); + } + + /** + * Asserts that an element has a certan location. + */ + expectLocation(element: FinderResult, {x, y}: Point): void { + this._getElement(element).getLocation().then((location: Point) => { + expect(location.x).toEqual(Math.round(x)); + expect(location.y).toEqual(Math.round(y)); + }); + } + + /** + * Asserts that one element is aligned with another. + */ + expectAlignedWith(element: FinderResult, otherElement: FinderResult): void { + this._getElement(otherElement).getLocation().then((location: Point) => { + this.expectLocation(this._getElement(element), location); + }); + } + + /** + * Waits for an element to be rendered. + */ + waitForElement(selector: string): webdriver.promise.Promise { + return browser.isElementPresent(by.css(selector)); + } + + /** + * Presses a single key or a sequence of keys. + */ + pressKeys(...keys: string[]): void { + let actions = browser.actions(); + actions.sendKeys.call(actions, keys).perform(); + } + + /** + * Clicks an element at a specific point. Useful if there's another element + * that covers part of the target and can catch the click. + */ + clickElementAtPoint(element: FinderResult, coords: Point): void { + let webElement = this._getElement(element).getWebElement(); + browser.actions().mouseMove(webElement, coords).click().perform(); + } + + /** + * Normalizes either turning a selector into an + * ElementFinder or returning the finder itself. + */ + private _getElement(el: FinderResult): protractor.ElementFinder { + return typeof el === 'string' ? element(by.css(el)) : el; + } +} + +interface Point { x: number; y: number; } +type FinderResult = protractor.ElementFinder | string; From 32fde96ca2b23f68f69ccc05c6f4d1cb5062b589 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Tue, 20 Dec 2016 13:27:29 +0200 Subject: [PATCH 2/7] Add types to the e2e utils and fix the selector on the dialog tests. --- e2e/components/dialog/dialog.e2e.ts | 2 +- e2e/utils.e2e.ts | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/e2e/components/dialog/dialog.e2e.ts b/e2e/components/dialog/dialog.e2e.ts index 29873cff269b..1a1530baae4e 100644 --- a/e2e/components/dialog/dialog.e2e.ts +++ b/e2e/components/dialog/dialog.e2e.ts @@ -91,6 +91,6 @@ describe('dialog', () => { } function clickOnBackrop() { - utils.clickElementAtPoint('.md-overlay-backdrop', { x: 0, y: 0 }); + utils.clickElementAtPoint('.cdk-overlay-backdrop', { x: 0, y: 0 }); } }); diff --git a/e2e/utils.e2e.ts b/e2e/utils.e2e.ts index 79e86871db52..4b5040f77074 100644 --- a/e2e/utils.e2e.ts +++ b/e2e/utils.e2e.ts @@ -1,3 +1,5 @@ +import {ElementFinder, browser, by, element, ProtractorBy} from 'protractor'; + /** * A set of utility functions for writing E2E tests. */ @@ -16,7 +18,7 @@ export class E2EUtils { */ expectFocusOn(element: FinderResult, expected = true): void { expect(browser.driver.switchTo().activeElement().getInnerHtml()).toBe( - this._getElement(element).getInnerHtml(), + (this._getElement(element) as any).getInnerHtml(), `Expected element${expected ? '' : ' not'} to be focused.` ); } @@ -44,7 +46,7 @@ export class E2EUtils { * Waits for an element to be rendered. */ waitForElement(selector: string): webdriver.promise.Promise { - return browser.isElementPresent(by.css(selector)); + return browser.isElementPresent(by.css(selector) as ProtractorBy); } /** @@ -68,10 +70,10 @@ export class E2EUtils { * Normalizes either turning a selector into an * ElementFinder or returning the finder itself. */ - private _getElement(el: FinderResult): protractor.ElementFinder { + private _getElement(el: FinderResult): ElementFinder { return typeof el === 'string' ? element(by.css(el)) : el; } } -interface Point { x: number; y: number; } -type FinderResult = protractor.ElementFinder | string; +export interface Point { x: number; y: number; } +export type FinderResult = ElementFinder | string; From 8c06b495b67c421f14fb6decc2b002ab7d9ca667 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Tue, 20 Dec 2016 13:48:30 +0200 Subject: [PATCH 3/7] Remove unnecessary rounding. --- e2e/utils.e2e.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/utils.e2e.ts b/e2e/utils.e2e.ts index 4b5040f77074..69ce509bc618 100644 --- a/e2e/utils.e2e.ts +++ b/e2e/utils.e2e.ts @@ -28,8 +28,8 @@ export class E2EUtils { */ expectLocation(element: FinderResult, {x, y}: Point): void { this._getElement(element).getLocation().then((location: Point) => { - expect(location.x).toEqual(Math.round(x)); - expect(location.y).toEqual(Math.round(y)); + expect(location.x).toEqual(x); + expect(location.y).toEqual(y); }); } From 9babd208ec37346941ff7b7bd296e28ffc71eb04 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Tue, 20 Dec 2016 13:49:57 +0200 Subject: [PATCH 4/7] Remove `any` cast. --- e2e/utils.e2e.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/utils.e2e.ts b/e2e/utils.e2e.ts index 69ce509bc618..99a9755fb6f9 100644 --- a/e2e/utils.e2e.ts +++ b/e2e/utils.e2e.ts @@ -18,7 +18,7 @@ export class E2EUtils { */ expectFocusOn(element: FinderResult, expected = true): void { expect(browser.driver.switchTo().activeElement().getInnerHtml()).toBe( - (this._getElement(element) as any).getInnerHtml(), + this._getElement(element).getWebElement().getInnerHtml(), `Expected element${expected ? '' : ' not'} to be focused.` ); } From 4fae2adb1c5d6891ecadbc880520cf8b1f9759ba Mon Sep 17 00:00:00 2001 From: crisbeto Date: Tue, 20 Dec 2016 14:06:09 +0200 Subject: [PATCH 5/7] Simplify the slide toggle tests. --- .../slide-toggle/slide-toggle.e2e.ts | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/e2e/components/slide-toggle/slide-toggle.e2e.ts b/e2e/components/slide-toggle/slide-toggle.e2e.ts index c68610615a6a..5b2f62c4b294 100644 --- a/e2e/components/slide-toggle/slide-toggle.e2e.ts +++ b/e2e/components/slide-toggle/slide-toggle.e2e.ts @@ -1,48 +1,49 @@ -import {browser, element, by, protractor} from 'protractor'; +import {browser, element, by, Key} from 'protractor'; +import {E2EUtils} from '../../utils.e2e'; describe('slide-toggle', () => { + const utils = new E2EUtils(); + const getInput = () => element(by.css('#normal-slide-toggle input')); + const getNormalToggle = () => element(by.css('#normal-slide-toggle')); beforeEach(() => browser.get('slide-toggle')); it('should render a slide-toggle', () => { - expect(element(by.css('md-slide-toggle')).isPresent()).toBe(true); + utils.expectToExist('md-slide-toggle'); }); it('should change the checked state on click', () => { - let slideToggleEl = element(by.css('#normal-slide-toggle')); - let inputEl = element(by.css('#normal-slide-toggle input')); + let inputEl = getInput(); expect(inputEl.getAttribute('checked')).toBeFalsy('Expect slide-toggle to be unchecked'); - slideToggleEl.click(); + getNormalToggle().click(); expect(inputEl.getAttribute('checked')).toBeTruthy('Expect slide-toggle to be checked'); }); it('should change the checked state on click', () => { - let slideToggleEl = element(by.css('#normal-slide-toggle')); - let inputEl = element(by.css('#normal-slide-toggle input')); + let inputEl = getInput(); expect(inputEl.getAttribute('checked')).toBeFalsy('Expect slide-toggle to be unchecked'); - slideToggleEl.click(); + getNormalToggle().click(); expect(inputEl.getAttribute('checked')).toBeTruthy('Expect slide-toggle to be checked'); }); it('should not change the checked state on click when disabled', () => { - let slideToggleEl = element(by.css('#disabled-slide-toggle')); - let inputEl = element(by.css('#disabled-slide-toggle input')); + let inputEl = getInput(); expect(inputEl.getAttribute('checked')).toBeFalsy('Expect slide-toggle to be unchecked'); - slideToggleEl.click(); + element(by.css('#disabled-slide-toggle')).click(); expect(inputEl.getAttribute('checked')).toBeFalsy('Expect slide-toggle to be unchecked'); }); it('should move the thumb on state change', () => { - let slideToggleEl = element(by.css('#normal-slide-toggle')); + let slideToggleEl = getNormalToggle(); let thumbEl = element(by.css('#normal-slide-toggle .md-slide-toggle-thumb-container')); let previousX = thumbEl.getLocation().then(pos => pos.x); @@ -55,11 +56,11 @@ describe('slide-toggle', () => { }); it('should toggle the slide-toggle on space key', () => { - let inputEl = element(by.css('#normal-slide-toggle input')); + let inputEl = getInput(); expect(inputEl.getAttribute('checked')).toBeFalsy('Expect slide-toggle to be unchecked'); - inputEl.sendKeys(protractor.Key.SPACE); + inputEl.sendKeys(Key.SPACE); expect(inputEl.getAttribute('checked')).toBeTruthy('Expect slide-toggle to be checked'); }); From 55eac9969fb7ccd1ec8d29c1de1eb0c26925a78e Mon Sep 17 00:00:00 2001 From: crisbeto Date: Thu, 5 Jan 2017 21:38:29 +0100 Subject: [PATCH 6/7] Split the E2E utils class into smaller files. --- e2e/components/dialog/dialog.e2e.ts | 34 ++++---- e2e/components/grid-list/grid-list.e2e.ts | 8 +- e2e/components/list/list.e2e.ts | 8 +- e2e/components/menu/menu.e2e.ts | 82 +++++++++---------- .../progress-bar/progress-bar.e2e.ts | 12 ++- .../slide-toggle/slide-toggle.e2e.ts | 5 +- e2e/components/tabs/tabs.e2e.ts | 16 ++-- e2e/util/actions.ts | 21 +++++ e2e/util/asserts.ts | 41 ++++++++++ e2e/util/query.ts | 18 ++++ e2e/utils.e2e.ts | 79 ------------------ 11 files changed, 158 insertions(+), 166 deletions(-) create mode 100644 e2e/util/actions.ts create mode 100644 e2e/util/asserts.ts create mode 100644 e2e/util/query.ts delete mode 100644 e2e/utils.e2e.ts diff --git a/e2e/components/dialog/dialog.e2e.ts b/e2e/components/dialog/dialog.e2e.ts index 1a1530baae4e..907713ceeaae 100644 --- a/e2e/components/dialog/dialog.e2e.ts +++ b/e2e/components/dialog/dialog.e2e.ts @@ -1,14 +1,14 @@ import {browser, by, element, Key} from 'protractor'; -import {E2EUtils} from '../../utils.e2e'; +import {expectToExist, expectFocusOn} from '../../util/asserts'; +import {pressKeys, clickElementAtPoint} from '../../util/actions'; +import {waitForElement} from '../../util/query'; describe('dialog', () => { - const utils = new E2EUtils(); - beforeEach(() => browser.get('/dialog')); it('should open a dialog', () => { element(by.id('default')).click(); - utils.expectToExist('md-dialog-container'); + expectToExist('md-dialog-container'); }); it('should close by clicking on the backdrop', () => { @@ -16,7 +16,7 @@ describe('dialog', () => { waitForDialog().then(() => { clickOnBackrop(); - utils.expectToExist('md-dialog-container', false); + expectToExist('md-dialog-container', false); }); }); @@ -24,8 +24,8 @@ describe('dialog', () => { element(by.id('default')).click(); waitForDialog().then(() => { - utils.pressKeys(Key.ESCAPE); - utils.expectToExist('md-dialog-container', false); + pressKeys(Key.ESCAPE); + expectToExist('md-dialog-container', false); }); }); @@ -34,7 +34,7 @@ describe('dialog', () => { waitForDialog().then(() => { element(by.id('close')).click(); - utils.expectToExist('md-dialog-container', false); + expectToExist('md-dialog-container', false); }); }); @@ -42,7 +42,7 @@ describe('dialog', () => { element(by.id('default')).click(); waitForDialog().then(() => { - utils.expectFocusOn('md-dialog-container input'); + expectFocusOn('md-dialog-container input'); }); }); @@ -53,7 +53,7 @@ describe('dialog', () => { waitForDialog().then(() => { clickOnBackrop(); - utils.expectFocusOn(openButton); + expectFocusOn(openButton); }); }); @@ -63,8 +63,8 @@ describe('dialog', () => { waitForDialog().then(() => { let tab = Key.TAB; - utils.pressKeys(tab, tab, tab); - utils.expectFocusOn('#close'); + pressKeys(tab, tab, tab); + expectFocusOn('#close'); }); }); @@ -73,7 +73,7 @@ describe('dialog', () => { waitForDialog().then(() => { clickOnBackrop(); - utils.expectToExist('md-dialog-container'); + expectToExist('md-dialog-container'); }); }); @@ -81,16 +81,16 @@ describe('dialog', () => { element(by.id('disabled')).click(); waitForDialog().then(() => { - utils.pressKeys(Key.ESCAPE); - utils.expectToExist('md-dialog-container'); + pressKeys(Key.ESCAPE); + expectToExist('md-dialog-container'); }); }); function waitForDialog() { - return utils.waitForElement('md-dialog-container'); + return waitForElement('md-dialog-container'); } function clickOnBackrop() { - utils.clickElementAtPoint('.cdk-overlay-backdrop', { x: 0, y: 0 }); + clickElementAtPoint('.cdk-overlay-backdrop', { x: 0, y: 0 }); } }); diff --git a/e2e/components/grid-list/grid-list.e2e.ts b/e2e/components/grid-list/grid-list.e2e.ts index d9eee8c4dc2a..814e0bbbc697 100644 --- a/e2e/components/grid-list/grid-list.e2e.ts +++ b/e2e/components/grid-list/grid-list.e2e.ts @@ -1,16 +1,14 @@ import {browser} from 'protractor'; -import {E2EUtils} from '../../utils.e2e'; +import {expectToExist} from '../../util/asserts'; describe('grid-list', () => { - const utils = new E2EUtils(); - beforeEach(() => browser.get('/grid-list')); it('should render a grid list container', () => { - utils.expectToExist('md-grid-list'); + expectToExist('md-grid-list'); }); it('should render list items inside the grid list container', () => { - utils.expectToExist('md-grid-list md-grid-tile'); + expectToExist('md-grid-list md-grid-tile'); }); }); diff --git a/e2e/components/list/list.e2e.ts b/e2e/components/list/list.e2e.ts index 203290dfac67..e616f1ffcde5 100644 --- a/e2e/components/list/list.e2e.ts +++ b/e2e/components/list/list.e2e.ts @@ -1,16 +1,14 @@ import {browser} from 'protractor'; -import {E2EUtils} from '../../utils.e2e'; +import {expectToExist} from '../../util/asserts'; describe('list', () => { - const utils = new E2EUtils(); - beforeEach(() => browser.get('/list')); it('should render a list container', () => { - utils.expectToExist('md-list'); + expectToExist('md-list'); }); it('should render list items inside the list container', () => { - utils.expectToExist('md-list md-list-item'); + expectToExist('md-list md-list-item'); }); }); diff --git a/e2e/components/menu/menu.e2e.ts b/e2e/components/menu/menu.e2e.ts index 7d2cf409da58..36e330fbf60d 100644 --- a/e2e/components/menu/menu.e2e.ts +++ b/e2e/components/menu/menu.e2e.ts @@ -1,26 +1,26 @@ import {browser, Key, protractor} from 'protractor'; -import {E2EUtils} from '../../utils.e2e'; import {MenuPage} from './menu-page'; +import {expectToExist, expectAlignedWith, expectFocusOn, expectLocation} from '../../util/asserts'; +import {pressKeys} from '../../util/actions'; describe('menu', () => { const menuSelector = '.md-menu-panel'; - const utils = new E2EUtils(); let page: MenuPage; beforeEach(() => page = new MenuPage()); it('should open menu when the trigger is clicked', () => { - utils.expectToExist(menuSelector, false); + expectToExist(menuSelector, false); page.trigger().click(); - utils.expectToExist(menuSelector); + expectToExist(menuSelector); expect(page.menu().getText()).toEqual('One\nTwo\nThree\nFour'); }); it('should close menu when menu item is clicked', () => { page.trigger().click(); page.items(0).click(); - utils.expectToExist(menuSelector, false); + expectToExist(menuSelector, false); }); it('should run click handlers on regular menu items', () => { @@ -43,18 +43,18 @@ describe('menu', () => { page.triggerTwo().click(); expect(page.menu().getText()).toEqual('One\nTwo\nThree\nFour'); - utils.expectAlignedWith(page.menu(), '#trigger-two'); + expectAlignedWith(page.menu(), '#trigger-two'); page.backdrop().click(); - utils.expectToExist(menuSelector, false); + expectToExist(menuSelector, false); page.trigger().click(); expect(page.menu().getText()).toEqual('One\nTwo\nThree\nFour'); - utils.expectAlignedWith(page.menu(), '#trigger'); + expectAlignedWith(page.menu(), '#trigger'); page.backdrop().click(); - utils.expectToExist(menuSelector, false); + expectToExist(menuSelector, false); }); it('should mirror classes on host to menu template in overlay', () => { @@ -68,71 +68,71 @@ describe('menu', () => { beforeEach(() => { // click start button to avoid tabbing past navigation page.start().click(); - utils.pressKeys(Key.TAB); + pressKeys(Key.TAB); }); it('should auto-focus the first item when opened with ENTER', () => { - utils.pressKeys(Key.ENTER); - utils.expectFocusOn(page.items(0)); + pressKeys(Key.ENTER); + expectFocusOn(page.items(0)); }); it('should auto-focus the first item when opened with SPACE', () => { - utils.pressKeys(Key.SPACE); - utils.expectFocusOn(page.items(0)); + pressKeys(Key.SPACE); + expectFocusOn(page.items(0)); }); it('should not focus the first item when opened with mouse', () => { page.trigger().click(); - utils.expectFocusOn(page.trigger()); + expectFocusOn(page.trigger()); }); it('should focus subsequent items when down arrow is pressed', () => { - utils.pressKeys(Key.ENTER, Key.DOWN); - utils.expectFocusOn(page.items(1)); + pressKeys(Key.ENTER, Key.DOWN); + expectFocusOn(page.items(1)); }); it('should focus previous items when up arrow is pressed', () => { - utils.pressKeys(Key.ENTER, Key.DOWN, Key.UP); - utils.expectFocusOn(page.items(0)); + pressKeys(Key.ENTER, Key.DOWN, Key.UP); + expectFocusOn(page.items(0)); }); it('should skip disabled items using arrow keys', () => { - utils.pressKeys(Key.ENTER, Key.DOWN, Key.DOWN); - utils.expectFocusOn(page.items(3)); + pressKeys(Key.ENTER, Key.DOWN, Key.DOWN); + expectFocusOn(page.items(3)); - utils.pressKeys(Key.UP); - utils.expectFocusOn(page.items(1)); + pressKeys(Key.UP); + expectFocusOn(page.items(1)); }); it('should close the menu when tabbing past items', () => { - utils.pressKeys(Key.ENTER, Key.TAB); - utils.expectToExist(menuSelector, false); + pressKeys(Key.ENTER, Key.TAB); + expectToExist(menuSelector, false); - utils.pressKeys(Key.TAB, Key.ENTER); - utils.expectToExist(menuSelector); + pressKeys(Key.TAB, Key.ENTER); + expectToExist(menuSelector); - utils.pressKeys(protractor.Key.chord(Key.SHIFT, Key.TAB)); - utils.expectToExist(menuSelector, false); + pressKeys(protractor.Key.chord(Key.SHIFT, Key.TAB)); + expectToExist(menuSelector, false); }); it('should wrap back to menu when arrow keying past items', () => { let down = Key.DOWN; - utils.pressKeys(Key.ENTER, down, down, down); - utils.expectFocusOn(page.items(0)); + pressKeys(Key.ENTER, down, down, down); + expectFocusOn(page.items(0)); - utils.pressKeys(Key.UP); - utils.expectFocusOn(page.items(3)); + pressKeys(Key.UP); + expectFocusOn(page.items(3)); }); it('should focus before and after trigger when tabbing past items', () => { let shiftTab = protractor.Key.chord(Key.SHIFT, Key.TAB); - utils.pressKeys(Key.ENTER, Key.TAB); - utils.expectFocusOn(page.triggerTwo()); + pressKeys(Key.ENTER, Key.TAB); + expectFocusOn(page.triggerTwo()); // navigate back to trigger - utils.pressKeys(shiftTab, Key.ENTER, shiftTab); - utils.expectFocusOn(page.start()); + pressKeys(shiftTab, Key.ENTER, shiftTab); + expectFocusOn(page.start()); }); }); @@ -143,7 +143,7 @@ describe('menu', () => { page.trigger().click(); // menu.x should equal trigger.x, menu.y should equal trigger.y - utils.expectAlignedWith(page.menu(), '#trigger'); + expectAlignedWith(page.menu(), '#trigger'); }); it('should align overlay end to origin end when x-position is "before"', () => { @@ -154,7 +154,7 @@ describe('menu', () => { // menu = 112px wide. trigger = 60px wide. 112 - 60 = 52px of menu to the left of trigger. // trigger.x (left corner) - 52px (menu left of trigger) = expected menu.x (left corner) // menu.y should equal trigger.y because only x position has changed. - utils.expectLocation(page.beforeMenu(), {x: trigger.x - 52, y: trigger.y}); + expectLocation(page.beforeMenu(), {x: trigger.x - 52, y: trigger.y}); }); }); @@ -166,7 +166,7 @@ describe('menu', () => { // menu.x should equal trigger.x because only y position has changed. // menu = 64px high. trigger = 20px high. 64 - 20 = 44px of menu extending up past trigger. // trigger.y (top corner) - 44px (menu above trigger) = expected menu.y (top corner) - utils.expectLocation(page.aboveMenu(), {x: trigger.x, y: trigger.y - 44}); + expectLocation(page.aboveMenu(), {x: trigger.x, y: trigger.y - 44}); }); }); @@ -176,7 +176,7 @@ describe('menu', () => { // trigger.x (left corner) - 52px (menu left of trigger) = expected menu.x // trigger.y (top corner) - 44px (menu above trigger) = expected menu.y - utils.expectLocation(page.combinedMenu(), {x: trigger.x - 52, y: trigger.y - 44}); + expectLocation(page.combinedMenu(), {x: trigger.x - 52, y: trigger.y - 44}); }); }); diff --git a/e2e/components/progress-bar/progress-bar.e2e.ts b/e2e/components/progress-bar/progress-bar.e2e.ts index 3b66dd2c5534..7bc4eec54c3e 100644 --- a/e2e/components/progress-bar/progress-bar.e2e.ts +++ b/e2e/components/progress-bar/progress-bar.e2e.ts @@ -1,24 +1,22 @@ import {browser} from 'protractor'; -import {E2EUtils} from '../../utils.e2e'; +import {expectToExist} from '../../util/asserts'; describe('progress-bar', () => { - const utils = new E2EUtils(); - beforeEach(() => browser.get('/progress-bar')); it('should render a determinate progress bar', () => { - utils.expectToExist('md-progress-bar[mode="determinate"]'); + expectToExist('md-progress-bar[mode="determinate"]'); }); it('should render a buffer progress bar', () => { - utils.expectToExist('md-progress-bar[mode="buffer"]'); + expectToExist('md-progress-bar[mode="buffer"]'); }); it('should render a query progress bar', () => { - utils.expectToExist('md-progress-bar[mode="query"]'); + expectToExist('md-progress-bar[mode="query"]'); }); it('should render a indeterminate progress bar', () => { - utils.expectToExist('md-progress-bar[mode="indeterminate"]'); + expectToExist('md-progress-bar[mode="indeterminate"]'); }); }); diff --git a/e2e/components/slide-toggle/slide-toggle.e2e.ts b/e2e/components/slide-toggle/slide-toggle.e2e.ts index 5b2f62c4b294..96f839e6e3ef 100644 --- a/e2e/components/slide-toggle/slide-toggle.e2e.ts +++ b/e2e/components/slide-toggle/slide-toggle.e2e.ts @@ -1,15 +1,14 @@ import {browser, element, by, Key} from 'protractor'; -import {E2EUtils} from '../../utils.e2e'; +import {expectToExist} from '../../util/asserts'; describe('slide-toggle', () => { - const utils = new E2EUtils(); const getInput = () => element(by.css('#normal-slide-toggle input')); const getNormalToggle = () => element(by.css('#normal-slide-toggle')); beforeEach(() => browser.get('slide-toggle')); it('should render a slide-toggle', () => { - utils.expectToExist('md-slide-toggle'); + expectToExist('md-slide-toggle'); }); it('should change the checked state on click', () => { diff --git a/e2e/components/tabs/tabs.e2e.ts b/e2e/components/tabs/tabs.e2e.ts index cce3e98fbcf8..a8c7c5f72f57 100644 --- a/e2e/components/tabs/tabs.e2e.ts +++ b/e2e/components/tabs/tabs.e2e.ts @@ -1,9 +1,7 @@ import {browser, by, element, ElementArrayFinder, ElementFinder, Key} from 'protractor'; -import {E2EUtils} from '../../utils.e2e'; +import {pressKeys} from '../../util/actions'; describe('tabs', () => { - const utils = new E2EUtils(); - describe('basic behavior', () => { let tabGroup: ElementFinder; let tabLabels: ElementArrayFinder; @@ -33,22 +31,22 @@ describe('tabs', () => { tabLabels.get(0).click(); expect(getFocusStates(tabLabels)).toEqual([true, false, false]); - utils.pressKeys(right); + pressKeys(right); expect(getFocusStates(tabLabels)).toEqual([false, true, false]); - utils.pressKeys(right); + pressKeys(right); expect(getFocusStates(tabLabels)).toEqual([false, false, true]); - utils.pressKeys(right); + pressKeys(right); expect(getFocusStates(tabLabels)).toEqual([false, false, true]); - utils.pressKeys(left); + pressKeys(left); expect(getFocusStates(tabLabels)).toEqual([false, true, false]); - utils.pressKeys(left); + pressKeys(left); expect(getFocusStates(tabLabels)).toEqual([true, false, false]); - utils.pressKeys(left); + pressKeys(left); expect(getFocusStates(tabLabels)).toEqual([true, false, false]); }); }); diff --git a/e2e/util/actions.ts b/e2e/util/actions.ts new file mode 100644 index 000000000000..8199481a8951 --- /dev/null +++ b/e2e/util/actions.ts @@ -0,0 +1,21 @@ +import {browser} from 'protractor'; +import {getElement, FinderResult} from './query'; + +/** + * Presses a single key or a sequence of keys. + */ +export function pressKeys(...keys: string[]): void { + let actions = browser.actions(); + actions.sendKeys.call(actions, keys).perform(); +} + +/** + * Clicks an element at a specific point. Useful if there's another element + * that covers part of the target and can catch the click. + */ +export function clickElementAtPoint(element: FinderResult, coords: Point): void { + let webElement = getElement(element).getWebElement(); + browser.actions().mouseMove(webElement, coords).click().perform(); +} + +export interface Point { x: number; y: number; } diff --git a/e2e/util/asserts.ts b/e2e/util/asserts.ts new file mode 100644 index 000000000000..e00d09d1cdab --- /dev/null +++ b/e2e/util/asserts.ts @@ -0,0 +1,41 @@ +import {browser} from 'protractor'; +import {getElement, FinderResult} from './query'; +import {Point} from './actions'; + +/** + * Asserts that an element exists. + */ +export function expectToExist(selector: string, expected = true): webdriver.promise.Promise { + return this.waitForElement(selector).then((isPresent: boolean) => { + expect(isPresent).toBe(expected, `Expected "${selector}"${expected ? '' : ' not'} to exist`); + }); +} + +/** + * Asserts that an element is focused. + */ +export function expectFocusOn(element: FinderResult, expected = true): void { + expect(browser.driver.switchTo().activeElement().getInnerHtml()).toBe( + getElement(element).getWebElement().getInnerHtml(), + `Expected element${expected ? '' : ' not'} to be focused.` + ); +} + +/** + * Asserts that an element has a certan location. + */ +export function expectLocation(element: FinderResult, {x, y}: Point): void { + getElement(element).getLocation().then((location: Point) => { + expect(location.x).toEqual(x); + expect(location.y).toEqual(y); + }); +} + +/** + * Asserts that one element is aligned with another. + */ +export function expectAlignedWith(element: FinderResult, otherElement: FinderResult): void { + getElement(otherElement).getLocation().then((location: Point) => { + this.expectLocation(getElement(element), location); + }); +} diff --git a/e2e/util/query.ts b/e2e/util/query.ts new file mode 100644 index 000000000000..a7e48ea4ed2c --- /dev/null +++ b/e2e/util/query.ts @@ -0,0 +1,18 @@ +import {ElementFinder, by, element, ProtractorBy, browser} from 'protractor'; + +/** + * Normalizes either turning a selector into an + * ElementFinder or returning the finder itself. + */ +export function getElement(el: FinderResult): ElementFinder { + return typeof el === 'string' ? element(by.css(el)) : el; +} + +/** + * Waits for an element to be rendered. + */ +export function waitForElement(selector: string): webdriver.promise.Promise { + return browser.isElementPresent(by.css(selector) as ProtractorBy); +} + +export type FinderResult = ElementFinder | string; diff --git a/e2e/utils.e2e.ts b/e2e/utils.e2e.ts deleted file mode 100644 index 99a9755fb6f9..000000000000 --- a/e2e/utils.e2e.ts +++ /dev/null @@ -1,79 +0,0 @@ -import {ElementFinder, browser, by, element, ProtractorBy} from 'protractor'; - -/** - * A set of utility functions for writing E2E tests. - */ -export class E2EUtils { - /** - * Asserts that an element exists. - */ - expectToExist(selector: string, expected = true): webdriver.promise.Promise { - return this.waitForElement(selector).then(isPresent => { - expect(isPresent).toBe(expected, `Expected "${selector}"${expected ? '' : ' not'} to exist`); - }); - } - - /** - * Asserts that an element is focused. - */ - expectFocusOn(element: FinderResult, expected = true): void { - expect(browser.driver.switchTo().activeElement().getInnerHtml()).toBe( - this._getElement(element).getWebElement().getInnerHtml(), - `Expected element${expected ? '' : ' not'} to be focused.` - ); - } - - /** - * Asserts that an element has a certan location. - */ - expectLocation(element: FinderResult, {x, y}: Point): void { - this._getElement(element).getLocation().then((location: Point) => { - expect(location.x).toEqual(x); - expect(location.y).toEqual(y); - }); - } - - /** - * Asserts that one element is aligned with another. - */ - expectAlignedWith(element: FinderResult, otherElement: FinderResult): void { - this._getElement(otherElement).getLocation().then((location: Point) => { - this.expectLocation(this._getElement(element), location); - }); - } - - /** - * Waits for an element to be rendered. - */ - waitForElement(selector: string): webdriver.promise.Promise { - return browser.isElementPresent(by.css(selector) as ProtractorBy); - } - - /** - * Presses a single key or a sequence of keys. - */ - pressKeys(...keys: string[]): void { - let actions = browser.actions(); - actions.sendKeys.call(actions, keys).perform(); - } - - /** - * Clicks an element at a specific point. Useful if there's another element - * that covers part of the target and can catch the click. - */ - clickElementAtPoint(element: FinderResult, coords: Point): void { - let webElement = this._getElement(element).getWebElement(); - browser.actions().mouseMove(webElement, coords).click().perform(); - } - - /** - * Normalizes either turning a selector into an - * ElementFinder or returning the finder itself. - */ - private _getElement(el: FinderResult): ElementFinder { - return typeof el === 'string' ? element(by.css(el)) : el; - } -} - -export interface Point { x: number; y: number; } -export type FinderResult = ElementFinder | string; From 3559a790f30f8b9a3b96161bfe5474fb01878b82 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Thu, 5 Jan 2017 22:18:58 +0100 Subject: [PATCH 7/7] Fix a leftover. --- e2e/util/asserts.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/util/asserts.ts b/e2e/util/asserts.ts index e00d09d1cdab..34ebdb24632f 100644 --- a/e2e/util/asserts.ts +++ b/e2e/util/asserts.ts @@ -1,12 +1,12 @@ import {browser} from 'protractor'; -import {getElement, FinderResult} from './query'; +import {getElement, FinderResult, waitForElement} from './query'; import {Point} from './actions'; /** * Asserts that an element exists. */ export function expectToExist(selector: string, expected = true): webdriver.promise.Promise { - return this.waitForElement(selector).then((isPresent: boolean) => { + return waitForElement(selector).then((isPresent: boolean) => { expect(isPresent).toBe(expected, `Expected "${selector}"${expected ? '' : ' not'} to exist`); }); }