Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(element chaining): make element chaining work when the control fl… #4029

Merged
merged 1 commit into from
Jan 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 24 additions & 12 deletions lib/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,18 +482,30 @@ export class ElementArrayFinder extends WebdriverWebElement {
let callerError = new Error();
let actionResults = this.getWebElements()
.then((arr: any) => wdpromise.all(arr.map(actionFn)))
.then(null, (e: IError | string) => {
let noSuchErr: any;
if (e instanceof Error) {
noSuchErr = e;
noSuchErr.stack = noSuchErr.stack + callerError.stack;
} else {
noSuchErr = new Error(e as string);
noSuchErr.stack = callerError.stack;
}
throw noSuchErr;
});
return new ElementArrayFinder(this.browser_, this.getWebElements, this.locator_, actionResults);
.then(
(value: any) => {
return {passed: true, value: value};
},
(error: any) => {
return {passed: false, value: error};
});
let getWebElements = () => actionResults.then(() => this.getWebElements());
actionResults = actionResults.then((result: {passed: boolean, value: any}) => {
if (result.passed) {
return result.value;
} else {
let noSuchErr: any;
if (result.value instanceof Error) {
noSuchErr = result.value;
noSuchErr.stack = noSuchErr.stack + callerError.stack;
} else {
noSuchErr = new Error(result.value as string);
noSuchErr.stack = callerError.stack;
}
throw noSuchErr;
}
});
return new ElementArrayFinder(this.browser_, getWebElements, this.locator_, actionResults);
}

/**
Expand Down
98 changes: 71 additions & 27 deletions spec/ts/noCF/smoke_spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
// Based off of spec/basic/elements_spec.js
import {promise as ppromise, browser, element, by, By, $, $$, ExpectedConditions, ElementFinder} from '../../..';
import * as q from 'q';

import {$, $$, browser, by, By, element, ElementArrayFinder, ElementFinder, ExpectedConditions, promise as ppromise, WebElement} from '../../..';

describe('verify control flow is off', function() {
it('should have set webdriver.promise.USE_PROMISE_MANAGER', () => {
expect((ppromise as any).USE_PROMISE_MANAGER).toBe(false);
});

it('should not wait on one command before starting another', async function() {
// Wait forever
browser.controlFlow().wait(
(browser.controlFlow() as any).promise((): void => undefined) as ppromise.Promise<void>);

// then return
await browser.controlFlow().execute(() => ppromise.when(null));
});
});

describe('ElementFinder', function() {
it('should return the same result as browser.findElement', async function() {
await browser.get('index.html#/form');
const nameByElement = element(by.binding('username'));

await expect(nameByElement.getText()).toEqual(
browser.findElement(by.binding('username')).getText());
await expect(nameByElement.getText())
.toEqual(browser.findElement(by.binding('username')).getText());
});

it('should wait to grab the WebElement until a method is called', async function() {
Expand All @@ -32,38 +49,65 @@ describe('ElementFinder', function() {

await expect(name.getText()).toEqual('Anon');

await ((usernameInput.clear() as any) as ElementFinder).sendKeys('Jane');
await((usernameInput.clear() as any) as ElementFinder).sendKeys('Jane');
await expect(name.getText()).toEqual('Jane');
});

it('chained call should wait to grab the WebElement until a method is called',
async function() {
it('should run chained element actions in sequence', function(done: any) {
// Testing private methods is bad :(
let els = new ElementArrayFinder(browser, () => {
return ppromise.when([null as WebElement]);
});
let applyAction_: (actionFn: (value: WebElement, index: number, array: WebElement[]) => any) =>
ElementArrayFinder = (ElementArrayFinder as any).prototype.applyAction_;
let order: string[] = [];

let deferredA = q.defer<void>();
els = applyAction_.call(els, () => {
return deferredA.promise.then(() => {
order.push('a');
});
});
let deferredB = q.defer<void>();
els = applyAction_.call(els, () => {
return deferredB.promise.then(() => {
order.push('b');
});
});

deferredB.resolve();
setTimeout(async function() {
deferredA.resolve();
await els;
expect(order).toEqual(['a', 'b']);
done();
}, 100);
});

it('chained call should wait to grab the WebElement until a method is called', async function() {
// These should throw no error before a page is loaded.
const reused = element(by.id('baz')).
element(by.binding('item.reusedBinding'));
const reused = element(by.id('baz')).element(by.binding('item.reusedBinding'));

await browser.get('index.html#/conflict');

await expect(reused.getText()).toEqual('Inner: inner');
await expect(reused.isPresent()).toBe(true);
});

it('should differentiate elements with the same binding by chaining',
async function() {
await browser.get('index.html#/conflict');
it('should differentiate elements with the same binding by chaining', async function() {
await browser.get('index.html#/conflict');

const outerReused = element(by.binding('item.reusedBinding'));
const innerReused =
element(by.id('baz')).element(by.binding('item.reusedBinding'));
const outerReused = element(by.binding('item.reusedBinding'));
const innerReused = element(by.id('baz')).element(by.binding('item.reusedBinding'));

await expect(outerReused.getText()).toEqual('Outer: outer');
await expect(innerReused.getText()).toEqual('Inner: inner');
});
await expect(outerReused.getText()).toEqual('Outer: outer');
await expect(innerReused.getText()).toEqual('Inner: inner');
});

it('should chain deeper than 2', async function() {
// These should throw no error before a page is loaded.
const reused = element(by.css('body')).element(by.id('baz')).
element(by.binding('item.reusedBinding'));
const reused =
element(by.css('body')).element(by.id('baz')).element(by.binding('item.reusedBinding'));

await browser.get('index.html#/conflict');

Expand Down Expand Up @@ -108,11 +152,13 @@ describe('ElementFinder', function() {
await browser.get('index.html#/form');

const invalidElement = element(by.binding('INVALID'));
const successful = invalidElement.getText().then(function() {
return true;
} as any as (() => ppromise.Promise<void>), function() {
return false;
} as any as (() => ppromise.Promise<void>));
const successful = invalidElement.getText().then(
function() {
return true;
} as any as (() => ppromise.Promise<void>),
function() {
return false;
} as any as (() => ppromise.Promise<void>));
await expect(successful).toEqual(false);
});

Expand All @@ -139,9 +185,7 @@ describe('ElementFinder', function() {
const name = element(by.binding('username'));

await expect(name.getText()).toEqual('Anon');
await expect(
name.getText().then(null, function() {})
).toEqual('Anon');
await expect(name.getText().then(null, function() {})).toEqual('Anon');

});

Expand Down