diff --git a/lib/element.ts b/lib/element.ts index c66600eed..f67a27726 100644 --- a/lib/element.ts +++ b/lib/element.ts @@ -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); } /** diff --git a/spec/ts/noCF/smoke_spec.ts b/spec/ts/noCF/smoke_spec.ts index 6707e3592..4477a1ccd 100644 --- a/spec/ts/noCF/smoke_spec.ts +++ b/spec/ts/noCF/smoke_spec.ts @@ -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); + + // 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() { @@ -32,15 +49,44 @@ 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(); + els = applyAction_.call(els, () => { + return deferredA.promise.then(() => { + order.push('a'); + }); + }); + let deferredB = q.defer(); + 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'); @@ -48,22 +94,20 @@ describe('ElementFinder', function() { 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'); @@ -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), function() { - return false; - } as any as (() => ppromise.Promise)); + const successful = invalidElement.getText().then( + function() { + return true; + } as any as (() => ppromise.Promise), + function() { + return false; + } as any as (() => ppromise.Promise)); await expect(successful).toEqual(false); }); @@ -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'); });