Skip to content
This repository was archived by the owner on Jul 29, 2024. It is now read-only.

Commit a20c7a7

Browse files
authored
fix(element chaining): make element chaining work when the control flow is disabled (#4029)
Also added some tests to `spec/ts/noCF/smoke_spec.ts` double checking that the control flow is off
1 parent 8d2fc07 commit a20c7a7

File tree

2 files changed

+95
-39
lines changed

2 files changed

+95
-39
lines changed

lib/element.ts

+24-12
Original file line numberDiff line numberDiff line change
@@ -482,18 +482,30 @@ export class ElementArrayFinder extends WebdriverWebElement {
482482
let callerError = new Error();
483483
let actionResults = this.getWebElements()
484484
.then((arr: any) => wdpromise.all(arr.map(actionFn)))
485-
.then(null, (e: IError | string) => {
486-
let noSuchErr: any;
487-
if (e instanceof Error) {
488-
noSuchErr = e;
489-
noSuchErr.stack = noSuchErr.stack + callerError.stack;
490-
} else {
491-
noSuchErr = new Error(e as string);
492-
noSuchErr.stack = callerError.stack;
493-
}
494-
throw noSuchErr;
495-
});
496-
return new ElementArrayFinder(this.browser_, this.getWebElements, this.locator_, actionResults);
485+
.then(
486+
(value: any) => {
487+
return {passed: true, value: value};
488+
},
489+
(error: any) => {
490+
return {passed: false, value: error};
491+
});
492+
let getWebElements = () => actionResults.then(() => this.getWebElements());
493+
actionResults = actionResults.then((result: {passed: boolean, value: any}) => {
494+
if (result.passed) {
495+
return result.value;
496+
} else {
497+
let noSuchErr: any;
498+
if (result.value instanceof Error) {
499+
noSuchErr = result.value;
500+
noSuchErr.stack = noSuchErr.stack + callerError.stack;
501+
} else {
502+
noSuchErr = new Error(result.value as string);
503+
noSuchErr.stack = callerError.stack;
504+
}
505+
throw noSuchErr;
506+
}
507+
});
508+
return new ElementArrayFinder(this.browser_, getWebElements, this.locator_, actionResults);
497509
}
498510

499511
/**

spec/ts/noCF/smoke_spec.ts

+71-27
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
11
// Based off of spec/basic/elements_spec.js
2-
import {promise as ppromise, browser, element, by, By, $, $$, ExpectedConditions, ElementFinder} from '../../..';
2+
import * as q from 'q';
3+
4+
import {$, $$, browser, by, By, element, ElementArrayFinder, ElementFinder, ExpectedConditions, promise as ppromise, WebElement} from '../../..';
5+
6+
describe('verify control flow is off', function() {
7+
it('should have set webdriver.promise.USE_PROMISE_MANAGER', () => {
8+
expect((ppromise as any).USE_PROMISE_MANAGER).toBe(false);
9+
});
10+
11+
it('should not wait on one command before starting another', async function() {
12+
// Wait forever
13+
browser.controlFlow().wait(
14+
(browser.controlFlow() as any).promise((): void => undefined) as ppromise.Promise<void>);
15+
16+
// then return
17+
await browser.controlFlow().execute(() => ppromise.when(null));
18+
});
19+
});
320

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

9-
await expect(nameByElement.getText()).toEqual(
10-
browser.findElement(by.binding('username')).getText());
26+
await expect(nameByElement.getText())
27+
.toEqual(browser.findElement(by.binding('username')).getText());
1128
});
1229

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

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

35-
await ((usernameInput.clear() as any) as ElementFinder).sendKeys('Jane');
52+
await((usernameInput.clear() as any) as ElementFinder).sendKeys('Jane');
3653
await expect(name.getText()).toEqual('Jane');
3754
});
3855

39-
it('chained call should wait to grab the WebElement until a method is called',
40-
async function() {
56+
it('should run chained element actions in sequence', function(done: any) {
57+
// Testing private methods is bad :(
58+
let els = new ElementArrayFinder(browser, () => {
59+
return ppromise.when([null as WebElement]);
60+
});
61+
let applyAction_: (actionFn: (value: WebElement, index: number, array: WebElement[]) => any) =>
62+
ElementArrayFinder = (ElementArrayFinder as any).prototype.applyAction_;
63+
let order: string[] = [];
64+
65+
let deferredA = q.defer<void>();
66+
els = applyAction_.call(els, () => {
67+
return deferredA.promise.then(() => {
68+
order.push('a');
69+
});
70+
});
71+
let deferredB = q.defer<void>();
72+
els = applyAction_.call(els, () => {
73+
return deferredB.promise.then(() => {
74+
order.push('b');
75+
});
76+
});
77+
78+
deferredB.resolve();
79+
setTimeout(async function() {
80+
deferredA.resolve();
81+
await els;
82+
expect(order).toEqual(['a', 'b']);
83+
done();
84+
}, 100);
85+
});
86+
87+
it('chained call should wait to grab the WebElement until a method is called', async function() {
4188
// These should throw no error before a page is loaded.
42-
const reused = element(by.id('baz')).
43-
element(by.binding('item.reusedBinding'));
89+
const reused = element(by.id('baz')).element(by.binding('item.reusedBinding'));
4490

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

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

51-
it('should differentiate elements with the same binding by chaining',
52-
async function() {
53-
await browser.get('index.html#/conflict');
97+
it('should differentiate elements with the same binding by chaining', async function() {
98+
await browser.get('index.html#/conflict');
5499

55-
const outerReused = element(by.binding('item.reusedBinding'));
56-
const innerReused =
57-
element(by.id('baz')).element(by.binding('item.reusedBinding'));
100+
const outerReused = element(by.binding('item.reusedBinding'));
101+
const innerReused = element(by.id('baz')).element(by.binding('item.reusedBinding'));
58102

59-
await expect(outerReused.getText()).toEqual('Outer: outer');
60-
await expect(innerReused.getText()).toEqual('Inner: inner');
61-
});
103+
await expect(outerReused.getText()).toEqual('Outer: outer');
104+
await expect(innerReused.getText()).toEqual('Inner: inner');
105+
});
62106

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

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

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

110154
const invalidElement = element(by.binding('INVALID'));
111-
const successful = invalidElement.getText().then(function() {
112-
return true;
113-
} as any as (() => ppromise.Promise<void>), function() {
114-
return false;
115-
} as any as (() => ppromise.Promise<void>));
155+
const successful = invalidElement.getText().then(
156+
function() {
157+
return true;
158+
} as any as (() => ppromise.Promise<void>),
159+
function() {
160+
return false;
161+
} as any as (() => ppromise.Promise<void>));
116162
await expect(successful).toEqual(false);
117163
});
118164

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

141187
await expect(name.getText()).toEqual('Anon');
142-
await expect(
143-
name.getText().then(null, function() {})
144-
).toEqual('Anon');
188+
await expect(name.getText().then(null, function() {})).toEqual('Anon');
145189

146190
});
147191

0 commit comments

Comments
 (0)