From 34f0eebd7e73b10e9b990caa06b63b6fd22b2589 Mon Sep 17 00:00:00 2001 From: Julie Ralph Date: Fri, 13 Mar 2015 17:37:32 -0700 Subject: [PATCH] fix(element): update to selenium-webdriver@2.45.1 and remove element.then This change updates the version of WebDriverJS (selenium-webdriver node module) from 2.44 to 2.45.1. See the full changelog at https://github.com/SeleniumHQ/selenium/blob/master/javascript/node/selenium-webdriver/CHANGES.md To enable the update and remove confusion, this removes the `element().then` function unless there is an action result. This function is completely unnecessary, because it would always resolve to itself, but the removal may cause breaking changes. Before: ```js element(by.css('foo')).then(function(el) { return el.getText().then(...); }); ``` After: ```js element(by.css('foo')).getText().then(...); ``` In other words, an ElementFinder is now no longer a promise until an action has been called. Before: ```js var el = element(by.css('foo')); protractor.promise.isPromise(el); // true protractor.promise.isPromise(el.click()); // true ``` After: ```js var el = element(by.css('foo')); protractor.promise.isPromise(el); // false protractor.promise.isPromise(el.click()); // true ``` Also, fixes `filter` and `map` to work with the new WebDriverJS. --- lib/element.js | 92 ++++++++++++++++--------------------- lib/protractor.js | 2 +- package.json | 2 +- spec/basic/elements_spec.js | 33 ++----------- spec/basic/locators_spec.js | 6 +++ spec/withLoginConf.js | 2 +- 6 files changed, 53 insertions(+), 84 deletions(-) diff --git a/lib/element.js b/lib/element.js index 32e5c78b3..34b1b4f40 100644 --- a/lib/element.js +++ b/lib/element.js @@ -1,4 +1,3 @@ -var util = require('util'); var webdriver = require('selenium-webdriver'); var log = require('./logger.js'); var clientSideScripts = require('./clientsidescripts.js'); @@ -6,7 +5,7 @@ var clientSideScripts = require('./clientsidescripts.js'); var WEB_ELEMENT_FUNCTIONS = [ 'click', 'sendKeys', 'getTagName', 'getCssValue', 'getAttribute', 'getText', 'getSize', 'getLocation', 'isEnabled', 'isSelected', 'submit', 'clear', - 'isDisplayed', 'getOuterHtml', 'getInnerHtml', 'getId']; + 'isDisplayed', 'getOuterHtml', 'getInnerHtml', 'getId', 'getRawId']; /** * ElementArrayFinder is used for operations on an array of elements (as opposed @@ -78,7 +77,6 @@ var ElementArrayFinder = function(ptor, getWebElements, locator, opt_actionResul }; }); }; -util.inherits(ElementArrayFinder, webdriver.promise.Promise); exports.ElementArrayFinder = ElementArrayFinder; @@ -209,18 +207,17 @@ ElementArrayFinder.prototype.filter = function(filterFn) { var elementFinder = ElementFinder.fromWebElement_(self.ptor_, parentWebElement, self.locator_); - var filterResults = filterFn(elementFinder, index); - if (filterResults instanceof webdriver.promise.Promise) { - filterResults.then(function(satisfies) { - if (satisfies) { - list.push(parentWebElements[index]); - } - }); - } else if (filterResults) { - list.push(parentWebElements[index]); - } + list.push(filterFn(elementFinder, index)); + }); + return webdriver.promise.all(list).then(function(resolvedList) { + var filteredElementList = []; + resolvedList.forEach(function(result, index) { + if (result) { + filteredElementList.push(parentWebElements[index]); + } + }); + return filteredElementList; }); - return list; }); }; return new ElementArrayFinder(this.ptor_, getWebElements, this.locator_); @@ -533,11 +530,9 @@ ElementArrayFinder.prototype.map = function(mapFn) { arr.forEach(function(elementFinder, index) { var mapResult = mapFn(elementFinder, index); // All nested arrays and objects will also be fully resolved. - webdriver.promise.fullyResolved(mapResult).then(function(resolved) { - list.push(resolved); - }); + list.push(webdriver.promise.fullyResolved(mapResult)); }); - return list; + return webdriver.promise.all(list); }); }; @@ -637,10 +632,10 @@ ElementArrayFinder.prototype.allowAnimations = function(value) { * * The ElementFinder can be treated as a WebElement for most purposes, in * particular, you may perform actions (i.e. click, getText) on them as you - * would a WebElement. ElementFinders extend Promise, and once an action - * is performed on an ElementFinder, the latest result from the chain can be - * accessed using then. Unlike a WebElement, an ElementFinder will wait for - * angular to settle before performing finds or actions. + * would a WebElement. Once an action is performed on an ElementFinder, the + * latest result from the chain can be accessed using the then method. + * Unlike a WebElement, an ElementFinder will wait for angular to settle before + * performing finds or actions. * * ElementFinder can be used to build a chain of locators that is used to find * an element. An ElementFinder does not actually attempt to find the element @@ -680,6 +675,28 @@ var ElementFinder = function(ptor, elementArrayFinder) { this.ptor_ = ptor; this.parentElementArrayFinder = elementArrayFinder; + // Only have a `then` method if the parent element array finder + // has action results. + if (this.parentElementArrayFinder.actionResults_) { + /** + * Access the underlying actionResult of ElementFinder. + * + * @param {function(webdriver.promise.Promise)} fn Function which takes + * the value of the underlying actionResult. + * + * @return {webdriver.promise.Promise} Promise which contains the results of + * evaluating fn. + */ + this.then = function(fn, errorFn) { + return this.elementArrayFinder_.then(function(actionResults) { + if (!fn) { + return actionResults[0]; + } + return fn(actionResults[0]); + }, errorFn); + }; + } + // This filter verifies that there is only 1 element returned by the // elementArrayFinder. It will warn if there are more than 1 element and // throw an error if there are no elements. @@ -717,7 +734,6 @@ var ElementFinder = function(ptor, elementArrayFinder) { }; }); }; -util.inherits(ElementFinder, webdriver.promise.Promise); exports.ElementFinder = ElementFinder; @@ -770,25 +786,6 @@ ElementFinder.prototype.getWebElement = function() { return new webdriver.WebElementPromise(this.ptor_.driver, id); }; -/** - * Access the underlying actionResult of ElementFinder. Implementation allows - * ElementFinder to be used as a webdriver.promise.Promise - * - * @param {function(webdriver.promise.Promise)} fn Function which takes - * the value of the underlying actionResult. - * - * @return {webdriver.promise.Promise} Promise which contains the results of - * evaluating fn. - */ -ElementFinder.prototype.then = function(fn, errorFn) { - return this.elementArrayFinder_.then(function(actionResults) { - if (!fn) { - return actionResults[0]; - } - return fn(actionResults[0]); - }, errorFn); -}; - /** * Calls to {@code all} may be chained to find an array of elements within a * parent. @@ -957,17 +954,6 @@ ElementFinder.prototype.allowAnimations = function(value) { return this.elementArrayFinder_.allowAnimations(value).toElementFinder_(); }; -/** - * Webdriver relies on this function to be present on Promises, so adding - * this dummy function as we inherited from webdriver.promise.Promise, but - * this function is irrelevant to our usage - * - * @return {boolean} Always false as ElementFinder is never in pending state. - */ -ElementFinder.prototype.isPending = function() { - return false; -}; - /** * Shortcut for querying the document directly with css. * diff --git a/lib/protractor.js b/lib/protractor.js index 43d0b7e0d..6ddd45d96 100644 --- a/lib/protractor.js +++ b/lib/protractor.js @@ -329,7 +329,7 @@ Protractor.prototype.findElements = function(locator) { * the element is present on the page. */ Protractor.prototype.isElementPresent = function(locatorOrElement) { - var element = (locatorOrElement instanceof webdriver.promise.Promise) ? + var element = (locatorOrElement.isPresent) ? locatorOrElement : this.element(locatorOrElement); return element.isPresent(); }; diff --git a/package.json b/package.json index 1c8786ea7..c2fcfe918 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "author": "Julie Ralph ", "dependencies": { "request": "~2.36.0", - "selenium-webdriver": "2.44.0", + "selenium-webdriver": "2.45.1", "minijasminenode": "1.1.1", "jasminewd": "1.1.0", "jasminewd2": "0.0.2", diff --git a/spec/basic/elements_spec.js b/spec/basic/elements_spec.js index 093d4dc8e..b5cce45fe 100644 --- a/spec/basic/elements_spec.js +++ b/spec/basic/elements_spec.js @@ -157,28 +157,10 @@ describe('ElementFinder', function() { expect(successful).toEqual(false); }); - it('then function should be equivalent to itself', function() { - browser.get('index.html#/form'); - var elem = element(by.binding('greeting')); - - elem.then(function(elem2) { - expect(elem.getId()).toEqual(elem2.getId()); - }); - }); - - it('should not resolve to itself', function() { - browser.get('index.html#/form'); - var elem1 = element(by.binding('greeting')); - - elem1.then(function(result) { - expect(result === elem1).toBe(false); - }); - }); - it('should be returned from a helper without infinite loops', function() { browser.get('index.html#/form'); - var helperPromise = element(by.binding('greeting')).then(function(result) { - return result; + var helperPromise = protractor.promise.when(true).then(function() { + return element(by.binding('greeting')); }); helperPromise.then(function(finalResult) { @@ -186,7 +168,7 @@ describe('ElementFinder', function() { }); }); - it('should be usable in WebDriver functions', function() { + it('should be usable in WebDriver functions via getWebElement', function() { // TODO(juliemr): should be able to do this without the getWebElement call browser.get('index.html#/form'); var greeting = element(by.binding('greeting')); @@ -509,16 +491,11 @@ describe('ElementArrayFinder', function() { '4/5: small cat\n'); }); - it('should always return a promise when calling then', function() { - browser.get('index.html#/form'); - var e1 = element(by.tagName('body')).then(function() {}); - expect(e1 instanceof protractor.promise.Promise).toBe(true); - }); - it('should allow using protractor locator within map', function() { browser.get('index.html#/repeater'); - var expected = [{ first: 'M', second: 'Monday' }, + var expected = [ + { first: 'M', second: 'Monday' }, { first: 'T', second: 'Tuesday' }, { first: 'W', second: 'Wednesday' }, { first: 'Th', second: 'Thursday' }, diff --git a/spec/basic/locators_spec.js b/spec/basic/locators_spec.js index 773d98ba3..100adb5e6 100644 --- a/spec/basic/locators_spec.js +++ b/spec/basic/locators_spec.js @@ -388,4 +388,10 @@ describe('locators', function() { expect(browser.isElementPresent(by.binding('greet'))).toBe(true); expect(browser.isElementPresent(by.binding('nopenopenope'))).toBe(false); }); + + it('should determine if an ElementFinder is present', function() { + expect(browser.isElementPresent(element(by.binding('greet')))).toBe(true); + expect(browser.isElementPresent(element(by.binding('nopenopenope')))) + .toBe(false); + }) }); diff --git a/spec/withLoginConf.js b/spec/withLoginConf.js index 1607afc82..9dcd9c170 100644 --- a/spec/withLoginConf.js +++ b/spec/withLoginConf.js @@ -28,7 +28,7 @@ exports.config = { browser.driver.wait(function() { return browser.driver.getCurrentUrl().then(function(url) { return /index/.test(url); - }); + }, 10000); }); } };