From 5b4036743347b7bc9a9b6289e3d435684fb13ad2 Mon Sep 17 00:00:00 2001 From: Julie Date: Wed, 28 May 2014 15:52:09 -0700 Subject: [PATCH 001/612] chore(travis/ci): add more logging about sauce connect to ci --- .travis.yml | 3 +++ scripts/print_logs.sh | 11 +++++++++++ scripts/sauce_connect_setup.sh | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 scripts/print_logs.sh diff --git a/.travis.yml b/.travis.yml index e8aab56fe..6b881f84c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,3 +20,6 @@ before_script: script: - ./scripts/testserver.sh - ./scripts/testonsauce.sh + +after_script: + - ./scripts/print_logs.sh diff --git a/scripts/print_logs.sh b/scripts/print_logs.sh new file mode 100644 index 000000000..5b882b818 --- /dev/null +++ b/scripts/print_logs.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +LOG_FILES=$LOGS_DIR/* + +for FILE in $LOG_FILES; do + echo -e "\n\n\n" + echo "==================================================================" + echo " $FILE" + echo "==================================================================" + cat $FILE +done diff --git a/scripts/sauce_connect_setup.sh b/scripts/sauce_connect_setup.sh index f7bd2f3f4..f7360be1d 100755 --- a/scripts/sauce_connect_setup.sh +++ b/scripts/sauce_connect_setup.sh @@ -46,5 +46,5 @@ echo "Starting Sauce Connect in the background, logging into:" echo " $CONNECT_LOG" echo " $CONNECT_STDOUT" echo " $CONNECT_STDERR" -sauce-connect/bin/sc -u $SAUCE_USERNAME -k $SAUCE_ACCESS_KEY $ARGS \ +sauce-connect/bin/sc -u $SAUCE_USERNAME -k $SAUCE_ACCESS_KEY -v $ARGS \ --logfile $CONNECT_LOG 2> $CONNECT_STDERR 1> $CONNECT_STDOUT & From acc3db7fbd7ba5ab87b1061904fef48218501331 Mon Sep 17 00:00:00 2001 From: Julie Date: Wed, 28 May 2014 16:07:06 -0700 Subject: [PATCH 002/612] chore(travis/ci): fix permissions for printing logs script --- scripts/print_logs.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/print_logs.sh diff --git a/scripts/print_logs.sh b/scripts/print_logs.sh old mode 100644 new mode 100755 From 6c8ee30648ffe4c6a9b5c5cb93a922e8fa4ea659 Mon Sep 17 00:00:00 2001 From: Michael Salmon Date: Tue, 27 May 2014 13:33:43 -0700 Subject: [PATCH 003/612] docs(getting-started): relative path update Updated document to correctly state that spec paths are relative to the protractor config instead of the spec file. --- docs/getting-started.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index a1b9a3ca1..29dc718f7 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -65,8 +65,9 @@ exports.config = { 'browserName': 'chrome' }, - // Spec patterns are relative to the location of the spec file. They may - // include glob patterns. + // Spec patterns are relative to the configuration file location passed + // to proractor (in this example conf.js). + // They may include glob patterns. specs: ['example-spec.js'], // Options to be passed to Jasmine-node. From 6a0e5dd7032d14346507b522b0f02b623d1096e8 Mon Sep 17 00:00:00 2001 From: Eric Heikes Date: Fri, 9 May 2014 16:48:48 -0500 Subject: [PATCH 004/612] docs(api): Specify the anchors for TOC to link to. --- docs/dgeni-config.js | 7 +++++++ docs/filters/link-slugify.js | 8 ++++++++ docs/processors/add-toc.js | 10 +--------- docs/templates/api-template.md | 2 +- docs/templates/toc-template.md | 2 +- 5 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 docs/filters/link-slugify.js diff --git a/docs/dgeni-config.js b/docs/dgeni-config.js index e3a93e017..d3bc632f7 100644 --- a/docs/dgeni-config.js +++ b/docs/dgeni-config.js @@ -31,6 +31,13 @@ module.exports = function(config) { require('./processors/add-toc') ]); + /* + * Add custom filters. + */ + config.append('rendering.filters', [ + require('./filters/link-slugify') + ]); + // Configure the tags that will be parsed from the jsDoc. var tagDefs = require('dgeni-packages/jsdoc/tag-defs'); diff --git a/docs/filters/link-slugify.js b/docs/filters/link-slugify.js new file mode 100644 index 000000000..54abfb600 --- /dev/null +++ b/docs/filters/link-slugify.js @@ -0,0 +1,8 @@ +// Transform links & anchors like 'elementFinder.isPresent' into +// 'api-elementfinder-ispresent'. +module.exports = { + name: 'slugify', + process: function(str) { + return 'api-' + str.replace(/\./g, '-').toLocaleLowerCase(); + } +}; diff --git a/docs/processors/add-toc.js b/docs/processors/add-toc.js index 63e605b41..25dae4be8 100644 --- a/docs/processors/add-toc.js +++ b/docs/processors/add-toc.js @@ -19,18 +19,10 @@ module.exports = { isHeader: true }); - // Add the link to jump to the jsdoc definition in api.md. docs.forEach(function(doc) { - // Remove the dots. var name = doc.name || ''; - - // The link looks like: 'elementFinder.isPresent', transform it into - // 'elementfinderispresent'. - var linkName = name.replace(/[\.\$]/g, '').toLocaleLowerCase(); - toc.push({ - name: name, - link: linkName + name: name }); }); }); diff --git a/docs/templates/api-template.md b/docs/templates/api-template.md index c5d6a5742..06cdebb0a 100644 --- a/docs/templates/api-template.md +++ b/docs/templates/api-template.md @@ -1,4 +1,4 @@ -##[{{ doc.name }}]({{ doc.sourceLink }}) +##[{{ doc.name }}]({{ doc.sourceLink }}) {%if doc.alias %}#### Use as: {{ doc.alias }}{%endif%} {{ doc.description }}{% if doc.deprecated %}**DEPRECATED** {{doc.deprecated}}{% endif %} diff --git a/docs/templates/toc-template.md b/docs/templates/toc-template.md index fac3dc642..fbb869e53 100644 --- a/docs/templates/toc-template.md +++ b/docs/templates/toc-template.md @@ -4,4 +4,4 @@ Protractor API {{doc.version}} {% for line in doc.toc %} {% if line.isHeader %} ##{{ line.name }} -{% else %}* [{{ line.name }}](#{{ line.link }}){% endif %}{% endfor %} +{% else %}* [{{ line.name }}](#{{ line.name | slugify }}){% endif %}{% endfor %} From 06dce6f69a6e3987ec09788bca44f794703eff8c Mon Sep 17 00:00:00 2001 From: Julie Date: Thu, 29 May 2014 16:58:04 -0700 Subject: [PATCH 005/612] docs(api): update for version 0.23.1 Should have gotten added at the last release. --- docs/api.md | 688 ++++++++++++++++++++++++++-------------------------- 1 file changed, 347 insertions(+), 341 deletions(-) diff --git a/docs/api.md b/docs/api.md index d330e1b34..cf8e4871d 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,158 +1,158 @@ -Protractor API 0.22.0 +Protractor API 0.23.1 ============== ##protractor -* [element](#element) -* [elementFinder.find](#elementfinderfind) -* [elementFinder.isPresent](#elementfinderispresent) -* [elementFinder.locator](#elementfinderlocator) -* [elementFinder.element](#elementfinderelement) -* [elementFinder.$](#elementfinder) -* [element.all](#elementall) -* [elementArrayFinder.count](#elementarrayfindercount) -* [elementArrayFinder.get](#elementarrayfinderget) -* [elementArrayFinder.first](#elementarrayfinderfirst) -* [elementArrayFinder.last](#elementarrayfinderlast) -* [elementArrayFinder.each](#elementarrayfindereach) -* [elementArrayFinder.map](#elementarrayfindermap) -* [Protractor](#protractor) -* [Protractor.prototype.waitForAngular](#protractorprototypewaitforangular) -* [Protractor.prototype.wrapWebElement](#protractorprototypewrapwebelement) -* [element.$](#element) -* [element.findElement](#elementfindelement) -* [element.$$](#element) -* [element.findElements](#elementfindelements) -* [element.isElementPresent](#elementiselementpresent) -* [element.evaluate](#elementevaluate) -* [Protractor.prototype.findElement](#protractorprototypefindelement) -* [Protractor.prototype.findElements](#protractorprototypefindelements) -* [Protractor.prototype.isElementPresent](#protractorprototypeiselementpresent) -* [Protractor.prototype.addMockModule](#protractorprototypeaddmockmodule) -* [Protractor.prototype.clearMockModules](#protractorprototypeclearmockmodules) -* [Protractor.prototype.removeMockModule](#protractorprototyperemovemockmodule) -* [Protractor.prototype.get](#protractorprototypeget) -* [Protractor.prototype.refresh](#protractorprototyperefresh) -* [Protractor.prototype.navigate](#protractorprototypenavigate) -* [Protractor.prototype.setLocation](#protractorprototypesetlocation) -* [Protractor.prototype.getLocationAbsUrl](#protractorprototypegetlocationabsurl) -* [Protractor.prototype.debugger](#protractorprototypedebugger) -* [Protractor.prototype.pause](#protractorprototypepause) +* [element](#api-element) +* [elementFinder.find](#api-elementfinder-find) +* [elementFinder.isPresent](#api-elementfinder-ispresent) +* [elementFinder.locator](#api-elementfinder-locator) +* [elementFinder.element](#api-elementfinder-element) +* [elementFinder.$](#api-elementfinder-$) +* [element.all](#api-element-all) +* [elementArrayFinder.count](#api-elementarrayfinder-count) +* [elementArrayFinder.get](#api-elementarrayfinder-get) +* [elementArrayFinder.first](#api-elementarrayfinder-first) +* [elementArrayFinder.last](#api-elementarrayfinder-last) +* [elementArrayFinder.each](#api-elementarrayfinder-each) +* [elementArrayFinder.map](#api-elementarrayfinder-map) +* [Protractor](#api-protractor) +* [Protractor.prototype.waitForAngular](#api-protractor-prototype-waitforangular) +* [Protractor.prototype.wrapWebElement](#api-protractor-prototype-wrapwebelement) +* [element.$](#api-element-$) +* [element.findElement](#api-element-findelement) +* [element.$$](#api-element-$$) +* [element.findElements](#api-element-findelements) +* [element.isElementPresent](#api-element-iselementpresent) +* [element.evaluate](#api-element-evaluate) +* [Protractor.prototype.findElement](#api-protractor-prototype-findelement) +* [Protractor.prototype.findElements](#api-protractor-prototype-findelements) +* [Protractor.prototype.isElementPresent](#api-protractor-prototype-iselementpresent) +* [Protractor.prototype.addMockModule](#api-protractor-prototype-addmockmodule) +* [Protractor.prototype.clearMockModules](#api-protractor-prototype-clearmockmodules) +* [Protractor.prototype.removeMockModule](#api-protractor-prototype-removemockmodule) +* [Protractor.prototype.get](#api-protractor-prototype-get) +* [Protractor.prototype.refresh](#api-protractor-prototype-refresh) +* [Protractor.prototype.navigate](#api-protractor-prototype-navigate) +* [Protractor.prototype.setLocation](#api-protractor-prototype-setlocation) +* [Protractor.prototype.getLocationAbsUrl](#api-protractor-prototype-getlocationabsurl) +* [Protractor.prototype.debugger](#api-protractor-prototype-debugger) +* [Protractor.prototype.pause](#api-protractor-prototype-pause) ##locators -* [ProtractorBy](#protractorby) -* [WebdriverBy.prototype](#webdriverbyprototype) -* [ProtractorBy.prototype.addLocator](#protractorbyprototypeaddlocator) -* [ProtractorBy.prototype.binding](#protractorbyprototypebinding) -* [ProtractorBy.prototype.exactBinding](#protractorbyprototypeexactbinding) -* [ProtractorBy.prototype.select](#protractorbyprototypeselect) -* [ProtractorBy.prototype.selectedOption](#protractorbyprototypeselectedoption) -* [ProtractorBy.prototype.input](#protractorbyprototypeinput) -* [ProtractorBy.prototype.model](#protractorbyprototypemodel) -* [ProtractorBy.prototype.buttonText](#protractorbyprototypebuttontext) -* [ProtractorBy.prototype.partialButtonText](#protractorbyprototypepartialbuttontext) -* [ProtractorBy.prototype.textarea](#protractorbyprototypetextarea) -* [ProtractorBy.prototype.repeater](#protractorbyprototyperepeater) -* [ProtractorBy.prototype.cssContainingText](#protractorbyprototypecsscontainingtext) +* [ProtractorBy](#api-protractorby) +* [WebdriverBy.prototype](#api-webdriverby-prototype) +* [ProtractorBy.prototype.addLocator](#api-protractorby-prototype-addlocator) +* [ProtractorBy.prototype.binding](#api-protractorby-prototype-binding) +* [ProtractorBy.prototype.exactBinding](#api-protractorby-prototype-exactbinding) +* [ProtractorBy.prototype.select](#api-protractorby-prototype-select) +* [ProtractorBy.prototype.selectedOption](#api-protractorby-prototype-selectedoption) +* [ProtractorBy.prototype.input](#api-protractorby-prototype-input) +* [ProtractorBy.prototype.model](#api-protractorby-prototype-model) +* [ProtractorBy.prototype.buttonText](#api-protractorby-prototype-buttontext) +* [ProtractorBy.prototype.partialButtonText](#api-protractorby-prototype-partialbuttontext) +* [ProtractorBy.prototype.textarea](#api-protractorby-prototype-textarea) +* [ProtractorBy.prototype.repeater](#api-protractorby-prototype-repeater) +* [ProtractorBy.prototype.cssContainingText](#api-protractorby-prototype-csscontainingtext) ##webdriver -* [webdriver.WebDriver](#webdriverwebdriver) -* [webdriver.WebDriver.attachToSession](#webdriverwebdriverattachtosession) -* [webdriver.WebDriver.createSession](#webdriverwebdrivercreatesession) -* [webdriver.WebDriver.prototype.controlFlow](#webdriverwebdriverprototypecontrolflow) -* [webdriver.WebDriver.prototype.schedule](#webdriverwebdriverprototypeschedule) -* [webdriver.WebDriver.prototype.getSession](#webdriverwebdriverprototypegetsession) -* [webdriver.WebDriver.prototype.getCapabilities](#webdriverwebdriverprototypegetcapabilities) -* [webdriver.WebDriver.prototype.quit](#webdriverwebdriverprototypequit) -* [webdriver.WebDriver.prototype.actions](#webdriverwebdriverprototypeactions) -* [webdriver.WebDriver.prototype.executeScript](#webdriverwebdriverprototypeexecutescript) -* [webdriver.WebDriver.prototype.executeAsyncScript](#webdriverwebdriverprototypeexecuteasyncscript) -* [webdriver.WebDriver.prototype.call](#webdriverwebdriverprototypecall) -* [webdriver.WebDriver.prototype.wait](#webdriverwebdriverprototypewait) -* [webdriver.WebDriver.prototype.sleep](#webdriverwebdriverprototypesleep) -* [webdriver.WebDriver.prototype.getWindowHandle](#webdriverwebdriverprototypegetwindowhandle) -* [webdriver.WebDriver.prototype.getAllWindowHandles](#webdriverwebdriverprototypegetallwindowhandles) -* [webdriver.WebDriver.prototype.getPageSource](#webdriverwebdriverprototypegetpagesource) -* [webdriver.WebDriver.prototype.close](#webdriverwebdriverprototypeclose) -* [webdriver.WebDriver.prototype.get](#webdriverwebdriverprototypeget) -* [webdriver.WebDriver.prototype.getCurrentUrl](#webdriverwebdriverprototypegetcurrenturl) -* [webdriver.WebDriver.prototype.getTitle](#webdriverwebdriverprototypegettitle) -* [webdriver.WebDriver.prototype.findElement](#webdriverwebdriverprototypefindelement) -* [webdriver.WebDriver.prototype.isElementPresent](#webdriverwebdriverprototypeiselementpresent) -* [webdriver.WebDriver.prototype.findElements](#webdriverwebdriverprototypefindelements) -* [webdriver.WebDriver.prototype.takeScreenshot](#webdriverwebdriverprototypetakescreenshot) -* [webdriver.WebDriver.prototype.manage](#webdriverwebdriverprototypemanage) -* [webdriver.WebDriver.prototype.navigate](#webdriverwebdriverprototypenavigate) -* [webdriver.WebDriver.prototype.switchTo](#webdriverwebdriverprototypeswitchto) -* [webdriver.WebDriver.Navigation](#webdriverwebdrivernavigation) -* [webdriver.WebDriver.Navigation.prototype.to](#webdriverwebdrivernavigationprototypeto) -* [webdriver.WebDriver.Navigation.prototype.back](#webdriverwebdrivernavigationprototypeback) -* [webdriver.WebDriver.Navigation.prototype.forward](#webdriverwebdrivernavigationprototypeforward) -* [webdriver.WebDriver.Navigation.prototype.refresh](#webdriverwebdrivernavigationprototyperefresh) -* [webdriver.WebDriver.Options](#webdriverwebdriveroptions) -* [webdriver.WebDriver.Options.prototype.addCookie](#webdriverwebdriveroptionsprototypeaddcookie) -* [webdriver.WebDriver.Options.prototype.deleteAllCookies](#webdriverwebdriveroptionsprototypedeleteallcookies) -* [webdriver.WebDriver.Options.prototype.deleteCookie](#webdriverwebdriveroptionsprototypedeletecookie) -* [webdriver.WebDriver.Options.prototype.getCookies](#webdriverwebdriveroptionsprototypegetcookies) -* [webdriver.WebDriver.Options.prototype.getCookie](#webdriverwebdriveroptionsprototypegetcookie) -* [webdriver.WebDriver.Options.prototype.logs](#webdriverwebdriveroptionsprototypelogs) -* [webdriver.WebDriver.Options.prototype.timeouts](#webdriverwebdriveroptionsprototypetimeouts) -* [webdriver.WebDriver.Options.prototype.window](#webdriverwebdriveroptionsprototypewindow) -* [webdriver.WebDriver.Timeouts](#webdriverwebdrivertimeouts) -* [webdriver.WebDriver.Timeouts.prototype.implicitlyWait](#webdriverwebdrivertimeoutsprototypeimplicitlywait) -* [webdriver.WebDriver.Timeouts.prototype.setScriptTimeout](#webdriverwebdrivertimeoutsprototypesetscripttimeout) -* [webdriver.WebDriver.Timeouts.prototype.pageLoadTimeout](#webdriverwebdrivertimeoutsprototypepageloadtimeout) -* [webdriver.WebDriver.Window](#webdriverwebdriverwindow) -* [webdriver.WebDriver.Window.prototype.getPosition](#webdriverwebdriverwindowprototypegetposition) -* [webdriver.WebDriver.Window.prototype.setPosition](#webdriverwebdriverwindowprototypesetposition) -* [webdriver.WebDriver.Window.prototype.getSize](#webdriverwebdriverwindowprototypegetsize) -* [webdriver.WebDriver.Window.prototype.setSize](#webdriverwebdriverwindowprototypesetsize) -* [webdriver.WebDriver.Window.prototype.maximize](#webdriverwebdriverwindowprototypemaximize) -* [webdriver.WebDriver.Logs](#webdriverwebdriverlogs) -* [webdriver.WebDriver.Logs.prototype.get](#webdriverwebdriverlogsprototypeget) -* [webdriver.WebDriver.Logs.prototype.getAvailableLogTypes](#webdriverwebdriverlogsprototypegetavailablelogtypes) -* [webdriver.WebDriver.TargetLocator](#webdriverwebdrivertargetlocator) -* [webdriver.WebDriver.TargetLocator.prototype.activeElement](#webdriverwebdrivertargetlocatorprototypeactiveelement) -* [webdriver.WebDriver.TargetLocator.prototype.defaultContent](#webdriverwebdrivertargetlocatorprototypedefaultcontent) -* [webdriver.WebDriver.TargetLocator.prototype.frame](#webdriverwebdrivertargetlocatorprototypeframe) -* [webdriver.WebDriver.TargetLocator.prototype.window](#webdriverwebdrivertargetlocatorprototypewindow) -* [webdriver.WebDriver.TargetLocator.prototype.alert](#webdriverwebdrivertargetlocatorprototypealert) -* [webdriver.Key.chord](#webdriverkeychord) -* [webdriver.WebElement](#webdriverwebelement) -* [webdriver.WebElement.equals](#webdriverwebelementequals) -* [webdriver.WebElement.prototype.getDriver](#webdriverwebelementprototypegetdriver) -* [webdriver.WebElement.prototype.toWireValue](#webdriverwebelementprototypetowirevalue) -* [webdriver.WebElement.prototype.findElement](#webdriverwebelementprototypefindelement) -* [webdriver.WebElement.prototype.isElementPresent](#webdriverwebelementprototypeiselementpresent) -* [webdriver.WebElement.prototype.findElements](#webdriverwebelementprototypefindelements) -* [webdriver.WebElement.prototype.click](#webdriverwebelementprototypeclick) -* [webdriver.WebElement.prototype.sendKeys](#webdriverwebelementprototypesendkeys) -* [webdriver.WebElement.prototype.getTagName](#webdriverwebelementprototypegettagname) -* [webdriver.WebElement.prototype.getCssValue](#webdriverwebelementprototypegetcssvalue) -* [webdriver.WebElement.prototype.getAttribute](#webdriverwebelementprototypegetattribute) -* [webdriver.WebElement.prototype.getText](#webdriverwebelementprototypegettext) -* [webdriver.WebElement.prototype.getSize](#webdriverwebelementprototypegetsize) -* [webdriver.WebElement.prototype.getLocation](#webdriverwebelementprototypegetlocation) -* [webdriver.WebElement.prototype.isEnabled](#webdriverwebelementprototypeisenabled) -* [webdriver.WebElement.prototype.isSelected](#webdriverwebelementprototypeisselected) -* [webdriver.WebElement.prototype.submit](#webdriverwebelementprototypesubmit) -* [webdriver.WebElement.prototype.clear](#webdriverwebelementprototypeclear) -* [webdriver.WebElement.prototype.isDisplayed](#webdriverwebelementprototypeisdisplayed) -* [webdriver.WebElement.prototype.getOuterHtml](#webdriverwebelementprototypegetouterhtml) -* [webdriver.WebElement.prototype.getInnerHtml](#webdriverwebelementprototypegetinnerhtml) -* [webdriver.Alert](#webdriveralert) -* [webdriver.Alert.prototype.getText](#webdriveralertprototypegettext) -* [webdriver.Alert.prototype.accept](#webdriveralertprototypeaccept) -* [webdriver.Alert.prototype.dismiss](#webdriveralertprototypedismiss) -* [webdriver.Alert.prototype.sendKeys](#webdriveralertprototypesendkeys) -* [webdriver.UnhandledAlertError](#webdriverunhandledalerterror) -* [webdriver.UnhandledAlertError.prototype.getAlert](#webdriverunhandledalerterrorprototypegetalert) - -##[element](https://github.com/angular/protractor/blob/master/lib/protractor.js#L74) +* [webdriver.WebDriver](#api-webdriver-webdriver) +* [webdriver.WebDriver.attachToSession](#api-webdriver-webdriver-attachtosession) +* [webdriver.WebDriver.createSession](#api-webdriver-webdriver-createsession) +* [webdriver.WebDriver.prototype.controlFlow](#api-webdriver-webdriver-prototype-controlflow) +* [webdriver.WebDriver.prototype.schedule](#api-webdriver-webdriver-prototype-schedule) +* [webdriver.WebDriver.prototype.getSession](#api-webdriver-webdriver-prototype-getsession) +* [webdriver.WebDriver.prototype.getCapabilities](#api-webdriver-webdriver-prototype-getcapabilities) +* [webdriver.WebDriver.prototype.quit](#api-webdriver-webdriver-prototype-quit) +* [webdriver.WebDriver.prototype.actions](#api-webdriver-webdriver-prototype-actions) +* [webdriver.WebDriver.prototype.executeScript](#api-webdriver-webdriver-prototype-executescript) +* [webdriver.WebDriver.prototype.executeAsyncScript](#api-webdriver-webdriver-prototype-executeasyncscript) +* [webdriver.WebDriver.prototype.call](#api-webdriver-webdriver-prototype-call) +* [webdriver.WebDriver.prototype.wait](#api-webdriver-webdriver-prototype-wait) +* [webdriver.WebDriver.prototype.sleep](#api-webdriver-webdriver-prototype-sleep) +* [webdriver.WebDriver.prototype.getWindowHandle](#api-webdriver-webdriver-prototype-getwindowhandle) +* [webdriver.WebDriver.prototype.getAllWindowHandles](#api-webdriver-webdriver-prototype-getallwindowhandles) +* [webdriver.WebDriver.prototype.getPageSource](#api-webdriver-webdriver-prototype-getpagesource) +* [webdriver.WebDriver.prototype.close](#api-webdriver-webdriver-prototype-close) +* [webdriver.WebDriver.prototype.get](#api-webdriver-webdriver-prototype-get) +* [webdriver.WebDriver.prototype.getCurrentUrl](#api-webdriver-webdriver-prototype-getcurrenturl) +* [webdriver.WebDriver.prototype.getTitle](#api-webdriver-webdriver-prototype-gettitle) +* [webdriver.WebDriver.prototype.findElement](#api-webdriver-webdriver-prototype-findelement) +* [webdriver.WebDriver.prototype.isElementPresent](#api-webdriver-webdriver-prototype-iselementpresent) +* [webdriver.WebDriver.prototype.findElements](#api-webdriver-webdriver-prototype-findelements) +* [webdriver.WebDriver.prototype.takeScreenshot](#api-webdriver-webdriver-prototype-takescreenshot) +* [webdriver.WebDriver.prototype.manage](#api-webdriver-webdriver-prototype-manage) +* [webdriver.WebDriver.prototype.navigate](#api-webdriver-webdriver-prototype-navigate) +* [webdriver.WebDriver.prototype.switchTo](#api-webdriver-webdriver-prototype-switchto) +* [webdriver.WebDriver.Navigation](#api-webdriver-webdriver-navigation) +* [webdriver.WebDriver.Navigation.prototype.to](#api-webdriver-webdriver-navigation-prototype-to) +* [webdriver.WebDriver.Navigation.prototype.back](#api-webdriver-webdriver-navigation-prototype-back) +* [webdriver.WebDriver.Navigation.prototype.forward](#api-webdriver-webdriver-navigation-prototype-forward) +* [webdriver.WebDriver.Navigation.prototype.refresh](#api-webdriver-webdriver-navigation-prototype-refresh) +* [webdriver.WebDriver.Options](#api-webdriver-webdriver-options) +* [webdriver.WebDriver.Options.prototype.addCookie](#api-webdriver-webdriver-options-prototype-addcookie) +* [webdriver.WebDriver.Options.prototype.deleteAllCookies](#api-webdriver-webdriver-options-prototype-deleteallcookies) +* [webdriver.WebDriver.Options.prototype.deleteCookie](#api-webdriver-webdriver-options-prototype-deletecookie) +* [webdriver.WebDriver.Options.prototype.getCookies](#api-webdriver-webdriver-options-prototype-getcookies) +* [webdriver.WebDriver.Options.prototype.getCookie](#api-webdriver-webdriver-options-prototype-getcookie) +* [webdriver.WebDriver.Options.prototype.logs](#api-webdriver-webdriver-options-prototype-logs) +* [webdriver.WebDriver.Options.prototype.timeouts](#api-webdriver-webdriver-options-prototype-timeouts) +* [webdriver.WebDriver.Options.prototype.window](#api-webdriver-webdriver-options-prototype-window) +* [webdriver.WebDriver.Timeouts](#api-webdriver-webdriver-timeouts) +* [webdriver.WebDriver.Timeouts.prototype.implicitlyWait](#api-webdriver-webdriver-timeouts-prototype-implicitlywait) +* [webdriver.WebDriver.Timeouts.prototype.setScriptTimeout](#api-webdriver-webdriver-timeouts-prototype-setscripttimeout) +* [webdriver.WebDriver.Timeouts.prototype.pageLoadTimeout](#api-webdriver-webdriver-timeouts-prototype-pageloadtimeout) +* [webdriver.WebDriver.Window](#api-webdriver-webdriver-window) +* [webdriver.WebDriver.Window.prototype.getPosition](#api-webdriver-webdriver-window-prototype-getposition) +* [webdriver.WebDriver.Window.prototype.setPosition](#api-webdriver-webdriver-window-prototype-setposition) +* [webdriver.WebDriver.Window.prototype.getSize](#api-webdriver-webdriver-window-prototype-getsize) +* [webdriver.WebDriver.Window.prototype.setSize](#api-webdriver-webdriver-window-prototype-setsize) +* [webdriver.WebDriver.Window.prototype.maximize](#api-webdriver-webdriver-window-prototype-maximize) +* [webdriver.WebDriver.Logs](#api-webdriver-webdriver-logs) +* [webdriver.WebDriver.Logs.prototype.get](#api-webdriver-webdriver-logs-prototype-get) +* [webdriver.WebDriver.Logs.prototype.getAvailableLogTypes](#api-webdriver-webdriver-logs-prototype-getavailablelogtypes) +* [webdriver.WebDriver.TargetLocator](#api-webdriver-webdriver-targetlocator) +* [webdriver.WebDriver.TargetLocator.prototype.activeElement](#api-webdriver-webdriver-targetlocator-prototype-activeelement) +* [webdriver.WebDriver.TargetLocator.prototype.defaultContent](#api-webdriver-webdriver-targetlocator-prototype-defaultcontent) +* [webdriver.WebDriver.TargetLocator.prototype.frame](#api-webdriver-webdriver-targetlocator-prototype-frame) +* [webdriver.WebDriver.TargetLocator.prototype.window](#api-webdriver-webdriver-targetlocator-prototype-window) +* [webdriver.WebDriver.TargetLocator.prototype.alert](#api-webdriver-webdriver-targetlocator-prototype-alert) +* [webdriver.Key.chord](#api-webdriver-key-chord) +* [webdriver.WebElement](#api-webdriver-webelement) +* [webdriver.WebElement.equals](#api-webdriver-webelement-equals) +* [webdriver.WebElement.prototype.getDriver](#api-webdriver-webelement-prototype-getdriver) +* [webdriver.WebElement.prototype.toWireValue](#api-webdriver-webelement-prototype-towirevalue) +* [webdriver.WebElement.prototype.findElement](#api-webdriver-webelement-prototype-findelement) +* [webdriver.WebElement.prototype.isElementPresent](#api-webdriver-webelement-prototype-iselementpresent) +* [webdriver.WebElement.prototype.findElements](#api-webdriver-webelement-prototype-findelements) +* [webdriver.WebElement.prototype.click](#api-webdriver-webelement-prototype-click) +* [webdriver.WebElement.prototype.sendKeys](#api-webdriver-webelement-prototype-sendkeys) +* [webdriver.WebElement.prototype.getTagName](#api-webdriver-webelement-prototype-gettagname) +* [webdriver.WebElement.prototype.getCssValue](#api-webdriver-webelement-prototype-getcssvalue) +* [webdriver.WebElement.prototype.getAttribute](#api-webdriver-webelement-prototype-getattribute) +* [webdriver.WebElement.prototype.getText](#api-webdriver-webelement-prototype-gettext) +* [webdriver.WebElement.prototype.getSize](#api-webdriver-webelement-prototype-getsize) +* [webdriver.WebElement.prototype.getLocation](#api-webdriver-webelement-prototype-getlocation) +* [webdriver.WebElement.prototype.isEnabled](#api-webdriver-webelement-prototype-isenabled) +* [webdriver.WebElement.prototype.isSelected](#api-webdriver-webelement-prototype-isselected) +* [webdriver.WebElement.prototype.submit](#api-webdriver-webelement-prototype-submit) +* [webdriver.WebElement.prototype.clear](#api-webdriver-webelement-prototype-clear) +* [webdriver.WebElement.prototype.isDisplayed](#api-webdriver-webelement-prototype-isdisplayed) +* [webdriver.WebElement.prototype.getOuterHtml](#api-webdriver-webelement-prototype-getouterhtml) +* [webdriver.WebElement.prototype.getInnerHtml](#api-webdriver-webelement-prototype-getinnerhtml) +* [webdriver.Alert](#api-webdriver-alert) +* [webdriver.Alert.prototype.getText](#api-webdriver-alert-prototype-gettext) +* [webdriver.Alert.prototype.accept](#api-webdriver-alert-prototype-accept) +* [webdriver.Alert.prototype.dismiss](#api-webdriver-alert-prototype-dismiss) +* [webdriver.Alert.prototype.sendKeys](#api-webdriver-alert-prototype-sendkeys) +* [webdriver.UnhandledAlertError](#api-webdriver-unhandledalerterror) +* [webdriver.UnhandledAlertError.prototype.getAlert](#api-webdriver-unhandledalerterror-prototype-getalert) + +##[element](https://github.com/angular/protractor/blob/master/lib/protractor.js#L74) #### Use as: element(locator) The element function returns an Element Finder. Element Finders do not actually attempt to find the element until a method is called on them, @@ -201,7 +201,7 @@ Type | Description ElementFinder | -##[elementFinder.find](https://github.com/angular/protractor/blob/master/lib/protractor.js#L128) +##[elementFinder.find](https://github.com/angular/protractor/blob/master/lib/protractor.js#L128) #### Use as: element(locator).find() Returns the specified WebElement. Throws the WebDriver error if the element doesn't exist. @@ -218,7 +218,7 @@ Type | Description [webdriver.WebElement](#webdriverwebelement) | -##[elementFinder.isPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L139) +##[elementFinder.isPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L139) #### Use as: element(locator).isPresent() Determine whether an element is present on the page. @@ -248,7 +248,7 @@ Type | Description !webdriver.promise.Promise | A promise which resolves to a boolean. -##[elementFinder.locator](https://github.com/angular/protractor/blob/master/lib/protractor.js#L161) +##[elementFinder.locator](https://github.com/angular/protractor/blob/master/lib/protractor.js#L161) Returns the originally specified locator. @@ -264,7 +264,7 @@ Type | Description webdriver.Locator | The element locator. -##[elementFinder.element](https://github.com/angular/protractor/blob/master/lib/protractor.js#L170) +##[elementFinder.element](https://github.com/angular/protractor/blob/master/lib/protractor.js#L170) #### Use as: element(locator).element(locator) Calls to element may be chained to find elements within a parent. @@ -312,7 +312,7 @@ Type | Description function(webdriver.Locator): ElementFinder | -##[elementFinder.$](https://github.com/angular/protractor/blob/master/lib/protractor.js#L201) +##[elementFinder.$](https://github.com/angular/protractor/blob/master/lib/protractor.js#L201) #### Use as: element(locator).$(cssSelector) Shortcut for chaining css element finders. @@ -356,7 +356,7 @@ Type | Description ElementFinder | -##[element.all](https://github.com/angular/protractor/blob/master/lib/protractor.js#L238) +##[element.all](https://github.com/angular/protractor/blob/master/lib/protractor.js#L238) #### Use as: element.all(locator) element.all is used for operations on an array of elements (as opposed to a single element). @@ -397,7 +397,7 @@ Type | Description ElementArrayFinder | -##[elementArrayFinder.count](https://github.com/angular/protractor/blob/master/lib/protractor.js#L262) +##[elementArrayFinder.count](https://github.com/angular/protractor/blob/master/lib/protractor.js#L262) #### Use as: element.all(locator).count() Count the number of elements found by the locator. @@ -428,7 +428,7 @@ Type | Description !webdriver.promise.Promise | A promise which resolves to the number of elements matching the locator. -##[elementArrayFinder.get](https://github.com/angular/protractor/blob/master/lib/protractor.js#L286) +##[elementArrayFinder.get](https://github.com/angular/protractor/blob/master/lib/protractor.js#L286) #### Use as: element.all(locator).get(index) Get an element found by the locator by index. The index starts at 0. @@ -467,7 +467,7 @@ Type | Description [webdriver.WebElement](#webdriverwebelement) | The element at the given index -##[elementArrayFinder.first](https://github.com/angular/protractor/blob/master/lib/protractor.js#L312) +##[elementArrayFinder.first](https://github.com/angular/protractor/blob/master/lib/protractor.js#L312) #### Use as: element.all(locator).first() Get the first element found using the locator. @@ -498,7 +498,7 @@ Type | Description [webdriver.WebElement](#webdriverwebelement) | The first matching element -##[elementArrayFinder.last](https://github.com/angular/protractor/blob/master/lib/protractor.js#L339) +##[elementArrayFinder.last](https://github.com/angular/protractor/blob/master/lib/protractor.js#L339) #### Use as: element.all(locator).last() Get the last matching element for the locator. @@ -529,7 +529,7 @@ Type | Description [webdriver.WebElement](#webdriverwebelement) | the last matching element -##[elementArrayFinder.each](https://github.com/angular/protractor/blob/master/lib/protractor.js#L389) +##[elementArrayFinder.each](https://github.com/angular/protractor/blob/master/lib/protractor.js#L389) #### Use as: element.all(locator).each(eachFunction) Calls the input function on each WebElement found by the locator. @@ -563,7 +563,7 @@ fn | function([webdriver.WebElement](#webdriverwebelement)) | Input function -##[elementArrayFinder.map](https://github.com/angular/protractor/blob/master/lib/protractor.js#L416) +##[elementArrayFinder.map](https://github.com/angular/protractor/blob/master/lib/protractor.js#L416) #### Use as: element.all(locator).map(mapFunction) Apply a map function to each element found using the locator. The callback receives the web element as the first argument and the index as @@ -613,7 +613,7 @@ Type | Description !webdriver.promise.Promise | A promise that resolves to an array of values returned by the map function. -##[Protractor](https://github.com/angular/protractor/blob/master/lib/protractor.js#L494) +##[Protractor](https://github.com/angular/protractor/blob/master/lib/protractor.js#L494) @@ -632,7 +632,7 @@ opt_rootElement | string= | Selector element that has an ng-app in scope. -##[Protractor.prototype.waitForAngular](https://github.com/angular/protractor/blob/master/lib/protractor.js#L589) +##[Protractor.prototype.waitForAngular](https://github.com/angular/protractor/blob/master/lib/protractor.js#L589) Instruct webdriver to wait until Angular has finished rendering and has no outstanding $http calls before continuing. @@ -649,7 +649,7 @@ Type | Description !webdriver.promise.Promise | A promise that will resolve to the scripts return value. -##[Protractor.prototype.wrapWebElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L630) +##[Protractor.prototype.wrapWebElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L630) Wrap a webdriver.WebElement with protractor specific functionality. @@ -672,7 +672,7 @@ Type | Description [webdriver.WebElement](#webdriverwebelement) | the wrapped web element. -##[element.$](https://github.com/angular/protractor/blob/master/lib/protractor.js#L655) +##[element.$](https://github.com/angular/protractor/blob/master/lib/protractor.js#L655) #### Use as: $(cssSelector) Shortcut for querying the document directly with css. @@ -709,7 +709,7 @@ Type | Description ![webdriver.WebElement](#webdriverwebelement) | -##[element.findElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L678) +##[element.findElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L678) @@ -725,7 +725,7 @@ Type | Description ![webdriver.WebElement](#webdriverwebelement) | -##[element.$$](https://github.com/angular/protractor/blob/master/lib/protractor.js#L695) +##[element.$$](https://github.com/angular/protractor/blob/master/lib/protractor.js#L695) #### Use as: $$(cssSelector) Shortcut for querying the document directly with css. @@ -768,7 +768,7 @@ Type | Description !webdriver.promise.Promise | A promise that will be resolved to an array of the located [webdriver.WebElement](#webdriverwebelement)s. -##[element.findElements](https://github.com/angular/protractor/blob/master/lib/protractor.js#L725) +##[element.findElements](https://github.com/angular/protractor/blob/master/lib/protractor.js#L725) @@ -784,7 +784,7 @@ Type | Description !webdriver.promise.Promise | A promise that will be resolved to an array of the located [webdriver.WebElement](#webdriverwebelement)s. -##[element.isElementPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L749) +##[element.isElementPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L749) @@ -800,7 +800,7 @@ Type | Description !webdriver.promise.Promise | A promise that will be resolved with whether an element could be located on the page. -##[element.evaluate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L765) +##[element.evaluate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L765) Evaluates the input as if it were on the scope of the current element. @@ -823,7 +823,7 @@ Type | Description !webdriver.promise.Promise | A promise that will resolve to the evaluated expression. The result will be resolved as in {@link webdriver.WebDriver.executeScript}. In summary - primitives will be resolved as is, functions will be converted to string, and elements will be returned as a WebElement. -##[Protractor.prototype.findElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L784) +##[Protractor.prototype.findElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L784) Waits for Angular to finish rendering before searching for elements. @@ -839,7 +839,7 @@ Type | Description ![webdriver.WebElement](#webdriverwebelement) | -##[Protractor.prototype.findElements](https://github.com/angular/protractor/blob/master/lib/protractor.js#L802) +##[Protractor.prototype.findElements](https://github.com/angular/protractor/blob/master/lib/protractor.js#L802) Waits for Angular to finish rendering before searching for elements. @@ -855,7 +855,7 @@ Type | Description !webdriver.promise.Promise | A promise that will be resolved to an array of the located [webdriver.WebElement](#webdriverwebelement)s. -##[Protractor.prototype.isElementPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L827) +##[Protractor.prototype.isElementPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L827) Tests if an element is present on the page. @@ -871,7 +871,7 @@ Type | Description !webdriver.promise.Promise | A promise that will resolve to whether the element is present on the page. -##[Protractor.prototype.addMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L843) +##[Protractor.prototype.addMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L843) Add a module to load before Angular whenever Protractor.get is called. Modules will be registered after existing modules already on the page, @@ -893,7 +893,7 @@ varArgs | ...* | Any additional arguments will be provided to the script and may -##[Protractor.prototype.clearMockModules](https://github.com/angular/protractor/blob/master/lib/protractor.js#L861) +##[Protractor.prototype.clearMockModules](https://github.com/angular/protractor/blob/master/lib/protractor.js#L861) Clear the list of registered mock modules. @@ -903,7 +903,7 @@ Clear the list of registered mock modules. -##[Protractor.prototype.removeMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L870) +##[Protractor.prototype.removeMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L870) Remove a registered mock module. @@ -920,7 +920,7 @@ name | !string | The name of the module to remove. -##[Protractor.prototype.get](https://github.com/angular/protractor/blob/master/lib/protractor.js#L881) +##[Protractor.prototype.get](https://github.com/angular/protractor/blob/master/lib/protractor.js#L881) See webdriver.WebDriver.get @@ -943,7 +943,7 @@ opt_timeout | number= | Number of seconds to wait for Angular to start. -##[Protractor.prototype.refresh](https://github.com/angular/protractor/blob/master/lib/protractor.js#L949) +##[Protractor.prototype.refresh](https://github.com/angular/protractor/blob/master/lib/protractor.js#L949) See webdriver.WebDriver.refresh @@ -965,7 +965,7 @@ opt_timeout | number= | Number of seconds to wait for Angular to start. -##[Protractor.prototype.navigate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L972) +##[Protractor.prototype.navigate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L972) Mixin navigation methods back into the navigation object so that they are invoked as before, i.e. driver.navigate().refresh() @@ -976,7 +976,7 @@ they are invoked as before, i.e. driver.navigate().refresh() -##[Protractor.prototype.setLocation](https://github.com/angular/protractor/blob/master/lib/protractor.js#L982) +##[Protractor.prototype.setLocation](https://github.com/angular/protractor/blob/master/lib/protractor.js#L982) Browse to another page using in-page navigation. @@ -999,7 +999,7 @@ Type | Description !webdriver.promise.Promise | A promise that will resolve once page has been changed. -##[Protractor.prototype.getLocationAbsUrl](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1000) +##[Protractor.prototype.getLocationAbsUrl](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1000) Returns the current absolute url from AngularJS. @@ -1009,7 +1009,7 @@ Returns the current absolute url from AngularJS. -##[Protractor.prototype.debugger](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1008) +##[Protractor.prototype.debugger](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1008) Pauses the test and injects some helper functions into the browser, so that debugging may be done in the browser console. @@ -1034,7 +1034,7 @@ debugger. -##[Protractor.prototype.pause](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1043) +##[Protractor.prototype.pause](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1043) Beta (unstable) pause function for debugging webdriver tests. Use browser.pause() in your test to enter the protractor debugger from that @@ -1047,7 +1047,7 @@ Does not require changes to the command line (no need to add 'debug'). -##[ProtractorBy](https://github.com/angular/protractor/blob/master/lib/locators.js#L6) +##[ProtractorBy](https://github.com/angular/protractor/blob/master/lib/locators.js#L6) The Protractor Locators. These provide ways of finding elements in Angular applications by binding, model, etc. @@ -1058,7 +1058,7 @@ Angular applications by binding, model, etc. -##[WebdriverBy.prototype](https://github.com/angular/protractor/blob/master/lib/locators.js#L15) +##[WebdriverBy.prototype](https://github.com/angular/protractor/blob/master/lib/locators.js#L15) webdriver's By is an enum of locator functions, so we must set it to a prototype before inheriting from it. @@ -1069,7 +1069,7 @@ a prototype before inheriting from it. -##[ProtractorBy.prototype.addLocator](https://github.com/angular/protractor/blob/master/lib/locators.js#L22) +##[ProtractorBy.prototype.addLocator](https://github.com/angular/protractor/blob/master/lib/locators.js#L22) #### Use as: by.addLocator(locatorName, functionOrScript) Add a locator to this instance of ProtractorBy. This locator can then be used with element(by.locatorName(args)). @@ -1113,7 +1113,7 @@ script | Function|string | A script to be run in the context of the browser -##[ProtractorBy.prototype.binding](https://github.com/angular/protractor/blob/master/lib/locators.js#L73) +##[ProtractorBy.prototype.binding](https://github.com/angular/protractor/blob/master/lib/locators.js#L73) #### Use as: by.binding() Find an element by binding. @@ -1151,7 +1151,7 @@ Type | Description {findElementsOverride: findElementsOverride, message: string} | -##[ProtractorBy.prototype.exactBinding](https://github.com/angular/protractor/blob/master/lib/locators.js#L102) +##[ProtractorBy.prototype.exactBinding](https://github.com/angular/protractor/blob/master/lib/locators.js#L102) #### Use as: by.exactBinding() Same as by.binding() except this does not allow for partial matches Find an element by exact binding. @@ -1175,7 +1175,7 @@ Type | Description {findElementsOverride: findElementsOverride, message: string} | -##[ProtractorBy.prototype.select](https://github.com/angular/protractor/blob/master/lib/locators.js#L122) +##[ProtractorBy.prototype.select](https://github.com/angular/protractor/blob/master/lib/locators.js#L122) **DEPRECATED** Use 'model' instead. @@ -1195,7 +1195,7 @@ element(by.select('user')); -##[ProtractorBy.prototype.selectedOption](https://github.com/angular/protractor/blob/master/lib/locators.js#L141) +##[ProtractorBy.prototype.selectedOption](https://github.com/angular/protractor/blob/master/lib/locators.js#L141) @@ -1215,7 +1215,7 @@ element(by.selectedOption("user")); -##[ProtractorBy.prototype.input](https://github.com/angular/protractor/blob/master/lib/locators.js#L158) +##[ProtractorBy.prototype.input](https://github.com/angular/protractor/blob/master/lib/locators.js#L158) **DEPRECATED** Use 'model' instead. @@ -1235,7 +1235,7 @@ element(by.input('user')); -##[ProtractorBy.prototype.model](https://github.com/angular/protractor/blob/master/lib/locators.js#L176) +##[ProtractorBy.prototype.model](https://github.com/angular/protractor/blob/master/lib/locators.js#L176) #### Use as: by.model(modelName) Find an element by ng-model expression. @@ -1264,7 +1264,7 @@ model | string | ng-model expression. -##[ProtractorBy.prototype.buttonText](https://github.com/angular/protractor/blob/master/lib/locators.js#L200) +##[ProtractorBy.prototype.buttonText](https://github.com/angular/protractor/blob/master/lib/locators.js#L200) Find a button by text. @@ -1297,7 +1297,7 @@ Type | Description {findElementsOverride: findElementsOverride, message: string} | -##[ProtractorBy.prototype.partialButtonText](https://github.com/angular/protractor/blob/master/lib/locators.js#L223) +##[ProtractorBy.prototype.partialButtonText](https://github.com/angular/protractor/blob/master/lib/locators.js#L223) Find a button by partial text. @@ -1330,7 +1330,7 @@ Type | Description {findElementsOverride: findElementsOverride, message: string} | -##[ProtractorBy.prototype.textarea](https://github.com/angular/protractor/blob/master/lib/locators.js#L247) +##[ProtractorBy.prototype.textarea](https://github.com/angular/protractor/blob/master/lib/locators.js#L247) **DEPRECATED** Use 'model' instead. @@ -1350,7 +1350,7 @@ element(by.textarea('user')); -##[ProtractorBy.prototype.repeater](https://github.com/angular/protractor/blob/master/lib/locators.js#L265) +##[ProtractorBy.prototype.repeater](https://github.com/angular/protractor/blob/master/lib/locators.js#L265) Find elements inside an ng-repeat. @@ -1386,7 +1386,7 @@ var rows = element.all(by.repeater('cat in pets')); -##[ProtractorBy.prototype.cssContainingText](https://github.com/angular/protractor/blob/master/lib/locators.js#L344) +##[ProtractorBy.prototype.cssContainingText](https://github.com/angular/protractor/blob/master/lib/locators.js#L344) Find elements by CSS which contain a certain string. @@ -1410,7 +1410,7 @@ var dog = element(by.cssContainingText('.pet', 'Dog')); -##[webdriver.WebDriver](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#47) +##[webdriver.WebDriver](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#47) Creates a new WebDriver client, which provides control over a browser. @@ -1445,7 +1445,7 @@ opt_flow | webdriver.promise.ControlFlow= | The flow to schedule commands throug -##[webdriver.WebDriver.attachToSession](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#87) +##[webdriver.WebDriver.attachToSession](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#87) Creates a new WebDriver client for an existing session. @@ -1469,7 +1469,7 @@ Type | Description ![webdriver.WebDriver](#webdriverwebdriver) | A new client for the specified session. -##[webdriver.WebDriver.createSession](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#102) +##[webdriver.WebDriver.createSession](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#102) Creates a new WebDriver session. @@ -1493,7 +1493,7 @@ Type | Description ![webdriver.WebDriver](#webdriverwebdriver) | The driver for the newly created session. -##[webdriver.WebDriver.prototype.controlFlow](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#243) +##[webdriver.WebDriver.prototype.controlFlow](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#243) @@ -1509,7 +1509,7 @@ Type | Description !webdriver.promise.ControlFlow | The control flow used by this instance. -##[webdriver.WebDriver.prototype.schedule](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#252) +##[webdriver.WebDriver.prototype.schedule](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#252) Schedules a {@code webdriver.Command} to be executed by this driver's {@code webdriver.CommandExecutor}. @@ -1531,10 +1531,10 @@ description | string | A description of the command for debugging. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the command result. +!webdriver.promise.Promise.<T> | A promise that will be resolved with the command result. -##[webdriver.WebDriver.prototype.getSession](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#304) +##[webdriver.WebDriver.prototype.getSession](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#305) @@ -1547,10 +1547,10 @@ Type | Description Type | Description --- | --- -!webdriver.promise.Promise | A promise for this client's session. +!webdriver.promise.Promise.<!webdriver.Session> | A promise for this client's session. -##[webdriver.WebDriver.prototype.getCapabilities](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#312) +##[webdriver.WebDriver.prototype.getCapabilities](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#314) @@ -1563,10 +1563,10 @@ Type | Description Type | Description --- | --- -!webdriver.promise.Promise | A promise that will resolve with the this instance's capabilities. +!webdriver.promise.Promise.<!webdriver.Capabilities> | A promise that will resolve with the this instance's capabilities. -##[webdriver.WebDriver.prototype.quit](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#323) +##[webdriver.WebDriver.prototype.quit](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#325) Schedules a command to quit the current session. After calling quit, this instance will be invalidated and may no longer be used to issue commands @@ -1581,10 +1581,10 @@ against the browser. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the command has completed. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the command has completed. -##[webdriver.WebDriver.prototype.actions](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#342) +##[webdriver.WebDriver.prototype.actions](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#344) Creates a new action sequence using this driver. The sequence will not be scheduled for execution until {@link webdriver.ActionSequence#perform} is @@ -1609,7 +1609,7 @@ Type | Description !webdriver.ActionSequence | A new action sequence for this instance. -##[webdriver.WebDriver.prototype.executeScript](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#360) +##[webdriver.WebDriver.prototype.executeScript](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#362) Schedules a command to execute JavaScript in the context of the currently selected frame or window. The script fragment will be executed as the body @@ -1659,10 +1659,10 @@ var_args | ...* | The arguments to pass to the script. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will resolve to the scripts return value. +!webdriver.promise.Promise.<T> | A promise that will resolve to the scripts return value. -##[webdriver.WebDriver.prototype.executeAsyncScript](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#409) +##[webdriver.WebDriver.prototype.executeAsyncScript](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#412) Schedules a command to execute asynchronous JavaScript in the context of the currently selected frame or window. The script fragment will be executed as @@ -1754,10 +1754,10 @@ var_args | ...* | The arguments to pass to the script. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will resolve to the scripts return value. +!webdriver.promise.Promise.<T> | A promise that will resolve to the scripts return value. -##[webdriver.WebDriver.prototype.call](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#500) +##[webdriver.WebDriver.prototype.call](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#504) Schedules a command to execute a custom function. @@ -1768,7 +1768,7 @@ Schedules a command to execute a custom function. Param | Type | Description --- | --- | --- -fn | !Function | The function to execute. +fn | function(...): (T|webdriver.promise.Promise.<T>) | The function to execute. opt_scope | Object= | The object in whose scope to execute the function. var_args | ...* | Any arguments to pass to the function. @@ -1779,15 +1779,20 @@ var_args | ...* | Any arguments to pass to the function. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the function's result. +!webdriver.promise.Promise.<T> | A promise that will be resolved' with the function's result. -##[webdriver.WebDriver.prototype.wait](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#519) +##[webdriver.WebDriver.prototype.wait](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#525) Schedules a command to wait for a condition to hold, as defined by some user supplied function. If any errors occur while evaluating the wait, they will be allowed to propagate. +

In the event a condition returns a {@link webdriver.promise.Promise}, the +polling loop will wait for it to be resolved and use the resolved value for +evaluating whether the condition has been satisfied. The resolution time for +a promise is factored into whether a wait has timed out. + @@ -1809,7 +1814,7 @@ Type | Description !webdriver.promise.Promise | A promise that will be resolved when the wait condition has been satisfied. -##[webdriver.WebDriver.prototype.sleep](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#535) +##[webdriver.WebDriver.prototype.sleep](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#547) Schedules a command to make the driver sleep for the given amount of time. @@ -1829,10 +1834,10 @@ ms | number | The amount of time, in milliseconds, to sleep. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the sleep has finished. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the sleep has finished. -##[webdriver.WebDriver.prototype.getWindowHandle](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#546) +##[webdriver.WebDriver.prototype.getWindowHandle](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#558) Schedules a command to retrieve they current window handle. @@ -1845,10 +1850,10 @@ Schedules a command to retrieve they current window handle. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the current window handle. +!webdriver.promise.Promise.<string> | A promise that will be resolved with the current window handle. -##[webdriver.WebDriver.prototype.getAllWindowHandles](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#558) +##[webdriver.WebDriver.prototype.getAllWindowHandles](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#570) Schedules a command to retrieve the current list of available window handles. @@ -1861,10 +1866,10 @@ Schedules a command to retrieve the current list of available window handles. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with an array of window handles. +!webdriver.promise.Promise.<!Array.<string>> | A promise that will be resolved with an array of window handles. -##[webdriver.WebDriver.prototype.getPageSource](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#570) +##[webdriver.WebDriver.prototype.getPageSource](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#582) Schedules a command to retrieve the current page's source. The page source returned is a representation of the underlying DOM: do not expect it to be @@ -1880,10 +1885,10 @@ server. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the current page source. +!webdriver.promise.Promise.<string> | A promise that will be resolved with the current page source. -##[webdriver.WebDriver.prototype.close](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#585) +##[webdriver.WebDriver.prototype.close](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#597) Schedules a command to close the current window. @@ -1896,10 +1901,10 @@ Schedules a command to close the current window. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when this command has completed. +!webdriver.promise.Promise.<void> | A promise that will be resolved when this command has completed. -##[webdriver.WebDriver.prototype.get](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#596) +##[webdriver.WebDriver.prototype.get](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#608) Schedules a command to navigate to the given URL. @@ -1919,10 +1924,10 @@ url | string | The fully qualified URL to open. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the document has finished loading. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the document has finished loading. -##[webdriver.WebDriver.prototype.getCurrentUrl](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#607) +##[webdriver.WebDriver.prototype.getCurrentUrl](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#619) Schedules a command to retrieve the URL of the current page. @@ -1935,10 +1940,10 @@ Schedules a command to retrieve the URL of the current page. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the current URL. +!webdriver.promise.Promise.<string> | A promise that will be resolved with the current URL. -##[webdriver.WebDriver.prototype.getTitle](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#619) +##[webdriver.WebDriver.prototype.getTitle](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#631) Schedules a command to retrieve the current page's title. @@ -1951,10 +1956,10 @@ Schedules a command to retrieve the current page's title. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the current page's title. +!webdriver.promise.Promise.<string> | A promise that will be resolved with the current page's title. -##[webdriver.WebDriver.prototype.findElement](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#630) +##[webdriver.WebDriver.prototype.findElement](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#642) Schedule a command to find an element on the page. If the element cannot be found, a {@link bot.ErrorCode.NO_SUCH_ELEMENT} result will be returned @@ -2017,7 +2022,7 @@ Type | Description ![webdriver.WebElement](#webdriverwebelement) | A WebElement that can be used to issue commands against the located element. If the element is not found, the element will be invalidated and all scheduled commands aborted. -##[webdriver.WebDriver.prototype.isElementPresent](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#777) +##[webdriver.WebDriver.prototype.isElementPresent](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#785) Schedules a command to test if an element is present on the page. @@ -2045,7 +2050,7 @@ Type | Description !webdriver.promise.Promise.<boolean> | A promise that will resolve with whether the element is present on the page. -##[webdriver.WebDriver.prototype.findElements](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#801) +##[webdriver.WebDriver.prototype.findElements](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#810) Schedule a command to search for multiple elements on the page. @@ -2068,7 +2073,7 @@ Type | Description !webdriver.promise.Promise.<!Array.<!webdriver.WebElement>> | A promise that will resolve to an array of WebElements. -##[webdriver.WebDriver.prototype.takeScreenshot](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#848) +##[webdriver.WebDriver.prototype.takeScreenshot](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#857) Schedule a command to take a screenshot. The driver makes a best effort to return a screenshot of the following, in order of preference: @@ -2088,10 +2093,10 @@ return a screenshot of the following, in order of preference: Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved to the screenshot as a base-64 encoded PNG. +!webdriver.promise.Promise.<string> | A promise that will be resolved to the screenshot as a base-64 encoded PNG. -##[webdriver.WebDriver.prototype.manage](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#867) +##[webdriver.WebDriver.prototype.manage](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#876) @@ -2107,7 +2112,7 @@ Type | Description ![webdriver.WebDriver.Options](#webdriverwebdriveroptions) | The options interface for this instance. -##[webdriver.WebDriver.prototype.navigate](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#876) +##[webdriver.WebDriver.prototype.navigate](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#885) @@ -2123,7 +2128,7 @@ Type | Description ![webdriver.WebDriver.Navigation](#webdriverwebdrivernavigation) | The navigation interface for this instance. -##[webdriver.WebDriver.prototype.switchTo](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#885) +##[webdriver.WebDriver.prototype.switchTo](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#894) @@ -2139,7 +2144,7 @@ Type | Description ![webdriver.WebDriver.TargetLocator](#webdriverwebdrivertargetlocator) | The target locator interface for this instance. -##[webdriver.WebDriver.Navigation](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#895) +##[webdriver.WebDriver.Navigation](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#904) Interface for navigating back and forth in the browser history. @@ -2156,7 +2161,7 @@ driver | ![webdriver.WebDriver](#webdriverwebdriver) | The parent driver. -##[webdriver.WebDriver.Navigation.prototype.to](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#907) +##[webdriver.WebDriver.Navigation.prototype.to](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#916) Schedules a command to navigate to a new URL. @@ -2176,10 +2181,10 @@ url | string | The URL to navigate to. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the URL has been loaded. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the URL has been loaded. -##[webdriver.WebDriver.Navigation.prototype.back](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#921) +##[webdriver.WebDriver.Navigation.prototype.back](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#930) Schedules a command to move backwards in the browser history. @@ -2192,10 +2197,10 @@ Schedules a command to move backwards in the browser history. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the navigation event has completed. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the navigation event has completed. -##[webdriver.WebDriver.Navigation.prototype.forward](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#933) +##[webdriver.WebDriver.Navigation.prototype.forward](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#942) Schedules a command to move forwards in the browser history. @@ -2208,10 +2213,10 @@ Schedules a command to move forwards in the browser history. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the navigation event has completed. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the navigation event has completed. -##[webdriver.WebDriver.Navigation.prototype.refresh](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#945) +##[webdriver.WebDriver.Navigation.prototype.refresh](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#954) Schedules a command to refresh the current page. @@ -2224,10 +2229,10 @@ Schedules a command to refresh the current page. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the navigation event has completed. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the navigation event has completed. -##[webdriver.WebDriver.Options](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#958) +##[webdriver.WebDriver.Options](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#967) Provides methods for managing browser and driver state. @@ -2244,7 +2249,7 @@ driver | ![webdriver.WebDriver](#webdriverwebdriver) | The parent driver. -##[webdriver.WebDriver.Options.prototype.addCookie](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#970) +##[webdriver.WebDriver.Options.prototype.addCookie](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#994) Schedules a command to add a cookie. @@ -2269,10 +2274,10 @@ opt_expiry | (number|!Date)= | When the cookie expires. If specified as a n Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the cookie has been added to the page. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the cookie has been added to the page. -##[webdriver.WebDriver.Options.prototype.deleteAllCookies](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1027) +##[webdriver.WebDriver.Options.prototype.deleteAllCookies](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1051) Schedules a command to delete all cookies visible to the current page. @@ -2285,10 +2290,10 @@ Schedules a command to delete all cookies visible to the current page. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when all cookies have been deleted. +!webdriver.promise.Promise.<void> | A promise that will be resolved when all cookies have been deleted. -##[webdriver.WebDriver.Options.prototype.deleteCookie](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1039) +##[webdriver.WebDriver.Options.prototype.deleteCookie](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1063) Schedules a command to delete the cookie with the given name. This command is a no-op if there is no cookie with the given name visible to the current @@ -2310,10 +2315,10 @@ name | string | The name of the cookie to delete. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the cookie has been deleted. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the cookie has been deleted. -##[webdriver.WebDriver.Options.prototype.getCookies](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1055) +##[webdriver.WebDriver.Options.prototype.getCookies](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1079) Schedules a command to retrieve all cookies visible to the current page. Each cookie will be returned as a JSON object as described by the WebDriver @@ -2328,10 +2333,11 @@ wire protocol. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the cookies visible to the current page. +!webdriver.promise.Promise.< + !Array.<webdriver.WebDriver.Options.Cookie>> | A promise that will be resolved with the cookies visible to the current page. -##[webdriver.WebDriver.Options.prototype.getCookie](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1070) +##[webdriver.WebDriver.Options.prototype.getCookie](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1095) Schedules a command to retrieve the cookie with the given name. Returns null if there is no such cookie. The cookie will be returned as a JSON object as @@ -2353,10 +2359,10 @@ name | string | The name of the cookie to retrieve. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the named cookie, or {@code null} if there is no such cookie. +!webdriver.promise.Promise.<?webdriver.WebDriver.Options.Cookie> | A promise that will be resolved with the named cookie, or {@code null} if there is no such cookie. -##[webdriver.WebDriver.Options.prototype.logs](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1088) +##[webdriver.WebDriver.Options.prototype.logs](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1114) @@ -2372,7 +2378,7 @@ Type | Description ![webdriver.WebDriver.Logs](#webdriverwebdriverlogs) | The interface for managing driver logs. -##[webdriver.WebDriver.Options.prototype.timeouts](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1097) +##[webdriver.WebDriver.Options.prototype.timeouts](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1123) @@ -2388,7 +2394,7 @@ Type | Description ![webdriver.WebDriver.Timeouts](#webdriverwebdrivertimeouts) | The interface for managing driver timeouts. -##[webdriver.WebDriver.Options.prototype.window](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1106) +##[webdriver.WebDriver.Options.prototype.window](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1132) @@ -2404,7 +2410,7 @@ Type | Description ![webdriver.WebDriver.Window](#webdriverwebdriverwindow) | The interface for managing the current window. -##[webdriver.WebDriver.Timeouts](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1116) +##[webdriver.WebDriver.Timeouts](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1142) An interface for managing timeout behavior for WebDriver instances. @@ -2421,7 +2427,7 @@ driver | ![webdriver.WebDriver](#webdriverwebdriver) | The parent driver. -##[webdriver.WebDriver.Timeouts.prototype.implicitlyWait](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1128) +##[webdriver.WebDriver.Timeouts.prototype.implicitlyWait](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1154) Specifies the amount of time the driver should wait when searching for an element if it is not immediately present. @@ -2455,10 +2461,10 @@ ms | number | The amount of time to wait, in milliseconds. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the implicit wait timeout has been set. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the implicit wait timeout has been set. -##[webdriver.WebDriver.Timeouts.prototype.setScriptTimeout](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1157) +##[webdriver.WebDriver.Timeouts.prototype.setScriptTimeout](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1183) Sets the amount of time to wait, in milliseconds, for an asynchronous script to finish execution before returning an error. If the timeout is less than or @@ -2480,10 +2486,10 @@ ms | number | The amount of time to wait, in milliseconds. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the script timeout has been set. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the script timeout has been set. -##[webdriver.WebDriver.Timeouts.prototype.pageLoadTimeout](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1174) +##[webdriver.WebDriver.Timeouts.prototype.pageLoadTimeout](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1200) Sets the amount of time to wait for a page load to complete before returning an error. If the timeout is negative, page loads may be indefinite. @@ -2504,10 +2510,10 @@ ms | number | The amount of time to wait, in milliseconds. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the timeout has been set. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the timeout has been set. -##[webdriver.WebDriver.Window](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1191) +##[webdriver.WebDriver.Window](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1217) An interface for managing the current window. @@ -2524,7 +2530,7 @@ driver | ![webdriver.WebDriver](#webdriverwebdriver) | The parent driver. -##[webdriver.WebDriver.Window.prototype.getPosition](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1203) +##[webdriver.WebDriver.Window.prototype.getPosition](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1229) Retrieves the window's current position, relative to the top left corner of the screen. @@ -2538,10 +2544,10 @@ the screen. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the window's position in the form of a {x:number, y:number} object literal. +!webdriver.promise.Promise.<{x: number, y: number}> | A promise that will be resolved with the window's position in the form of a {x:number, y:number} object literal. -##[webdriver.WebDriver.Window.prototype.setPosition](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1217) +##[webdriver.WebDriver.Window.prototype.setPosition](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1244) Repositions the current window. @@ -2562,10 +2568,10 @@ y | number | The desired vertical position, relative to the top of the of the sc Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the command has completed. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the command has completed. -##[webdriver.WebDriver.Window.prototype.getSize](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1236) +##[webdriver.WebDriver.Window.prototype.getSize](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1263) Retrieves the window's current size. @@ -2578,10 +2584,10 @@ Retrieves the window's current size. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the window's size in the form of a {width:number, height:number} object literal. +!webdriver.promise.Promise.<{width: number, height: number}> | A promise that will be resolved with the window's size in the form of a {width:number, height:number} object literal. -##[webdriver.WebDriver.Window.prototype.setSize](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1250) +##[webdriver.WebDriver.Window.prototype.setSize](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1277) Resizes the current window. @@ -2602,10 +2608,10 @@ height | number | The desired window height. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the command has completed. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the command has completed. -##[webdriver.WebDriver.Window.prototype.maximize](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1267) +##[webdriver.WebDriver.Window.prototype.maximize](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1294) Maximizes the current window. @@ -2618,10 +2624,10 @@ Maximizes the current window. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the command has completed. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the command has completed. -##[webdriver.WebDriver.Logs](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1280) +##[webdriver.WebDriver.Logs](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1307) Interface for managing WebDriver log records. @@ -2638,7 +2644,7 @@ driver | ![webdriver.WebDriver](#webdriverwebdriver) | The parent driver. -##[webdriver.WebDriver.Logs.prototype.get](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1292) +##[webdriver.WebDriver.Logs.prototype.get](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1319) Fetches available log entries for the given type. @@ -2667,7 +2673,7 @@ Type | Description !webdriver.promise.Promise.<!Array.<!webdriver.logging.Entry>> | A promise that will resolve to a list of log entries for the specified type. -##[webdriver.WebDriver.Logs.prototype.getAvailableLogTypes](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1323) +##[webdriver.WebDriver.Logs.prototype.getAvailableLogTypes](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1350) Retrieves the log types available to this driver. @@ -2683,7 +2689,7 @@ Type | Description !webdriver.promise.Promise.<!Array.<!webdriver.logging.Type>> | A promise that will resolve to a list of available log types. -##[webdriver.WebDriver.TargetLocator](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1336) +##[webdriver.WebDriver.TargetLocator](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1363) An interface for changing the focus of the driver to another frame or window. @@ -2700,7 +2706,7 @@ driver | ![webdriver.WebDriver](#webdriverwebdriver) | The parent driver. -##[webdriver.WebDriver.TargetLocator.prototype.activeElement](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1348) +##[webdriver.WebDriver.TargetLocator.prototype.activeElement](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1375) Schedules a command retrieve the {@code document.activeElement} element on the current document, or {@code document.body} if activeElement is not @@ -2718,7 +2724,7 @@ Type | Description ![webdriver.WebElement](#webdriverwebelement) | The active element. -##[webdriver.WebDriver.TargetLocator.prototype.defaultContent](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1362) +##[webdriver.WebDriver.TargetLocator.prototype.defaultContent](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1389) Schedules a command to switch focus of all future commands to the first frame on the page. @@ -2732,10 +2738,10 @@ on the page. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the driver has changed focus to the default content. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the driver has changed focus to the default content. -##[webdriver.WebDriver.TargetLocator.prototype.frame](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1376) +##[webdriver.WebDriver.TargetLocator.prototype.frame](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1403) Schedules a command to switch the focus of all future commands to another frame on the page. @@ -2767,10 +2773,10 @@ nameOrIndex | string|number | The frame locator. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the driver has changed focus to the specified frame. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the driver has changed focus to the specified frame. -##[webdriver.WebDriver.TargetLocator.prototype.window](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1402) +##[webdriver.WebDriver.TargetLocator.prototype.window](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1429) Schedules a command to switch the focus of all future commands to another window. Windows may be specified by their {@code window.name} attribute or @@ -2795,10 +2801,10 @@ nameOrHandle | string | The name or window handle of the window to switch focus Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the driver has changed focus to the specified window. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the driver has changed focus to the specified window. -##[webdriver.WebDriver.TargetLocator.prototype.alert](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1422) +##[webdriver.WebDriver.TargetLocator.prototype.alert](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1449) Schedules a command to change focus to the active alert dialog. This command will return a {@link bot.ErrorCode.NO_MODAL_DIALOG_OPEN} error if a modal @@ -2816,7 +2822,7 @@ Type | Description ![webdriver.Alert](#webdriveralert) | The open alert. -##[webdriver.Key.chord](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1436) +##[webdriver.Key.chord](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1463) Simulate pressing many keys at once in a "chord". Takes a sequence of {@link webdriver.Key}s or strings, appends each of the values to a string, @@ -2845,7 +2851,7 @@ Type | Description string | The null-terminated key sequence. -##[webdriver.WebElement](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1468) +##[webdriver.WebElement](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1495) Represents a DOM element. WebElements can be found by searching from the document root using a {@code webdriver.WebDriver} instance, or by searching @@ -2883,7 +2889,7 @@ id | !(string|webdriver.promise.Promise) | Either the opaque ID for the und -##[webdriver.WebElement.equals](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1550) +##[webdriver.WebElement.equals](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1585) Compares to WebElements for equality. @@ -2904,10 +2910,10 @@ b | ![webdriver.WebElement](#webdriverwebelement) | A WebElement. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved to whether the two WebElements are equal. +!webdriver.promise.Promise.<boolean> | A promise that will be resolved to whether the two WebElements are equal. -##[webdriver.WebElement.prototype.getDriver](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1578) +##[webdriver.WebElement.prototype.getDriver](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1613) @@ -2923,7 +2929,7 @@ Type | Description ![webdriver.WebDriver](#webdriverwebdriver) | The parent driver for this instance. -##[webdriver.WebElement.prototype.toWireValue](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1586) +##[webdriver.WebElement.prototype.toWireValue](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1621) @@ -2936,10 +2942,10 @@ Type | Description Type | Description --- | --- -!webdriver.promise.Promise | A promise that resolves to this element's JSON representation as defined by the WebDriver wire protocol. +!webdriver.promise.Promise.<webdriver.WebElement.Id> | A promise that resolves to this element's JSON representation as defined by the WebDriver wire protocol. -##[webdriver.WebElement.prototype.findElement](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1613) +##[webdriver.WebElement.prototype.findElement](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1650) Schedule a command to find a descendant of this element. If the element cannot be found, a {@code bot.ErrorCode.NO_SUCH_ELEMENT} result will @@ -2993,7 +2999,7 @@ Type | Description ![webdriver.WebElement](#webdriverwebelement) | A WebElement that can be used to issue commands against the located element. If the element is not found, the element will be invalidated and all scheduled commands aborted. -##[webdriver.WebElement.prototype.isElementPresent](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1669) +##[webdriver.WebElement.prototype.isElementPresent](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1706) Schedules a command to test if there is at least one descendant of this element that matches the given search criteria. @@ -3017,7 +3023,7 @@ Type | Description !webdriver.promise.Promise.<boolean> | A promise that will be resolved with whether an element could be located on the page. -##[webdriver.WebElement.prototype.findElements](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1685) +##[webdriver.WebElement.prototype.findElements](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1722) Schedules a command to find all of the descendants of this element that match the given search criteria. @@ -3041,7 +3047,7 @@ Type | Description !webdriver.promise.Promise.<!Array.<!webdriver.WebElement>> | A promise that will resolve to an array of WebElements. -##[webdriver.WebElement.prototype.click](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1708) +##[webdriver.WebElement.prototype.click](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1745) Schedules a command to click on this element. @@ -3054,10 +3060,10 @@ Schedules a command to click on this element. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the click command has completed. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the click command has completed. -##[webdriver.WebElement.prototype.sendKeys](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1720) +##[webdriver.WebElement.prototype.sendKeys](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1757) Schedules a command to type a sequence on the DOM element represented by this instance. @@ -3108,10 +3114,10 @@ var_args | ...string | The sequence of keys to type. All arguments will be joine Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when all keys have been typed. +!webdriver.promise.Promise.<void> | A promise that will be resolved when all keys have been typed. -##[webdriver.WebElement.prototype.getTagName](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1777) +##[webdriver.WebElement.prototype.getTagName](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1814) Schedules a command to query for the tag/node name of this element. @@ -3124,10 +3130,10 @@ Schedules a command to query for the tag/node name of this element. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the element's tag name. +!webdriver.promise.Promise.<string> | A promise that will be resolved with the element's tag name. -##[webdriver.WebElement.prototype.getCssValue](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1789) +##[webdriver.WebElement.prototype.getCssValue](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1826) Schedules a command to query for the computed style of the element represented by this instance. If the element inherits the named style from @@ -3154,10 +3160,10 @@ cssStyleProperty | string | The name of the CSS style property to look up. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the requested CSS value. +!webdriver.promise.Promise.<string> | A promise that will be resolved with the requested CSS value. -##[webdriver.WebElement.prototype.getAttribute](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1813) +##[webdriver.WebElement.prototype.getAttribute](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1850) Schedules a command to query for the value of the given attribute of the element. Will return the current value, even if it has been modified after @@ -3199,10 +3205,10 @@ attributeName | string | The name of the attribute to query. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the attribute's value. The returned value will always be either a string or null. +!webdriver.promise.Promise.<?string> | A promise that will be resolved with the attribute's value. The returned value will always be either a string or null. -##[webdriver.WebElement.prototype.getText](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1850) +##[webdriver.WebElement.prototype.getText](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1887) Get the visible (i.e. not hidden by CSS) innerText of this element, including sub-elements, without any leading or trailing whitespace. @@ -3216,10 +3222,10 @@ sub-elements, without any leading or trailing whitespace. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the element's visible text. +!webdriver.promise.Promise.<string> | A promise that will be resolved with the element's visible text. -##[webdriver.WebElement.prototype.getSize](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1863) +##[webdriver.WebElement.prototype.getSize](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1900) Schedules a command to compute the size of this element's bounding box, in pixels. @@ -3233,10 +3239,10 @@ pixels. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the element's size as a {@code {width:number, height:number}} object. +!webdriver.promise.Promise.<{width: number, height: number}> | A promise that will be resolved with the element's size as a {@code {width:number, height:number}} object. -##[webdriver.WebElement.prototype.getLocation](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1876) +##[webdriver.WebElement.prototype.getLocation](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1914) Schedules a command to compute the location of this element in page space. @@ -3249,10 +3255,10 @@ Schedules a command to compute the location of this element in page space. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved to the element's location as a {@code {x:number, y:number}} object. +!webdriver.promise.Promise.<{x: number, y: number}> | A promise that will be resolved to the element's location as a {@code {x:number, y:number}} object. -##[webdriver.WebElement.prototype.isEnabled](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1888) +##[webdriver.WebElement.prototype.isEnabled](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1927) Schedules a command to query whether the DOM element represented by this instance is enabled, as dicted by the {@code disabled} attribute. @@ -3266,10 +3272,10 @@ instance is enabled, as dicted by the {@code disabled} attribute. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with whether this element is currently enabled. +!webdriver.promise.Promise.<boolean> | A promise that will be resolved with whether this element is currently enabled. -##[webdriver.WebElement.prototype.isSelected](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1901) +##[webdriver.WebElement.prototype.isSelected](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1940) Schedules a command to query whether this element is selected. @@ -3282,10 +3288,10 @@ Schedules a command to query whether this element is selected. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with whether this element is currently selected. +!webdriver.promise.Promise.<boolean> | A promise that will be resolved with whether this element is currently selected. -##[webdriver.WebElement.prototype.submit](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1913) +##[webdriver.WebElement.prototype.submit](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1952) Schedules a command to submit the form containing this element (or this element if it is a FORM element). This command is a no-op if the element is @@ -3300,10 +3306,10 @@ not contained in a form. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the form has been submitted. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the form has been submitted. -##[webdriver.WebElement.prototype.clear](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1927) +##[webdriver.WebElement.prototype.clear](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1966) Schedules a command to clear the {@code value} of this element. This command has no effect if the underlying DOM element is neither a text INPUT element @@ -3318,10 +3324,10 @@ nor a TEXTAREA element. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when the element has been cleared. +!webdriver.promise.Promise.<void> | A promise that will be resolved when the element has been cleared. -##[webdriver.WebElement.prototype.isDisplayed](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1941) +##[webdriver.WebElement.prototype.isDisplayed](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1980) Schedules a command to test whether this element is currently displayed. @@ -3334,10 +3340,10 @@ Schedules a command to test whether this element is currently displayed. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with whether this element is currently visible on the page. +!webdriver.promise.Promise.<boolean> | A promise that will be resolved with whether this element is currently visible on the page. -##[webdriver.WebElement.prototype.getOuterHtml](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1953) +##[webdriver.WebElement.prototype.getOuterHtml](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1992) Schedules a command to retrieve the outer HTML of this element. @@ -3350,10 +3356,10 @@ Schedules a command to retrieve the outer HTML of this element. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the element's outer HTML. +!webdriver.promise.Promise.<string> | A promise that will be resolved with the element's outer HTML. -##[webdriver.WebElement.prototype.getInnerHtml](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1972) +##[webdriver.WebElement.prototype.getInnerHtml](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#2011) Schedules a command to retrieve the inner HTML of this element. @@ -3366,10 +3372,10 @@ Schedules a command to retrieve the inner HTML of this element. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with the element's inner HTML. +!webdriver.promise.Promise.<string> | A promise that will be resolved with the element's inner HTML. -##[webdriver.Alert](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#1983) +##[webdriver.Alert](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#2022) Represents a modal dialog such as {@code alert}, {@code confirm}, or {@code prompt}. Provides functions to retrieve the message displayed with @@ -3384,13 +3390,13 @@ case of {@code prompt}). Param | Type | Description --- | --- | --- driver | ![webdriver.WebDriver](#webdriverwebdriver) | The driver controlling the browser this alert is attached to. -text | !(string|webdriver.promise.Promise) | Either the message text displayed with this alert, or a promise that will be resolved to said text. +text | !(string|webdriver.promise.Promise.<string>) | Either the message text displayed with this alert, or a promise that will be resolved to said text. -##[webdriver.Alert.prototype.getText](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#2019) +##[webdriver.Alert.prototype.getText](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#2058) Retrieves the message text displayed with this alert. For instance, if the alert were opened with alert("hello"), then this would return "hello". @@ -3404,10 +3410,10 @@ alert were opened with alert("hello"), then this would return "hello". Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved to the text displayed with this alert. +!webdriver.promise.Promise.<string> | A promise that will be resolved to the text displayed with this alert. -##[webdriver.Alert.prototype.accept](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#2030) +##[webdriver.Alert.prototype.accept](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#2069) Accepts this alert. @@ -3420,10 +3426,10 @@ Accepts this alert. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when this command has completed. +!webdriver.promise.Promise.<void> | A promise that will be resolved when this command has completed. -##[webdriver.Alert.prototype.dismiss](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#2042) +##[webdriver.Alert.prototype.dismiss](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#2081) Dismisses this alert. @@ -3436,10 +3442,10 @@ Dismisses this alert. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when this command has completed. +!webdriver.promise.Promise.<void> | A promise that will be resolved when this command has completed. -##[webdriver.Alert.prototype.sendKeys](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#2054) +##[webdriver.Alert.prototype.sendKeys](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#2093) Sets the response text on this alert. This command will return an error if the underlying alert does not support response text (e.g. window.alert and @@ -3461,10 +3467,10 @@ text | string | The text to set. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved when this command has completed. +!webdriver.promise.Promise.<void> | A promise that will be resolved when this command has completed. -##[webdriver.UnhandledAlertError](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#2071) +##[webdriver.UnhandledAlertError](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#2110) An error returned to indicate that there is an unhandled modal dialog on the current page. @@ -3483,7 +3489,7 @@ alert | ![webdriver.Alert](#webdriveralert) | The alert handle. -##[webdriver.UnhandledAlertError.prototype.getAlert](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#2088) +##[webdriver.UnhandledAlertError.prototype.getAlert](https://code.google.com/p/selenium/source/browse/javascript/webdriver/webdriver.js#2127) From 7fc21678fa1d9a6abd5f79097b66705ab413bba0 Mon Sep 17 00:00:00 2001 From: Julie Date: Mon, 2 Jun 2014 15:15:55 -0700 Subject: [PATCH 006/612] refactor(runner): split framework adapters into their own files and add documentation Split out the mocha/cucumber/jasmine bits of the runner for better separation and readability. Closes #792 --- lib/frameworks/README.md | 26 +++++ lib/frameworks/cucumber.js | 69 +++++++++++ lib/frameworks/debugprint.js | 16 +++ lib/frameworks/jasmine.js | 53 +++++++++ lib/frameworks/mocha.js | 61 ++++++++++ lib/runner.js | 220 ++++------------------------------- spec/unit/runner_test.js | 2 +- 7 files changed, 246 insertions(+), 201 deletions(-) create mode 100644 lib/frameworks/README.md create mode 100644 lib/frameworks/cucumber.js create mode 100644 lib/frameworks/debugprint.js create mode 100644 lib/frameworks/jasmine.js create mode 100644 lib/frameworks/mocha.js diff --git a/lib/frameworks/README.md b/lib/frameworks/README.md new file mode 100644 index 000000000..6b7c35f78 --- /dev/null +++ b/lib/frameworks/README.md @@ -0,0 +1,26 @@ +Framework Adapters for Protractor +================================= + +Protractor can work with any test framework that is adapted here. + +Each file details the adapter for one test framework. Each file must export a `run` function with the interface: + +```js +/** + * @param {Runner} runner The Protractor runner instance. + * @param {Array.} specs A list of absolute filenames. + * @param {Function} done A callback for when tests are finished. + */ +exports.run = function(runner, specs, done) +``` + +Requirements +------------ + + - `runner.emit` must be called with `testPass` and `testFail` messages. + + - `runner.runTestPreparers` must be called before any tests are run. + + - `runner.getConfig().onComplete` must be called when tests are finished. + + - When finished, `done` must be invoked and passed a results object. This object must have a `failedCount` property. diff --git a/lib/frameworks/cucumber.js b/lib/frameworks/cucumber.js new file mode 100644 index 000000000..2203f9dfa --- /dev/null +++ b/lib/frameworks/cucumber.js @@ -0,0 +1,69 @@ +var ConfigParser = require('../configParser'); + +/** + * Execute the Runner's test cases through Cucumber. + * + * @param {Runner} runner The current Protractor Runner. + * @param {Array} specs Array of Directory Path Strings. + * @param done A callback for when tests are finished. + */ +exports.run = function(runner, specs, done) { + // TODO - add the event interface for cucumber. + var Cucumber = require('cucumber'), + execOptions = ['node', 'node_modules/.bin/cucumber-js'], + cucumberResolvedRequire; + + // Set up exec options for Cucumber + execOptions = execOptions.concat(specs); + if (runner.getConfig().cucumberOpts) { + + // Process Cucumber Require param + if (runner.getConfig().cucumberOpts.require) { + // TODO - this should move into the launcher. + cucumberResolvedRequire = + ConfigParser.resolveFilePatterns( + runner.getConfig().cucumberOpts.require, + false, + runner.getConfig().configDir); + if (cucumberResolvedRequire && cucumberResolvedRequire.length) { + execOptions = cucumberResolvedRequire.reduce(function(a, fn) { + return a.concat('-r', fn); + }, execOptions); + } + } + + // Process Cucumber Tag param + if (Array.isArray(runner.getConfig().cucumberOpts.tags)) { + for (var i in runner.getConfig().cucumberOpts.tags) { + var tags = runner.getConfig().cucumberOpts.tags[i]; + execOptions.push('-t'); + execOptions.push(tags); + } + } else if (runner.getConfig().cucumberOpts.tags) { + execOptions.push('-t'); + execOptions.push(runner.getConfig().cucumberOpts.tags); + } + + // Process Cucumber Format param + if (runner.getConfig().cucumberOpts.format) { + execOptions.push('-f'); + execOptions.push(runner.getConfig().cucumberOpts.format); + } + } + global.cucumber = Cucumber.Cli(execOptions); + + runner.controlFlow().execute(function() { + runner.runTestPreparers(); + }, 'run test preparers').then(function() { + + global.cucumber.run(function(succeeded) { + if (runner.getConfig().onComplete) { + runner.getConfig().onComplete(); + } + var resolvedObj = { + failedCount: succeeded ? 0 : 1 + }; + done(resolvedObj); + }); + }); +}; diff --git a/lib/frameworks/debugprint.js b/lib/frameworks/debugprint.js new file mode 100644 index 000000000..1c3ce1118 --- /dev/null +++ b/lib/frameworks/debugprint.js @@ -0,0 +1,16 @@ +var util = require('util'); + +/** + * A debug framework which does not actually run any tests, just spits + * out the list that would be run. + * + * @param {Runner} runner The current Protractor Runner. + * @param {Array} specs Array of Directory Path Strings. + * @param done A callback for when tests are finished. + */ +exports.run = function(runner, specs, done) { + console.log('Resolved spec files: ' + util.inspect(specs)); + done({ + failedCount: 0 + }); +}; diff --git a/lib/frameworks/jasmine.js b/lib/frameworks/jasmine.js new file mode 100644 index 000000000..a846f3037 --- /dev/null +++ b/lib/frameworks/jasmine.js @@ -0,0 +1,53 @@ +/** + * Execute the Runner's test cases through Jasmine. + * + * @param {Runner} runner The current Protractor Runner. + * @param {Array} specs Array of Directory Path Strings. + * @param done A callback for when tests are finished. + */ +exports.run = function(runner, specs, done) { + var minijn = require('minijasminenode'); + + require('../../jasminewd'); + /* global jasmine */ + + var RunnerReporter = function(emitter) { + this.emitter = emitter; + }; + + RunnerReporter.prototype.reportRunnerStarting = function() {}; + RunnerReporter.prototype.reportRunnerResults = function() {}; + RunnerReporter.prototype.reportSuiteResults = function() {}; + RunnerReporter.prototype.reportSpecStarting = function() {}; + RunnerReporter.prototype.reportSpecResults = function(spec) { + if (spec.results().passed()) { + this.emitter.emit('testPass'); + } else { + this.emitter.emit('testFail'); + } + }; + RunnerReporter.prototype.log = function() {}; + + // On timeout, the flow should be reset. This will prevent webdriver tasks + // from overflowing into the next test and causing it to fail or timeout + // as well. This is done in the reporter instead of an afterEach block + // to ensure that it runs after any afterEach() blocks with webdriver tasks + // get to complete first. + jasmine.getEnv().addReporter(new RunnerReporter(runner)); + + runner.controlFlow().execute(function() { + runner.runTestPreparers(); + }, 'run test preparers').then(function() { + var jasmineNodeOpts = runner.getConfig().jasmineNodeOpts; + var originalOnComplete = runner.getConfig().onComplete; + jasmineNodeOpts.onComplete = function(jasmineRunner, log) { + if (originalOnComplete) { + originalOnComplete(jasmineRunner, log); + } + done(jasmineRunner.results()); + }; + + minijn.addSpecs(specs); + minijn.executeSpecs(jasmineNodeOpts); + }); +}; diff --git a/lib/frameworks/mocha.js b/lib/frameworks/mocha.js new file mode 100644 index 000000000..e74554f30 --- /dev/null +++ b/lib/frameworks/mocha.js @@ -0,0 +1,61 @@ +/** + * Execute the Runner's test cases through Mocha. + * + * @param {Runner} runner The current Protractor Runner. + * @param {Array} specs Array of Directory Path Strings. + * @param done A callback for when tests are finished. + */ +exports.run = function(runner, specs, done) { + + var Mocha = require('mocha'), + mocha = new Mocha(runner.getConfig().mochaOpts); + + // Mocha doesn't set up the ui until the pre-require event, so + // wait until then to load mocha-webdriver adapters as well. + mocha.suite.on('pre-require', function() { + var mochaAdapters = require('selenium-webdriver/testing'); + global.after = mochaAdapters.after; + global.afterEach = mochaAdapters.afterEach; + global.before = mochaAdapters.before; + global.beforeEach = mochaAdapters.beforeEach; + + // The implementation of mocha's it.only uses global.it, so since that has + // already been wrapped we must avoid wrapping it a second time. + // See Mocha.prototype.loadFiles and bdd's context.it.only for more details. + var originalOnly = global.it.only; + global.it = mochaAdapters.it; + global.it.only = global.iit = originalOnly; + + global.it.skip = global.xit = mochaAdapters.xit; + }); + + mocha.suite.on('pass', function() { + runner.emit('testPass'); + }); + + mocha.suite.on('fail', function() { + runner.emit('testFail'); + }); + + mocha.loadFiles(); + + runner.controlFlow().execute(function() { + runner.runTestPreparers(); + }, 'run test preparers').then(function() { + + specs.forEach(function(file) { + mocha.addFile(file); + }); + + mocha.run(function(failures) { + if (runner.getConfig().onComplete) { + runner.getConfig().onComplete(); + } + var resolvedObj = { + failedCount: failures + }; + + done(resolvedObj); + }); + }); +}; diff --git a/lib/runner.js b/lib/runner.js index db6ca7baa..8f2dda5e2 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -1,6 +1,5 @@ var protractor = require('./protractor'), webdriver = require('selenium-webdriver'), - ConfigParser = require('./configParser'), path = require('path'), util = require('util'), q = require('q'), @@ -50,191 +49,6 @@ var Runner = function(config) { util.inherits(Runner, EventEmitter); -/** - * Execute the Runner's test cases through Jasmine. - * - * @private - * @param {Array} specs Array of Directory Path Strings - * @param done A callback for when tests are finished. - */ -Runner.prototype.runJasmine_ = function(specs, done) { - var minijn = require('minijasminenode'), - self = this; - - require('../jasminewd'); - /* global jasmine */ - - var RunnerReporter = function(emitter) { - this.emitter = emitter; - }; - - RunnerReporter.prototype.reportRunnerStarting = function() {}; - RunnerReporter.prototype.reportRunnerResults = function() {}; - RunnerReporter.prototype.reportSuiteResults = function() {}; - RunnerReporter.prototype.reportSpecStarting = function() {}; - RunnerReporter.prototype.reportSpecResults = function(spec) { - if (spec.results().passed()) { - this.emitter.emit('testPass'); - } else { - this.emitter.emit('testFail'); - } - }; - RunnerReporter.prototype.log = function() {}; - - // On timeout, the flow should be reset. This will prevent webdriver tasks - // from overflowing into the next test and causing it to fail or timeout - // as well. This is done in the reporter instead of an afterEach block - // to ensure that it runs after any afterEach() blocks with webdriver tasks - // get to complete first. - jasmine.getEnv().addReporter(new RunnerReporter(this)); - - webdriver.promise.controlFlow().execute(function() { - self.runTestPreparers_(); - }, 'run test preparers').then(function() { - var jasmineNodeOpts = self.config_.jasmineNodeOpts; - var originalOnComplete = self.config_.onComplete; - jasmineNodeOpts.onComplete = function(runner, log) { - if (originalOnComplete) { - originalOnComplete(runner, log); - } - done(runner.results()); - }; - - minijn.addSpecs(specs); - minijn.executeSpecs(jasmineNodeOpts); - }); -}; - - -/** - * Execute the Runner's test cases through Mocha. - * - * @private - * @param {Array} specs Array of Directory Path Strings - * @param done A callback for when tests are finished. - */ -Runner.prototype.runMocha_ = function(specs, done) { - - var Mocha = require('mocha'), - mocha = new Mocha(this.config_.mochaOpts), - self = this; - - // Mocha doesn't set up the ui until the pre-require event, so - // wait until then to load mocha-webdriver adapters as well. - mocha.suite.on('pre-require', function() { - var mochaAdapters = require('selenium-webdriver/testing'); - global.after = mochaAdapters.after; - global.afterEach = mochaAdapters.afterEach; - global.before = mochaAdapters.before; - global.beforeEach = mochaAdapters.beforeEach; - - // The implementation of mocha's it.only uses global.it, so since that has - // already been wrapped we must avoid wrapping it a second time. - // See Mocha.prototype.loadFiles and bdd's context.it.only for more details. - var originalOnly = global.it.only; - global.it = mochaAdapters.it; - global.it.only = global.iit = originalOnly; - - global.it.skip = global.xit = mochaAdapters.xit; - }); - - mocha.suite.on('pass', function() { - this.emit('testPass'); - }); - - mocha.suite.on('fail', function() { - this.emit('testFail'); - }); - - mocha.loadFiles(); - - webdriver.promise.controlFlow().execute(function() { - self.runTestPreparers_(); - }, 'run test preparers').then(function() { - - specs.forEach(function(file) { - mocha.addFile(file); - }); - - mocha.run(function(failures) { - if (self.config_.onComplete) { - self.config_.onComplete(); - } - var resolvedObj = { - failedCount: failures - }; - - done(resolvedObj); - }); - }); -}; - -/** - * Execute the Runner's test cases through Cucumber. - * - * @private - * @param {Array} specs Array of Directory Path Strings - * @param done A callback for when tests are finished. - */ -Runner.prototype.runCucumber_ = function(specs, done) { - // TODO - add the event interface for cucumber. - var Cucumber = require('cucumber'), - self = this, - execOptions = ['node', 'node_modules/.bin/cucumber-js'], - cucumberResolvedRequire; - - // Set up exec options for Cucumber - execOptions = execOptions.concat(specs); - if (self.config_.cucumberOpts) { - - // Process Cucumber Require param - if (self.config_.cucumberOpts.require) { - cucumberResolvedRequire = - ConfigParser.resolveFilePatterns(self.config_.cucumberOpts.require); - if (cucumberResolvedRequire && cucumberResolvedRequire.length) { - execOptions = cucumberResolvedRequire.reduce(function(a, fn) { - return a.concat('-r', fn); - }, execOptions); - } - } - - // Process Cucumber Tag param - if (Array.isArray(self.config_.cucumberOpts.tags)) { - for (var i in self.config_.cucumberOpts.tags) { - var tags = self.config_.cucumberOpts.tags[i]; - execOptions.push('-t'); - execOptions.push(tags); - } - } else if (self.config_.cucumberOpts.tags) { - execOptions.push('-t'); - execOptions.push(self.config_.cucumberOpts.tags); - } - - // Process Cucumber Format param - if (self.config_.cucumberOpts.format) { - execOptions.push('-f'); - execOptions.push(self.config_.cucumberOpts.format); - } - } - global.cucumber = Cucumber.Cli(execOptions); - - webdriver.promise.controlFlow().execute(function() { - self.runTestPreparers_(); - }, 'run test preparers').then(function() { - - global.cucumber.run(function(succeeded) { - if (self.config_.onComplete) { - self.config_.onComplete(); - } - var resolvedObj = { - failedCount: succeeded ? 0 : 1 - }; - done(resolvedObj); - }); - }); -}; - - /** * Internal helper for abstraction of polymorphic filenameOrFn properties. * @private @@ -271,9 +85,9 @@ Runner.prototype.registerTestPreparer = function(filenameOrFn) { /** * Executor of testPreparers - * @private + * @public */ -Runner.prototype.runTestPreparers_ = function() { +Runner.prototype.runTestPreparers = function() { this.runFilenamesOrFns_(this.preparers_); }; @@ -329,6 +143,15 @@ Runner.prototype.getConfig = function() { }; +/** + * Get the control flow used by this runner. + * @return {Object} WebDriver control flow. + */ +Runner.prototype.controlFlow = function() { + return webdriver.promise.controlFlow(); +}; + + /** * Sets up convenience globals for test specs * @private @@ -392,25 +215,22 @@ Runner.prototype.run = function() { // Do the framework setup here so that jasmine and mocha globals are // available to the onPrepare function. + var frameworkPath = ''; if (self.config_.framework === 'jasmine') { - self.runJasmine_.bind(self)(specs, function(result) { - deferred.resolve(result); - }); + frameworkPath = './frameworks/jasmine.js'; } else if (self.config_.framework === 'mocha') { - self.runMocha_.bind(self)(specs, function(result) { - deferred.resolve(result); - }); + frameworkPath = './frameworks/mocha.js'; } else if (self.config_.framework === 'cucumber') { - self.runCucumber_.bind(self)(specs, function(result) { - deferred.resolve(result); - }); - } else if (self.config_.framework === 'simpleprint') { - console.log('Resolved spec files ' + util.inspect(specs)); - deferred.resolve({failedCount: 0}); + frameworkPath = './frameworks/cucumber.js'; + } else if (self.config_.framework === 'debugprint') { + frameworkPath = './frameworks/debugprint.js'; } else { throw new Error('config.framework (' + self.config_.framework + ') is not a valid framework.'); } + require(frameworkPath).run(self, specs, function(result) { + deferred.resolve(result); + }); return deferred.promise; diff --git a/spec/unit/runner_test.js b/spec/unit/runner_test.js index 02bb300d3..6d7aa1944 100644 --- a/spec/unit/runner_test.js +++ b/spec/unit/runner_test.js @@ -44,7 +44,7 @@ describe('the Protractor runner', function() { it('should run', function(done) { var config = { specs: ['*.js'], - framework: 'simpleprint' + framework: 'debugprint' }; var exitCode; Runner.prototype.loadDriverProvider_ = function() { From 6533ea53e9c158db51f60219faf269239ad8b945 Mon Sep 17 00:00:00 2001 From: Julie Date: Tue, 3 Jun 2014 10:00:21 -0700 Subject: [PATCH 007/612] chore(package): fix dependency notation for request --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a844df3ee..686d162f7 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ ], "author": "Julie Ralph ", "dependencies": { - "request": "^2.36.0", + "request": "~2.36.0", "selenium-webdriver": "2.42.0", "minijasminenode": "0.4.0", "saucelabs": "~0.1.0", From f22456d3cf0768a577371776d716b8888a74397d Mon Sep 17 00:00:00 2001 From: Julie Date: Tue, 3 Jun 2014 14:31:50 -0700 Subject: [PATCH 008/612] refactor(jasminewd): use jasminewd from its own node module The Jasmine Webdriver Adapter is now its own npm module. The code has been moved to http://www.github.com/angular/jasminewd. Remove the code from Protractor, and add a dependency on jasminewd@1.0.0. --- jasminewd/index.js | 291 ---------------------------------- jasminewd/spec/adapterSpec.js | 219 ------------------------- lib/frameworks/jasmine.js | 2 +- package.json | 1 + scripts/test.js | 2 +- 5 files changed, 3 insertions(+), 512 deletions(-) delete mode 100644 jasminewd/index.js delete mode 100644 jasminewd/spec/adapterSpec.js diff --git a/jasminewd/index.js b/jasminewd/index.js deleted file mode 100644 index 026cfa72d..000000000 --- a/jasminewd/index.js +++ /dev/null @@ -1,291 +0,0 @@ -/** - * Adapts Jasmine-Node tests to work better with WebDriverJS. Borrows - * heavily from the mocha WebDriverJS adapter at - * https://code.google.com/p/selenium/source/browse/javascript/node/selenium-webdriver/testing/index.js - */ - -var webdriver = require('selenium-webdriver'); - -var flow = webdriver.promise.controlFlow(); - -/** - * Wraps a function so that all passed arguments are ignored. - * @param {!Function} fn The function to wrap. - * @return {!Function} The wrapped function. - */ -function seal(fn) { - return function() { - fn(); - }; -}; - -/** - * Validates that the parameter is a function. - * @param {Object} functionToValidate The function to validate. - * @throws {Error} - * @return {Object} The original parameter. - */ -function validateFunction(functionToValidate) { - if (functionToValidate && Object.prototype.toString.call(functionToValidate) === '[object Function]') { - return functionToValidate; - } else { - throw Error(functionToValidate + ' is not a function'); - } -} - -/** - * Validates that the parameter is a number. - * @param {Object} numberToValidate The number to validate. - * @throws {Error} - * @return {Object} The original number. - */ -function validateNumber(numberToValidate) { - if (!isNaN(numberToValidate)) { - return numberToValidate; - } else { - throw Error(numberToValidate + ' is not a number'); - } -} - -/** - * Validates that the parameter is a string. - * @param {Object} stringToValidate The string to validate. - * @throws {Error} - * @return {Object} The original string. - */ -function validateString(stringtoValidate) { - if (typeof stringtoValidate == 'string' || stringtoValidate instanceof String) { - return stringtoValidate; - } else { - throw Error(stringtoValidate + ' is not a string'); - } -} - -/** - * Wraps a function so it runs inside a webdriver.promise.ControlFlow and - * waits for the flow to complete before continuing. - * @param {!Function} globalFn The function to wrap. - * @return {!Function} The new function. - */ -function wrapInControlFlow(globalFn, fnName) { - return function() { - var driverError = new Error(); - driverError.stack = driverError.stack.replace(/ +at.+jasminewd.+\n/, ''); - - function asyncTestFn(fn, desc) { - return function(done) { - var desc_ = 'Asynchronous test function: ' + fnName + '('; - if (desc) { - desc_ += '"' + desc + '"'; - } - desc_ += ')'; - - // deferred object for signaling completion of asychronous function within globalFn - var asyncFnDone = webdriver.promise.defer(); - - if (fn.length == 0) { - // function with globalFn not asychronous - asyncFnDone.fulfill(); - } else if (fn.length > 1) { - throw Error('Invalid # arguments (' + fn.length + ') within function "' + fnName +'"'); - } - - var flowFinished = flow.execute(function() { - fn.call(jasmine.getEnv().currentSpec, function(userError) { - if (userError) { - asyncFnDone.reject(new Error(userError)); - } else { - asyncFnDone.fulfill(); - } - }); - }, desc_); - - webdriver.promise.all([asyncFnDone, flowFinished]).then(function() { - seal(done)(); - }, function(e) { - e.stack = e.stack + '==== async task ====\n' + driverError.stack; - done(e); - }); - }; - }; - - var description, func, timeout; - switch (fnName) { - case 'it': - case 'iit': - description = validateString(arguments[0]); - func = validateFunction(arguments[1]); - if (!arguments[2]) { - globalFn(description, asyncTestFn(func)); - } else { - timeout = validateNumber(arguments[2]); - globalFn(description, asyncTestFn(func), timeout); - } - break; - case 'beforeEach': - case 'afterEach': - func = validateFunction(arguments[0]); - if (!arguments[1]) { - globalFn(asyncTestFn(func)); - } else { - timeout = validateNumber(arguments[1]); - globalFn(asyncTestFn(func), timeout); - } - break; - default: - throw Error('invalid function: ' + fnName); - } - }; -}; - -global.it = wrapInControlFlow(global.it, 'it'); -global.iit = wrapInControlFlow(global.iit, 'iit'); -global.beforeEach = wrapInControlFlow(global.beforeEach, 'beforeEach'); -global.afterEach = wrapInControlFlow(global.afterEach, 'afterEach'); - - -/** - * Wrap a Jasmine matcher function so that it can take webdriverJS promises. - * @param {!Function} matcher The matcher function to wrap. - * @param {webdriver.promise.Promise} actualPromise The promise which will - * resolve to the actual value being tested. - * @param {boolean} not Whether this is being called with 'not' active. - */ -function wrapMatcher(matcher, actualPromise, not) { - return function() { - var originalArgs = arguments; - var matchError = new Error("Failed expectation"); - matchError.stack = matchError.stack.replace(/ +at.+jasminewd.+\n/, ''); - actualPromise.then(function(actual) { - var expected = originalArgs[0]; - - var expectation = expect(actual); - if (not) { - expectation = expectation.not; - } - var originalAddMatcherResult = expectation.spec.addMatcherResult; - var error = matchError; - expectation.spec.addMatcherResult = function(result) { - result.trace = error; - jasmine.Spec.prototype.addMatcherResult.call(this, result); - }; - - if (expected instanceof webdriver.promise.Promise) { - if (originalArgs.length > 1) { - throw error('Multi-argument matchers with promises are not ' - + 'supported.'); - } - expected.then(function(exp) { - expectation[matcher].apply(expectation, [exp]); - expectation.spec.addMatcherResult = originalAddMatcherResult; - }); - } else { - expectation.spec.addMatcherResult = function(result) { - result.trace = error; - originalAddMatcherResult.call(this, result); - } - expectation[matcher].apply(expectation, originalArgs); - expectation.spec.addMatcherResult = originalAddMatcherResult; - } - }); - }; -}; - -/** - * Return a chained set of matcher functions which will be evaluated - * after actualPromise is resolved. - * @param {webdriver.promise.Promise} actualPromise The promise which will - * resolve to the acutal value being tested. - */ -function promiseMatchers(actualPromise) { - var promises = {not: {}}; - var env = jasmine.getEnv(); - var matchersClass = env.currentSpec.matchersClass || env.matchersClass; - - for (matcher in matchersClass.prototype) { - promises[matcher] = wrapMatcher(matcher, actualPromise, false); - promises.not[matcher] = wrapMatcher(matcher, actualPromise, true); - }; - - return promises; -}; - -var originalExpect = global.expect; - -global.expect = function(actual) { - if (actual instanceof webdriver.promise.Promise) { - if (actual instanceof webdriver.WebElement) { - throw 'expect called with WebElement argument, expected a Promise. ' + - 'Did you mean to use .getText()?'; - } - return promiseMatchers(actual); - } else { - return originalExpect(actual); - } -}; - -// Wrap internal Jasmine function to allow custom matchers -// to return promises that resolve to truthy or falsy values -var originalMatcherFn = jasmine.Matchers.matcherFn_; -jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { - var matcherFnThis = this; - var matcherFnArgs = jasmine.util.argsToArray(arguments); - return function() { - var matcherThis = this; - var matcherArgs = jasmine.util.argsToArray(arguments); - var result = matcherFunction.apply(this, arguments); - - if (result instanceof webdriver.promise.Promise) { - result.then(function(resolution) { - matcherFnArgs[1] = function() { - return resolution; - }; - originalMatcherFn.apply(matcherFnThis, matcherFnArgs). - apply(matcherThis, matcherArgs); - }); - } else { - originalMatcherFn.apply(matcherFnThis, matcherFnArgs). - apply(matcherThis, matcherArgs); - } - }; -}; - -/** - * A Jasmine reporter which does nothing but execute the input function - * on a timeout failure. - */ -var OnTimeoutReporter = function(fn) { - this.callback = fn; -}; - -OnTimeoutReporter.prototype.reportRunnerStarting = function() {}; -OnTimeoutReporter.prototype.reportRunnerResults = function() {}; -OnTimeoutReporter.prototype.reportSuiteResults = function() {}; -OnTimeoutReporter.prototype.reportSpecStarting = function() {}; -OnTimeoutReporter.prototype.reportSpecResults = function(spec) { - if (!spec.results().passed()) { - var result = spec.results(); - var failureItem = null; - - var items_length = result.getItems().length; - for (var i = 0; i < items_length; i++) { - if (result.getItems()[i].passed_ === false) { - failureItem = result.getItems()[i]; - - if (failureItem.toString().match(/timeout/)) { - this.callback(); - } - } - } - } -}; -OnTimeoutReporter.prototype.log = function() {}; - -// On timeout, the flow should be reset. This will prevent webdriver tasks -// from overflowing into the next test and causing it to fail or timeout -// as well. This is done in the reporter instead of an afterEach block -// to ensure that it runs after any afterEach() blocks with webdriver tasks -// get to complete first. -jasmine.getEnv().addReporter(new OnTimeoutReporter(function() { - flow.reset(); -})); diff --git a/jasminewd/spec/adapterSpec.js b/jasminewd/spec/adapterSpec.js deleted file mode 100644 index beb132f42..000000000 --- a/jasminewd/spec/adapterSpec.js +++ /dev/null @@ -1,219 +0,0 @@ -require('../index.js'); -var webdriver = require('selenium-webdriver'); - -/** - * Tests for the WebDriverJS Jasmine-Node Adapter. These tests use - * WebDriverJS's control flow and promises without setting up the whole - * webdriver. - */ - -var getFakeDriver = function() { - var flow = webdriver.promise.controlFlow(); - return { - controlFlow: function() { - return flow; - }, - sleep: function(ms) { - return flow.timeout(ms); - }, - setUp: function() { - return flow.execute(function() { - return webdriver.promise.fulfilled('setup done'); - }); - }, - getValueA: function() { - return flow.execute(function() { - return webdriver.promise.delayed(500).then(function() { - return webdriver.promise.fulfilled('a'); - }); - }); - }, - getOtherValueA: function() { - return flow.execute(function() { - return webdriver.promise.fulfilled('a'); - }); - }, - getValueB: function() { - return flow.execute(function() { - return webdriver.promise.fulfilled('b'); - }); - }, - getBigNumber: function() { - return flow.execute(function() { - return webdriver.promise.fulfilled(1111); - }); - }, - getDecimalNumber: function() { - return flow.execute(function() { - return webdriver.promise.fulfilled(3.14159); - }); - }, - getDisplayedElement: function() { - return flow.execute(function() { - return webdriver.promise.fulfilled({ - isDisplayed: function() { - return webdriver.promise.fulfilled(true); - } - }); - }); - }, - getHiddenElement: function() { - return flow.execute(function() { - return webdriver.promise.fulfilled({ - isDisplayed: function() { - return webdriver.promise.fulfilled(false); - } - }); - }); - } - }; -}; - -var fakeDriver = getFakeDriver(); - -describe('webdriverJS Jasmine adapter plain', function() { - it('should pass normal synchronous tests', function() { - expect(true).toBe(true); - }); -}); - - -describe('webdriverJS Jasmine adapter', function() { - // Shorten this and you should see tests timing out. - jasmine.getEnv().defaultTimeoutInterval = 2000; - - beforeEach(function() { - // 'this' should work properly to add matchers. - this.addMatchers({ - toBeLotsMoreThan: function(expected) { - return this.actual > expected + 100; - }, - // Example custom matcher returning a promise that resolves to true/false. - toBeDisplayed: function() { - return this.actual.isDisplayed(); - } - }); - }); - - beforeEach(function() { - fakeDriver.setUp().then(function(value) { - console.log('This should print before each test: ' + value); - }); - }); - - it('should pass normal synchronous tests', function() { - expect(true).toEqual(true); - }); - - it('should compare a promise to a primitive', function() { - expect(fakeDriver.getValueA()).toEqual('a'); - expect(fakeDriver.getValueB()).toEqual('b'); - }); - - it('should wait till the expect to run the flow', function() { - var promiseA = fakeDriver.getValueA(); - expect(promiseA.isPending()).toBe(true); - expect(promiseA).toEqual('a'); - expect(promiseA.isPending()).toBe(true); - }) - - it('should compare a promise to a promise', function() { - expect(fakeDriver.getValueA()).toEqual(fakeDriver.getOtherValueA()); - }); - - it('should still allow use of the underlying promise', function() { - var promiseA = fakeDriver.getValueA(); - promiseA.then(function(value) { - expect(value).toEqual('a'); - }); - }); - - it('should allow scheduling of tasks', function() { - fakeDriver.sleep(300); - expect(fakeDriver.getValueB()).toEqual('b'); - }); - - it('should allow the use of custom matchers', function() { - expect(500).toBeLotsMoreThan(3); - expect(fakeDriver.getBigNumber()).toBeLotsMoreThan(33); - }); - - it('should allow custom matchers to return a promise', function() { - expect(fakeDriver.getDisplayedElement()).toBeDisplayed(); - expect(fakeDriver.getHiddenElement()).not.toBeDisplayed(); - }); - - it('should pass multiple arguments to matcher', function() { - // Passing specific precision - expect(fakeDriver.getDecimalNumber()).toBeCloseTo(3.1, 1); - expect(fakeDriver.getDecimalNumber()).not.toBeCloseTo(3.1, 2); - - // Using default precision (2) - expect(fakeDriver.getDecimalNumber()).not.toBeCloseTo(3.1); - expect(fakeDriver.getDecimalNumber()).toBeCloseTo(3.14); - }); - - describe('not', function() { - it('should still pass normal synchronous tests', function() { - expect(4).not.toEqual(5); - }); - - it('should compare a promise to a primitive', function() { - expect(fakeDriver.getValueA()).not.toEqual('b'); - }); - - it('should compare a promise to a promise', function() { - expect(fakeDriver.getValueA()).not.toEqual(fakeDriver.getValueB()); - }); - }); - - it('should throw an error with a WebElement actual value', function() { - var webElement = new webdriver.WebElement(fakeDriver, 'idstring'); - - expect(function() { - expect(webElement).toEqual(4); - }).toThrow('expect called with WebElement argument, expected a Promise. ' + - 'Did you mean to use .getText()?'); - }); - - // Uncomment to see timeout failures. - // it('should timeout after 200ms', function() { - // expect(fakeDriver.getValueA()).toEqual('a'); - // }, 200); - - // it('should timeout after 300ms', function() { - // fakeDriver.sleep(9999); - // expect(fakeDriver.getValueB()).toEqual('b'); - // }, 300); - - // it('should pass errors from done callback', function(done) { - // done('an error'); - // }); - - it('should pass after the timed out tests', function() { - expect(true).toEqual(true); - }); - - describe('should work for both synchronous and asynchronous tests', function() { - var x; - - beforeEach(function() { - x = 0; - }); - - afterEach(function() { - expect(x).toBe(1); - }); - - it('should execute a synchronous test', function() { - x = 1; - }); - - it('should execute an asynchronous test', function(done) { - setTimeout(function(){ - x = 1; - done(); - }, 500); - }); - }); -}); diff --git a/lib/frameworks/jasmine.js b/lib/frameworks/jasmine.js index a846f3037..9d04be363 100644 --- a/lib/frameworks/jasmine.js +++ b/lib/frameworks/jasmine.js @@ -8,7 +8,7 @@ exports.run = function(runner, specs, done) { var minijn = require('minijasminenode'); - require('../../jasminewd'); + require('jasminewd'); /* global jasmine */ var RunnerReporter = function(emitter) { diff --git a/package.json b/package.json index 686d162f7..f278f0f86 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "request": "~2.36.0", "selenium-webdriver": "2.42.0", "minijasminenode": "0.4.0", + "jasminewd": "1.0.0", "saucelabs": "~0.1.0", "glob": "~3.2", "adm-zip": "0.4.4", diff --git a/scripts/test.js b/scripts/test.js index 2b62f169d..13d3b7e05 100755 --- a/scripts/test.js +++ b/scripts/test.js @@ -18,7 +18,7 @@ var scripts = [ ]; scripts.push( - 'node node_modules/.bin/minijasminenode jasminewd/spec/adapterSpec.js ' + + 'node node_modules/.bin/minijasminenode ' + glob('spec/unit/*.js').join(' ') + ' ' + glob('docs/spec/*.js').join(' ')); From cc82a1daf0c0ba4501eb394d2add642198e8864e Mon Sep 17 00:00:00 2001 From: Julie Date: Tue, 3 Jun 2014 15:19:59 -0700 Subject: [PATCH 009/612] docs(driverprovider): add documentation for the requirements of driverproviders Also, remove the redundant `.dp` in their filenames. --- lib/driverProviders/README.md | 42 +++++++++++++++++++ .../{chrome.dp.js => chrome.js} | 0 .../{hosted.dp.js => hosted.js} | 0 lib/driverProviders/{local.dp.js => local.js} | 0 lib/driverProviders/{sauce.dp.js => sauce.js} | 0 lib/runner.js | 10 ++--- 6 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 lib/driverProviders/README.md rename lib/driverProviders/{chrome.dp.js => chrome.js} (100%) rename lib/driverProviders/{hosted.dp.js => hosted.js} (100%) rename lib/driverProviders/{local.dp.js => local.js} (100%) rename lib/driverProviders/{sauce.dp.js => sauce.js} (100%) diff --git a/lib/driverProviders/README.md b/lib/driverProviders/README.md new file mode 100644 index 000000000..29f676b70 --- /dev/null +++ b/lib/driverProviders/README.md @@ -0,0 +1,42 @@ +WebDriver Providers for Protractor +================================== + +DriverProviders define ways that the Protractor runner can connect to +WebDriver. + +Each file exports a function which takes in the configuration as a parameter and returns a new DriverProvider, which has the following interface: + +```js +/** + * @return {q.promise} A promise which will resolve when the environment is + * ready to test. + */ +DriverProvider.prototype.setupEnv = function(runner, specs, done) + +/** + * @return {webdriver.WebDriver} The setup driver instance. + */ +DriverProvider.prototype.getDriver() + +/** + * @return {q.promise} A promise which will resolve when the environment + * is down. + */ +DriverProvider.prototype.teardownEnv() + +/** + * This is an optional function. If defined, it will be called with the final + * status of the test suite (pass/fail) once it is completed. + * + * @param {{passed: boolean}} + * @return {q.promise} A promise that will resolve when the update is complete. + */ +DriverProvider.prototype.updateJob() +``` + +Requirements +------------ + + - `setupEnv` and `getDriver` will be called before the test framework is loaded, so any pre-work which might cause timeouts on the first test should be done there. + + - `teardownEnv` should call the driver's `quit` method. diff --git a/lib/driverProviders/chrome.dp.js b/lib/driverProviders/chrome.js similarity index 100% rename from lib/driverProviders/chrome.dp.js rename to lib/driverProviders/chrome.js diff --git a/lib/driverProviders/hosted.dp.js b/lib/driverProviders/hosted.js similarity index 100% rename from lib/driverProviders/hosted.dp.js rename to lib/driverProviders/hosted.js diff --git a/lib/driverProviders/local.dp.js b/lib/driverProviders/local.js similarity index 100% rename from lib/driverProviders/local.dp.js rename to lib/driverProviders/local.js diff --git a/lib/driverProviders/sauce.dp.js b/lib/driverProviders/sauce.js similarity index 100% rename from lib/driverProviders/sauce.dp.js rename to lib/driverProviders/sauce.js diff --git a/lib/runner.js b/lib/runner.js index 8f2dda5e2..1f3100e78 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -106,15 +106,15 @@ Runner.prototype.runTestPreparers = function() { Runner.prototype.loadDriverProvider_ = function() { var runnerPath; if (this.config_.chromeOnly) { - runnerPath = './driverProviders/chrome.dp'; + runnerPath = './driverProviders/chrome'; } else if (this.config_.seleniumAddress) { - runnerPath = './driverProviders/hosted.dp'; + runnerPath = './driverProviders/hosted'; } else if (this.config_.sauceUser && this.config_.sauceKey) { - runnerPath = './driverProviders/sauce.dp'; + runnerPath = './driverProviders/sauce'; } else if (this.config_.seleniumServerJar) { - runnerPath = './driverProviders/local.dp'; + runnerPath = './driverProviders/local'; } else { - runnerPath = './driverProviders/local.dp'; + runnerPath = './driverProviders/local'; } this.driverprovider_ = require(runnerPath)(this.config_); From 5bb29943697b61dfab3ca2a990f543b1980afa9b Mon Sep 17 00:00:00 2001 From: Julie Date: Tue, 3 Jun 2014 15:37:52 -0700 Subject: [PATCH 010/612] docs(driverprovider): remove parameters from doc --- lib/driverProviders/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/driverProviders/README.md b/lib/driverProviders/README.md index 29f676b70..9ed964c18 100644 --- a/lib/driverProviders/README.md +++ b/lib/driverProviders/README.md @@ -11,18 +11,18 @@ Each file exports a function which takes in the configuration as a parameter and * @return {q.promise} A promise which will resolve when the environment is * ready to test. */ -DriverProvider.prototype.setupEnv = function(runner, specs, done) +DriverProvider.prototype.setupEnv /** * @return {webdriver.WebDriver} The setup driver instance. */ -DriverProvider.prototype.getDriver() +DriverProvider.prototype.getDriver /** * @return {q.promise} A promise which will resolve when the environment * is down. */ -DriverProvider.prototype.teardownEnv() +DriverProvider.prototype.teardownEnv /** * This is an optional function. If defined, it will be called with the final @@ -31,7 +31,7 @@ DriverProvider.prototype.teardownEnv() * @param {{passed: boolean}} * @return {q.promise} A promise that will resolve when the update is complete. */ -DriverProvider.prototype.updateJob() +DriverProvider.prototype.updateJob ``` Requirements From e4600aff0f5535ab724ee033296f7387e4a0427a Mon Sep 17 00:00:00 2001 From: Julie Date: Wed, 4 Jun 2014 10:15:54 -0700 Subject: [PATCH 011/612] tests(driverprovider): update driver provider medium tests with new filenames --- spec/driverprovider_test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/driverprovider_test.js b/spec/driverprovider_test.js index f68ff3426..d4097f32c 100644 --- a/spec/driverprovider_test.js +++ b/spec/driverprovider_test.js @@ -44,7 +44,7 @@ var chromeConfig = { browserName: 'chrome' } }; -testDriverProvider(require('../lib/driverProviders/chrome.dp')(chromeConfig)). +testDriverProvider(require('../lib/driverProviders/chrome')(chromeConfig)). then(function() { console.log('chrome.dp working!'); }, function(err) { @@ -57,7 +57,7 @@ var hostedConfig = { browserName: 'firefox' } }; -testDriverProvider(require('../lib/driverProviders/hosted.dp')(hostedConfig)). +testDriverProvider(require('../lib/driverProviders/hosted')(hostedConfig)). then(function() { console.log('hosted.dp working!'); }, function(err) { @@ -70,7 +70,7 @@ var localConfig = { browserName: 'chrome' } }; -testDriverProvider(require('../lib/driverProviders/local.dp')(localConfig)). +testDriverProvider(require('../lib/driverProviders/local')(localConfig)). then(function() { console.log('local.dp working!'); }, function(err) { @@ -85,7 +85,7 @@ if (argv.sauceUser && argv.sauceKey) { browserName: 'chrome' } }; - testDriverProvider(require('../lib/driverProviders/sauce.dp')(sauceConfig)). + testDriverProvider(require('../lib/driverProviders/sauce')(sauceConfig)). then(function() { console.log('sauce.dp working!'); }, function(err) { From fbfc72bad15667990232bb9ff1da503e03d16230 Mon Sep 17 00:00:00 2001 From: Hank Duan Date: Wed, 4 Jun 2014 10:50:36 -0700 Subject: [PATCH 012/612] feat(launcher): Add support for maxSession - add support for maxSession and capability-specific specs - cleaned up launcher (refactored out taskScheduler.js) - (breaking change) changed the config to shard test files; also sharding is specific to capabilities now - Before: config.splitTestsBetweenCapabilities - Now: config.capabilities.shardTestFiles or config.multiCapabilities[index].shardTestFiles --- lib/configParser.js | 4 - lib/launcher.js | 452 ++++++++++---------- lib/taskScheduler.js | 158 +++++++ referenceConf.js | 24 +- scripts/test.js | 2 +- spec/{multiSplitConf.js => shardingConf.js} | 20 +- spec/unit/config_test.js | 4 +- spec/unit/data/config.js | 2 +- spec/unit/data/fakespecC.js | 1 + spec/unit/taskScheduler_test.js | 224 ++++++++++ 10 files changed, 640 insertions(+), 251 deletions(-) create mode 100644 lib/taskScheduler.js rename spec/{multiSplitConf.js => shardingConf.js} (57%) create mode 100644 spec/unit/data/fakespecC.js create mode 100644 spec/unit/taskScheduler_test.js diff --git a/lib/configParser.js b/lib/configParser.js index 98e03bffc..a71b7e95f 100644 --- a/lib/configParser.js +++ b/lib/configParser.js @@ -22,10 +22,6 @@ var ConfigParser = function() { // Default configuration. this.config_= { specs: [], - capabilities: { - browserName: 'chrome' - }, - splitTestsBetweenCapabilities: false, multiCapabilities: [], rootElement: 'body', allScriptsTimeout: 11000, diff --git a/lib/launcher.js b/lib/launcher.js index 5a1e8bdb9..5ebde181e 100644 --- a/lib/launcher.js +++ b/lib/launcher.js @@ -5,7 +5,8 @@ 'use strict'; var child = require('child_process'), - ConfigParser = require('./configParser'); + ConfigParser = require('./configParser'), + TaskScheduler = require('./taskScheduler'); var launcherPrefix = '[launcher] '; @@ -13,9 +14,6 @@ var log_ = function(stuff) { console.log(launcherPrefix + stuff); }; -var noLineLog_ = function(stuff) { - process.stdout.write(launcherPrefix + stuff); -}; /** * Initialize and run the tests. * @@ -24,12 +22,7 @@ var noLineLog_ = function(stuff) { */ var init = function(configFile, additionalConfig) { - // capabilities = array that holds one array per selected capability (Chrome, FF, Canary, ...) - // This array holds all the instances of the capability - var capabilities = [], - launcherExitCode = 0, - allSpecs, - excludes; + var launcherExitCode = 0; var configParser = new ConfigParser(); if (configFile) { @@ -39,205 +32,102 @@ var init = function(configFile, additionalConfig) { configParser.addConfig(additionalConfig); } var config = configParser.getConfig(); - - var countDriverInstances = function() { - var count = 0; - capabilities.forEach(function(capabilityDriverInstances) { - count += capabilityDriverInstances.length; - }); - return count; - }; - var countRunningDriverInstances = function() { - var count = 0; - capabilities.forEach(function(capabilityDriverInstances) { - capabilityDriverInstances.forEach(function(driverInstance) { - if (!driverInstance.done) { - count += 1; - } - }); - }); - return count; - }; - - var listRemainingForks = function() { - var remaining = countRunningDriverInstances(); - if (remaining) { - noLineLog_(remaining + ' instance(s) of WebDriver still running'); - } - }; - - var logSummary = function() { - capabilities.forEach(function(capabilityDriverInstances) { - capabilityDriverInstances.forEach(function(driverInstance) { - var shortChildName = driverInstance.capability.browserName + - (driverInstance.runNumber ? ' #' + driverInstance.runNumber : ''); - if (driverInstance.failedCount) { - log_(shortChildName + ' failed ' + driverInstance.failedCount + ' test(s)'); - } else { - log_(shortChildName + ' passed'); - } - }); - }); - }; - - if (config.multiCapabilities.length) { - if (config.debug) { - throw new Error('Cannot run in debug mode with multiCapabilities'); - } - log_('Running using config.multiCapabilities - ' + - 'config.capabilities will be ignored'); - } - // Use capabilities if multiCapabilities is empty. - if (!config.multiCapabilities.length) { - config.multiCapabilities = [config.capabilities]; - } - - var Fork = function(configFile, additionalConfig, capability, specs, runNumber, single) { - var silent = single ? false: true; - - this.configFile = configFile; - this.additionalConfig = additionalConfig; - this.capability = capability; - this.runNumber = runNumber; - this.single = single; - this.specs = specs; - + var scheduler = new TaskScheduler(config); + + /** + * A fork of a runner for running a specified task. The RunnerFork will + * start a new process that calls on '/runFromLauncher.js' and report the + * result to a reporter. + * + * @constructor + * @param {object} task Task to run. + */ + var RunnerFork = function(task) { + this.capability = task.capability; + this.specs = task.specs; this.process = child.fork( __dirname + '/runFromLauncher.js', - process.argv.slice(2),{ + process.argv.slice(2), { cwd: process.cwd(), - silent: silent + silent: true } ); + this.reporter = reporter.addTaskReporter(task, this.process.pid); }; - Fork.prototype.reportHeader_ = function() { - var capability = this.capability; - var eol = require('os').EOL; - var outputHeader = eol + '------------------------------------' + eol; - outputHeader += 'PID: ' + this.process.pid + ' (capability: '; - outputHeader += (capability.browserName) ? - capability.browserName : ''; - outputHeader += (capability.version) ? - capability.version : ''; - outputHeader += (capability.platform) ? - capability.platform : ''; - outputHeader += (this.runNumber) ? - ' #' + this.runNumber : ''; - outputHeader += ')' + eol; - if (config.splitTestsBetweenCapabilities) { - outputHeader += 'Specs: '+ this.specs.toString() + eol; - } - outputHeader += '------------------------------------' + eol; - - - console.log(outputHeader); - }; - - // If we're launching multiple runners, aggregate output until completion. - // Otherwise, there is a single runner, let's pipe the output straight - // through to maintain realtime reporting. - Fork.prototype.addEventHandlers = function(testsDoneCallback) { + /** + * Add handlers for the RunnerFork for events like stdout, stderr, testsDone, + * testPass, testFail, error, and exit. Optionally, you can pass in a + * callback function to be called when a test completes. + * + * @param {function()} testsDoneCallback Callback function for testsDone events. + */ + RunnerFork.prototype.addEventHandlers = function(testsDoneCallback) { var self = this; - if (this.single) { - this.process.on('error', function(err) { - log_('Runner Process(' + self.process.pid + ') Error: ' + err); - }); - this.process.on('message', function(m) { - switch (m.event) { - case 'testsDone': - this.failedCount = m.failedCount; - break; - } - }); - - this.process.on('exit', function(code) { - if (code) { - log_('Runner Process Exited With Error Code: ' + code); - launcherExitCode = 1; - } - }); - } else { - // Multiple capabilities and/or instances - this.output = ''; - - // stdin pipe - this.process.stdout.on('data', function(chunk) { - self.output += chunk; - }); + // stdout pipe + this.process.stdout.on('data', function(chunk) { + self.reporter.logStdout(chunk); + }); - // stderr pipe - this.process.stderr.on('data', function(chunk) { - self.output += chunk; - }); + // stderr pipe + this.process.stderr.on('data', function(chunk) { + self.reporter.logStderr(chunk); + }); - this.process.on('message', function(m) { - switch (m.event) { - case 'testPass': - process.stdout.write('.'); - break; - case 'testFail': - process.stdout.write('F'); - break; - case 'testsDone': - self.failedCount = m.failedCount; - if (typeof testsDoneCallback === 'function') { - testsDoneCallback(); - } - break; - } - }); + this.process.on('message', function(m) { + switch (m.event) { + case 'testPass': + process.stdout.write('.'); + break; + case 'testFail': + process.stdout.write('F'); + break; + case 'testsDone': + self.reporter.testsDone(m.failedCount); + if (typeof testsDoneCallback === 'function') { + testsDoneCallback(); + } + break; + } + }); - // err handler - this.process.on('error', function(err) { - log_('Runner Process(' + self.process.pid + ') Error: ' + err); - }); + this.process.on('error', function(err) { + log_('Runner Process(' + self.process.pid + ') Error: ' + err); + launcherExitCode = 1; + }); - // exit handlers - this.process.on('exit', function(code) { - if (code) { - log_('Runner Process Exited With Error Code: ' + code); - launcherExitCode = 1; - } - self.reportHeader_(); - console.log(self.output); - self.done = true; - listRemainingForks(); - }); - } + this.process.on('exit', function(code) { + if (code) { + log_('Runner Process Exited With Error Code: ' + code); + launcherExitCode = 1; + } + log_(scheduler.countActiveTasks() + + ' instance(s) of WebDriver still running'); + }); }; - Fork.prototype.run = function() { + /** + * Sends the run command. + */ + RunnerFork.prototype.run = function() { this.process.send({ command: 'run', - configFile: this.configFile, - additionalConfig: this.additionalConfig, + configFile: configFile, + additionalConfig: additionalConfig, capability: this.capability, specs: this.specs }); + this.reporter.reportHeader_(); }; - excludes = ConfigParser.resolveFilePatterns( config.exclude, true, config.configDir); - - allSpecs = ConfigParser.resolveFilePatterns( - ConfigParser.getSpecs(config), false, config.configDir).filter(function(path) { - return excludes.indexOf(path) < 0; - }); - - // If there is a single capability with a single instance, avoid starting a separate process - // and print output directly. - // Otherwise, if we're launching multiple runners, aggregate output until - // completion. - if (config.multiCapabilities.length === 1 - && (config.multiCapabilities[0].count === 1 || !config.multiCapabilities[0].count)) { + // Don't start new process if there is only 1 task. + var totalTasks = scheduler.numTasksRemaining(); + if (totalTasks === 1) { var Runner = require('./runner'); - capabilities[0] = [{ - capability: config.multiCapabilities[0], - runNumber: 0 - }]; - config.capabilities = capabilities[0][0].capability; - config.specs = allSpecs; + var task = scheduler.nextTask(); + config.capabilities = task.capability; + config.specs = task.specs; var runner = new Runner(config); runner.run().then(function(exitCode) { @@ -246,64 +136,152 @@ var init = function(configFile, additionalConfig) { log_('Error: ' + err.message); process.exit(1); }); - - runner.on('testsDone', function(failedCount) { - capabilities[0][0].failedCount = failedCount; - }); } else { - // Loop over different capabilities in config file - // Make array for each of them in capabilities array - // Push driverInstances into this array, each with one spec (file!) - // When a driverInstance finishes it's spec, it will create a new driver (Fork) - // with the next spec in line - config.multiCapabilities.forEach(function(capability, index) { - var forksCounter = 0; - - capability.count = capability.count || 1; - - capabilities[index] = []; // Matrix: Dim 1: Capabilities, Dim 2: Instances of capability - if (allSpecs.length < capability.count && config.splitTestsBetweenCapabilities) { - // When we split the specs over multiple instances, - // we can have maximum as many instances as we have specs - capability.count = allSpecs.length; - } - - while(forksCounter < capability.count) { - if (config.splitTestsBetweenCapabilities) { - var createAndRunPartialSpecFork = function() { - var specs = allSpecs[forksCounter]; - if (!specs) { - return; - } - var fork = new Fork(configFile,additionalConfig, - capability,[specs],forksCounter+1,false); - capabilities[index].push(fork); - fork.run(); - fork.addEventHandlers(createAndRunPartialSpecFork); - forksCounter++; + if (config.debug) { + throw new Error('Cannot run in debug mode with ' + + 'multiCapabilities, count > 1, or sharding'); + } + for (var i = 0; i < scheduler.maxConcurrentTasks(); ++i) { + var createNextRunnerFork = function() { + var task = scheduler.nextTask(); + if (task) { + var done = function() { + task.done(); + createNextRunnerFork(); }; - createAndRunPartialSpecFork(); - } else { - var fork = new Fork(configFile, additionalConfig, - capability, allSpecs, forksCounter+1, false); - capabilities[index].push(fork); - fork.run(); - fork.addEventHandlers(); - forksCounter++; + var runnerFork = new RunnerFork(task); + runnerFork.addEventHandlers(done); + runnerFork.run(); } + }; + createNextRunnerFork(); + } + log_('Running ' + scheduler.countActiveTasks() + ' instances of WebDriver'); + + process.on('exit', function(code) { + launcherExitCode = code ? code : launcherExitCode; + if (launcherExitCode) { + log_('Process exited with error code ' + launcherExitCode); + } else { + if (scheduler.numTasksRemaining() > 0) { + throw new Error('BUG: launcher exited with ' + + scheduler.numTasksRemaining() + ' tasks remaining'); + } + reporter.reportSummary(); } + process.exit(launcherExitCode); }); } - noLineLog_('Running ' + countDriverInstances() + - ' instances of WebDriver'); +}; - process.on('exit', function(code) { - if (code) { - launcherExitCode = code; - } - logSummary(); - process.exit(launcherExitCode); - }); +//###### REPORTER #######// +/** + * Keeps track of a list of task reporters. Provides method to add a new + * reporter and to aggregate the reports into a summary. + */ +var reporter = { + taskReporters_: [], + + addTaskReporter: function(task, pid) { + var taskReporter = new TaskReporter_(task, pid); + this.taskReporters_.push(taskReporter); + return taskReporter; + }, + + reportSummary: function() { + this.taskReporters_.forEach(function(taskReporter) { + var capability = taskReporter.task.capability; + var shortName = (capability.browserName) ? capability.browserName : ''; + shortName += (capability.version) ? capability.version : ''; + shortName += (' #' + taskReporter.task.taskId); + if (taskReporter.failedCount) { + log_(shortName + ' failed ' + taskReporter.failedCount + ' test(s)'); + } else { + log_(shortName + ' passed'); + } + }); + } +}; + +/** + * A reporter for a specific task. + * + * @constructor + * @param {object} task Task that is being reported. + * @param {number} pid PID of process running the task. + */ +var TaskReporter_ = function(task, pid) { + this.task = task; + this.pid = pid; + this.failedCount = 0; + this.buffer = ''; +}; + +/** + * Report the header for the current task including information such as + * PID, browser name/version, task Id, specs being run. + */ +TaskReporter_.prototype.reportHeader_ = function() { + var capability = this.task.capability; + var eol = require('os').EOL; + var output = '------------------------------------' + eol; + output += 'PID: ' + this.pid + ' (capability: '; + output += (capability.browserName) ? + capability.browserName : ''; + output += (capability.version) ? + capability.version : ''; + output += (capability.platform) ? + capability.platform : ''; + output += (' #' + this.task.taskId); + output += ')' + eol; + if (this.task.specs.length === 1) { + output += 'Specs: '+ this.task.specs.toString() + eol; + } + this.log_(output); +}; + +/** + * Log the stdout. The reporter is responsible for reporting this data when + * appropriate. + * + * @param {string} stdout Stdout data to log + */ +TaskReporter_.prototype.logStdout = function(stdout) { + this.log_(stdout); +}; + +/** + * Log the stderr. The reporter is responsible for reporting this data when + * appropriate. + * + * @param {string} stderr Stderr data to log + */ +TaskReporter_.prototype.logStderr = function(stderr) { + this.log_(stderr); +}; + +/** + * Signal that the task is completed. This must be called at the end of a task. + * + * @param {number} failedCount Number of failures + */ +TaskReporter_.prototype.testsDone = function(failedCount) { + this.failedCount = failedCount; + if (this.buffer) { + // Flush buffer if nonempty + process.stdout.write(this.buffer); + } +}; + +/** + * Report the following data. The data will be saved to a buffer + * until it is flushed by the function testsDone. + * + * @private + * @param {string} data + */ +TaskReporter_.prototype.log_ = function(data) { + this.buffer += data; }; exports.init = init; diff --git a/lib/taskScheduler.js b/lib/taskScheduler.js new file mode 100644 index 000000000..2770cacf5 --- /dev/null +++ b/lib/taskScheduler.js @@ -0,0 +1,158 @@ +/** + * The taskScheduler keeps track of the specs that needs to run next + * and which task is running what. + */ +'use strict'; + +var ConfigParser = require('./configParser'); + +// A queue of specs for a particular capacity +var TaskQueue = function(capability, specLists) { + this.capability = capability; + this.numRunningInstances = 0; + this.maxInstance = capability.maxInstances || 1; + this.specsIndex = 0; + this.specLists = specLists; +}; + +/** + * A scheduler to keep track of specs that need running and their associated + * capability. It will suggest a task (combination of capability and spec) + * to run while observing the following config rules: capabilities, + * multiCapabilities, shardTestFiles, and maxInstance. + * + * @constructor + * @param {Object} config parsed from the config file + */ +var TaskScheduler = function(config) { + var excludes = ConfigParser.resolveFilePatterns(config.exclude, true, config.configDir); + var allSpecs = ConfigParser.resolveFilePatterns( + ConfigParser.getSpecs(config), false, config.configDir).filter(function(path) { + return excludes.indexOf(path) < 0; + }); + + if (config.capabilities) { + if (config.multiCapabilities.length) { + console.log('Running using config.multiCapabilities - ' + + 'config.capabilities will be ignored'); + } else { + // Use capabilities if multiCapabilities is empty. + config.multiCapabilities = [config.capabilities]; + } + } else if (!config.multiCapabilities.length) { + // Default to chrome if no capability given + config.multiCapabilities = [{ + browserName: 'chrome' + }]; + } + + var taskQueues = []; + config.multiCapabilities.forEach(function(capability) { + var capabilitySpecs = allSpecs; + if (capability.specs) { + var capabilitySpecificSpecs = ConfigParser.resolveFilePatterns( + capability.specs, false, config.configDir); + capabilitySpecs = capabilitySpecs.concat(capabilitySpecificSpecs); + } + + var specLists = []; + // If we shard, we return an array of one element arrays, each containing + // the spec file. If we don't shard, we return an one element array + // containing an array of all the spec files + if (capability.shardTestFiles) { + capabilitySpecs.forEach(function(spec) { + specLists.push([spec]); + }); + } else { + specLists.push(capabilitySpecs); + } + + capability.count = capability.count || 1; + + for (var i = 0; i < capability.count; ++i) { + taskQueues.push(new TaskQueue(capability, specLists)); + } + }); + this.taskQueues = taskQueues; + this.config = config; + this.rotationIndex = 0; // Helps suggestions to rotate amongst capabilities +}; + +/** + * Get the next task that is allowed to run without going over maxInstance. + * + * @return {{capability: Object, specs: Array., taskId: string, done: function()}} + */ +TaskScheduler.prototype.nextTask = function() { + for (var i = 0; i < this.taskQueues.length; ++i) { + var rotatedIndex = ((i + this.rotationIndex) % this.taskQueues.length); + var queue = this.taskQueues[rotatedIndex]; + if (queue.numRunningInstances < queue.maxInstance && + queue.specsIndex < queue.specLists.length) { + this.rotationIndex = rotatedIndex + 1; + ++queue.numRunningInstances; + var taskId = rotatedIndex + 1; + if (queue.specLists.length > 1) { + taskId += String.fromCharCode(97 + queue.specsIndex); //ascii 97 is 'a' + } + var specs = queue.specLists[queue.specsIndex]; + ++queue.specsIndex; + + return { + capability: queue.capability, + specs: specs, + taskId: taskId, + done: function() { + --queue.numRunningInstances; + } + }; + } + } + + return null; +}; + +/** + * Get the number of tasks left to run. + * + * @return {number} + */ +TaskScheduler.prototype.numTasksRemaining = function() { + var count = 0; + this.taskQueues.forEach(function(queue) { + count += (queue.specLists.length - queue.specsIndex); + }); + return count; +}; + +/** + * Get maximum number of concurrent tasks required/permitted. + * + * @return {number} + */ +TaskScheduler.prototype.maxConcurrentTasks = function() { + if (this.config.maxSessions && this.config.maxSessions > 0) { + return this.config.maxSessions; + } else { + var count = 0; + this.taskQueues.forEach(function(queue) { + count += Math.min(queue.maxInstance, queue.specLists.length); + }); + return count; + } +}; + +/** + * Returns number of tasks currently running. + * + * @return {number} + */ +TaskScheduler.prototype.countActiveTasks = function() { + var count = 0; + this.taskQueues.forEach(function(queue) { + count += queue.numRunningInstances; + }); + return count; +}; + +module.exports = TaskScheduler; diff --git a/referenceConf.js b/referenceConf.js index 47f1026f2..4b9631b31 100644 --- a/referenceConf.js +++ b/referenceConf.js @@ -67,14 +67,34 @@ exports.config = { full: 'spec/*.js' }, + // Maximum number of total browser sessions to run. Tests are queued in + // sequence if number of browser sessions is limited by this parameter. + // Use a number less than 1 to denote unlimited. Default is unlimited. + maxSessions: -1, + // ----- Capabilities to be passed to the webdriver instance ---- // - // For a full list of available capabilities, see + // For a list of available capabilities, see // https://code.google.com/p/selenium/wiki/DesiredCapabilities // and // https://code.google.com/p/selenium/source/browse/javascript/webdriver/capabilities.js + // Additionally, you may specify count, shardTestFiles, and maxInstances. capabilities: { - 'browserName': 'chrome' + browserName: 'chrome', + + // Number of times to run this set of capabilities (in parallel, unless + // limited by maxSessions). Default is 1. + count: 1, + + // If this is set to be true, specs will be sharded by file (i.e. all + // files to be run by this set of capabilities will run in parallel). + // Default is false. + shardTestFiles: false, + + // Maximum number of browser instances that can run in parallel for this + // set of capabilities. This is only needed if shardTestFiles is true. + // Default is 1. + maxInstances: 1 }, // If you would like to run more than one instance of webdriver on the same diff --git a/scripts/test.js b/scripts/test.js index 13d3b7e05..c5d897260 100755 --- a/scripts/test.js +++ b/scripts/test.js @@ -6,7 +6,7 @@ var spawn = require('child_process').spawn; var scripts = [ 'node lib/cli.js spec/basicConf.js', 'node lib/cli.js spec/multiConf.js', - 'node lib/cli.js spec/multiSplitConf.js', + 'node lib/cli.js spec/shardingConf.js', 'node lib/cli.js spec/altRootConf.js', 'node lib/cli.js spec/onPrepareConf.js', 'node lib/cli.js spec/onPrepareFileConf.js', diff --git a/spec/multiSplitConf.js b/spec/shardingConf.js similarity index 57% rename from spec/multiSplitConf.js rename to spec/shardingConf.js index 27bfabe63..0b7599a8b 100644 --- a/spec/multiSplitConf.js +++ b/spec/shardingConf.js @@ -7,7 +7,8 @@ exports.config = { // Spec patterns are relative to this directory. specs: [ - 'basic/*_spec.js' + 'basic/mock*', + 'basic/lib_spec.js' ], // Exclude patterns are relative to this directory. @@ -16,11 +17,22 @@ exports.config = { ], chromeOnly: false, - - splitTestsBetweenCapabilities: true, + framework: 'debugprint', + maxSessions: 3, multiCapabilities: [{ 'browserName': 'chrome', - count: 2 + maxInstances: 2 + }, { + 'browserName': 'chrome', + shardTestFiles: true, + maxInstances: 1, + specs: 'basic/polling*' // Capacity specific specs + }, { + shardTestFiles: true, + 'browserName': 'firefox', + maxInstances: 2, + count: 2, + specs: 'basic/action*', }], baseUrl: env.baseUrl, diff --git a/spec/unit/config_test.js b/spec/unit/config_test.js index 8b523f4f3..6dccb2cb9 100644 --- a/spec/unit/config_test.js +++ b/spec/unit/config_test.js @@ -26,7 +26,7 @@ describe('the config parser', function() { expect(config.rootElement).toEqual('.mycontainer'); expect(config.onPrepare.indexOf(path.normalize('/spec/unit/data/foo/bar.js'))).not.toEqual(-1); expect(config.specs.length).toEqual(1); - expect(config.specs[0]).toEqual('fakespec*.js'); + expect(config.specs[0]).toEqual('fakespec[AB].js'); }); it('should keep filepaths relative to the cwd when merging', function() { @@ -43,7 +43,7 @@ describe('the config parser', function() { it('should resolve relative to the cwd', function() { spyOn(process, 'cwd').andReturn(__dirname + '/'); var toAdd = { - specs: 'data/*spec*.js' + specs: 'data/*spec[AB].js' }; var config = new ConfigParser().addConfig(toAdd).getConfig(); var specs = ConfigParser.resolveFilePatterns(config.specs); diff --git a/spec/unit/data/config.js b/spec/unit/data/config.js index a6ad7f5b4..3baa8d08a 100644 --- a/spec/unit/data/config.js +++ b/spec/unit/data/config.js @@ -1,5 +1,5 @@ exports.config = { onPrepare: 'foo/bar.js', - specs: [ 'fakespec*.js' ], + specs: [ 'fakespec[AB].js' ], rootElement: '.mycontainer' }; diff --git a/spec/unit/data/fakespecC.js b/spec/unit/data/fakespecC.js new file mode 100644 index 000000000..f87af3c19 --- /dev/null +++ b/spec/unit/data/fakespecC.js @@ -0,0 +1 @@ +// Blank diff --git a/spec/unit/taskScheduler_test.js b/spec/unit/taskScheduler_test.js new file mode 100644 index 000000000..f80bb7b47 --- /dev/null +++ b/spec/unit/taskScheduler_test.js @@ -0,0 +1,224 @@ +var TaskScheduler = require('../../lib/taskScheduler.js'); +var ConfigParser = require('../../lib/configParser'); + +describe('the task scheduler', function() { + + it('should schedule single capability tests', function() { + var toAdd = { + specs: [ + 'spec/unit/data/fakespecA.js', + 'spec/unit/data/fakespecB.js' + ], + capabilities: { + browserName: 'chrome' + } + }; + var config = new ConfigParser().addConfig(toAdd).getConfig(); + var scheduler = new TaskScheduler(config); + + var task = scheduler.nextTask(); + expect(task.capability.browserName).toEqual('chrome'); + expect(task.specs.length).toEqual(2); + + expect(scheduler.numTasksRemaining()).toEqual(0); + }); + + it('should schedule single capability tests with sharding', function() { + var toAdd = { + specs: [ + 'spec/unit/data/fakespecA.js', + 'spec/unit/data/fakespecB.js' + ], + capabilities: { + shardTestFiles: true, + maxInstances: 2, + browserName: 'chrome' + } + }; + var config = new ConfigParser().addConfig(toAdd).getConfig(); + var scheduler = new TaskScheduler(config); + + var task1 = scheduler.nextTask(); + expect(task1.capability.browserName).toEqual('chrome'); + expect(task1.specs.length).toEqual(1); + + var task2 = scheduler.nextTask(); + expect(task2.capability.browserName).toEqual('chrome'); + expect(task2.specs.length).toEqual(1); + + expect(scheduler.numTasksRemaining()).toEqual(0); + }); + + it('should schedule single capability tests with count', function() { + var toAdd = { + specs: [ + 'spec/unit/data/fakespecA.js', + 'spec/unit/data/fakespecB.js' + ], + capabilities: { + count: 2, + browserName: 'chrome' + } + }; + var config = new ConfigParser().addConfig(toAdd).getConfig(); + var scheduler = new TaskScheduler(config); + + var task1 = scheduler.nextTask(); + expect(task1.capability.browserName).toEqual('chrome'); + expect(task1.specs.length).toEqual(2); + + var task2 = scheduler.nextTask(); + expect(task2.capability.browserName).toEqual('chrome'); + expect(task2.specs.length).toEqual(2); + + expect(scheduler.numTasksRemaining()).toEqual(0); + }); + + it('should schedule multiCapabilities tests', function() { + var toAdd = { + specs: [ + 'spec/unit/data/fakespecA.js', + 'spec/unit/data/fakespecB.js' + ], + multiCapabilities: [{ + 'browserName': 'chrome' + }, { + 'browserName': 'firefox' + }], + }; + var config = new ConfigParser().addConfig(toAdd).getConfig(); + var scheduler = new TaskScheduler(config); + + var task1 = scheduler.nextTask(); + expect(task1.capability.browserName).toEqual('chrome'); + expect(task1.specs.length).toEqual(2); + + var task2 = scheduler.nextTask(); + expect(task2.capability.browserName).toEqual('firefox'); + expect(task2.specs.length).toEqual(2); + + expect(scheduler.numTasksRemaining()).toEqual(0); + }); + + it('should obey maxInstances', function() { + var toAdd = { + specs: [ + 'spec/unit/data/fakespecA.js', + 'spec/unit/data/fakespecB.js' + ], + capabilities: { + shardTestFiles: true, + maxInstances: 1, + browserName: 'chrome' + } + }; + var config = new ConfigParser().addConfig(toAdd).getConfig(); + var scheduler = new TaskScheduler(config); + + var task1 = scheduler.nextTask(); + expect(task1.capability.browserName).toEqual('chrome'); + expect(task1.specs.length).toEqual(1); + + var task2 = scheduler.nextTask(); + expect(task2).toBeNull(); + expect(scheduler.numTasksRemaining()).toEqual(1); + + task1.done(); + var task3 = scheduler.nextTask(); + expect(task3.capability.browserName).toEqual('chrome'); + expect(task3.specs.length).toEqual(1); + + expect(scheduler.numTasksRemaining()).toEqual(0); + }); + + it('should allow capability-specific specs', function() { + var toAdd = { + specs: [ + 'spec/unit/data/fakespecA.js', + 'spec/unit/data/fakespecB.js' + ], + multiCapabilities: [{ + 'browserName': 'chrome', + specs: 'spec/unit/data/fakespecC.js' + }], + }; + var config = new ConfigParser().addConfig(toAdd).getConfig(); + var scheduler = new TaskScheduler(config); + + var task = scheduler.nextTask(); + expect(task.capability.browserName).toEqual('chrome'); + expect(task.specs.length).toEqual(3); + + expect(scheduler.numTasksRemaining()).toEqual(0); + }); + + it('should handle multiCapabilities with mixture of features', function() { + var toAdd = { + specs: [ + 'spec/unit/data/fakespecA.js', + 'spec/unit/data/fakespecB.js' + ], + multiCapabilities: [{ + 'browserName': 'chrome', + maxInstances: 2, + count: 2 + }, { + 'browserName': 'firefox', + shardTestFiles: true, + maxInstances: 1, + count: 2 + }], + }; + var config = new ConfigParser().addConfig(toAdd).getConfig(); + var scheduler = new TaskScheduler(config); + + var task1 = scheduler.nextTask(); + expect(task1.capability.browserName).toEqual('chrome'); + expect(task1.specs.length).toEqual(2); + task1.done(); + + var task2 = scheduler.nextTask(); + expect(task2.capability.browserName).toEqual('chrome'); + expect(task2.specs.length).toEqual(2); + task2.done(); + + var task3 = scheduler.nextTask(); + expect(task3.capability.browserName).toEqual('firefox'); + expect(task3.specs.length).toEqual(1); + task3.done(); + + var task4 = scheduler.nextTask(); + expect(task4.capability.browserName).toEqual('firefox'); + expect(task4.specs.length).toEqual(1); + task4.done(); + + var task5 = scheduler.nextTask(); + expect(task5.capability.browserName).toEqual('firefox'); + expect(task5.specs.length).toEqual(1); + task5.done(); + + var task6 = scheduler.nextTask(); + expect(task6.capability.browserName).toEqual('firefox'); + expect(task6.specs.length).toEqual(1); + task6.done(); + + expect(scheduler.numTasksRemaining()).toEqual(0); + }); + + it('should default to chrome when no capability is defined', function() { + var toAdd = { + specs: [ + 'spec/unit/data/fakespecA.js', + 'spec/unit/data/fakespecB.js' + ] + }; + var config = new ConfigParser().addConfig(toAdd).getConfig(); + var scheduler = new TaskScheduler(config); + + var task = scheduler.nextTask(); + expect(task.capability.browserName).toEqual('chrome'); + expect(task.specs.length).toEqual(2); + + expect(scheduler.numTasksRemaining()).toEqual(0); + }); +}); From 46a8ca5425587a7a7348c0c965fcfa3951bda519 Mon Sep 17 00:00:00 2001 From: Julie Date: Wed, 4 Jun 2014 11:32:37 -0700 Subject: [PATCH 013/612] chore(testapp): change the default port from 8000 to 8081 This prevents conflicts with the default port for the angular.js application. Closes #887 --- referenceConf.js | 2 +- spec/environment.js | 2 +- testapp/scripts/web-server.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/referenceConf.js b/referenceConf.js index 4b9631b31..411d4c625 100644 --- a/referenceConf.js +++ b/referenceConf.js @@ -106,7 +106,7 @@ exports.config = { // // A base URL for your application under test. Calls to protractor.get() // with relative paths will be prepended with this. - baseUrl: 'http://localhost:' + (process.env.HTTP_PORT || '8000'), + baseUrl: 'http://localhost:9876', // Selector for the element housing the angular app - this defaults to // body, but is necessary if ng-app is on a descendant of diff --git a/spec/environment.js b/spec/environment.js index 88f93d73f..8f588c40e 100644 --- a/spec/environment.js +++ b/spec/environment.js @@ -15,6 +15,6 @@ module.exports = { // A base URL for your application under test. baseUrl: 'http://' + (process.env.HTTP_HOST || 'localhost') + - ':' + (process.env.HTTP_PORT || '8000') + ':' + (process.env.HTTP_PORT || '8081') }; diff --git a/testapp/scripts/web-server.js b/testapp/scripts/web-server.js index 893f685b4..16b24baa4 100755 --- a/testapp/scripts/web-server.js +++ b/testapp/scripts/web-server.js @@ -3,7 +3,7 @@ var express = require('express'); var util = require('util'); var testApp = express(); -var DEFAULT_PORT = process.env.HTTP_PORT || 8000; +var DEFAULT_PORT = process.env.HTTP_PORT || 8081; var testAppDir = require('path').resolve(__dirname, '..'); var main = function(argv) { From 729915554cfa440bda0eec8a1c4bf423f4089481 Mon Sep 17 00:00:00 2001 From: Hank Duan Date: Fri, 23 May 2014 11:31:34 -0700 Subject: [PATCH 014/612] feat(sauceprovider): append spec filename to capabilities.name --- lib/driverProviders/sauce.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/driverProviders/sauce.js b/lib/driverProviders/sauce.js index 0dcbf3f93..0d530a0ad 100644 --- a/lib/driverProviders/sauce.js +++ b/lib/driverProviders/sauce.js @@ -57,6 +57,11 @@ SauceDriverProvider.prototype.setupEnv = function() { this.config_.capabilities.accessKey = this.config_.sauceKey; this.config_.seleniumAddress = 'http://' + this.config_.sauceUser + ':' + this.config_.sauceKey + '@ondemand.saucelabs.com:80/wd/hub'; + + // Append filename to capabilities.name so that it's easier to identify tests + if (this.config_.capabilities.name && this.config_.capabilities.shardTestFiles) { + this.config_.capabilities.name += (':' + this.config_.specs.toString().replace(/^.*[\\\/]/, '')); + } util.puts('Using SauceLabs selenium server at ' + this.config_.seleniumAddress.replace(/\/\/.+@/, '//')); From 3c0e727136ab3d397c1a9a2bb02692d0aeb9be40 Mon Sep 17 00:00:00 2001 From: Hank Duan Date: Wed, 4 Jun 2014 11:52:44 -0700 Subject: [PATCH 015/612] refactor(protractor): reorganize internal structure of elementFinder/webelement - Allow chaining of actions (i.e. element(By.x).clear().sendKeys('abc)) - first(), last(), and get(index) are not executed immediately, allowing them to be placed in page objects - Rework the way that elementFinder and wrappedWebElement is represented - Breaking changes: - element.all is chained differently Before: element(By.x).element.all(By.y) Now: element(By.x).all(By.y) However, using element.all without chaining did not change, i.e. element.all(By.x) - Changed the way for retrieving underlying webElements Before: element(By.x).find(), element(By.x).findElement(By.y), and element(By.x).findElements(By.y) Now: element(By.x).getWebElement(), element(By.x).element(By.y).getWebElement(), and element(By.x).element(By.y).getWebElements(), respectively - browser.findElement returns a raw WebElement so $, $$, and evaluate will no longer be available --- lib/protractor.js | 1169 +++++++++++++++++------------------ spec/basic/elements_spec.js | 176 ++---- 2 files changed, 589 insertions(+), 756 deletions(-) diff --git a/lib/protractor.js b/lib/protractor.js index d45b99c1e..563b77654 100644 --- a/lib/protractor.js +++ b/lib/protractor.js @@ -1,9 +1,8 @@ var url = require('url'); +var util = require('util'); var webdriver = require('selenium-webdriver'); var clientSideScripts = require('./clientsidescripts.js'); - - var ProtractorBy = require('./locators.js').ProtractorBy; var DEFER_LABEL = 'NG_DEFER_BOOTSTRAP!'; @@ -60,24 +59,286 @@ var mixin = function(to, from, fnName, setupFn) { * * @private * @param {Protractor} ptor - * @param {Array.=} opt_usingChain * @return {function(webdriver.Locator): ElementFinder} */ -var buildElementHelper = function(ptor, opt_usingChain) { - var usingChain = opt_usingChain || []; - var using = function() { - var base = ptor; - for (var i = 0; i < usingChain.length; ++i) { - base = base.findElement(usingChain[i]); +var buildElementHelper = function(ptor) { + + /** + * ElementArrayFinder is used for operations on an array of elements (as opposed + * to a single element). + * + * @alias element.all(locator) + * @view + *

    + *
  • First
  • + *
  • Second
  • + *
  • Third
  • + *
+ * + * @example + * element.all(by.css('.items li')).then(function(items) { + * expect(items.length).toBe(3); + * expect(items[0].getText()).toBe('First'); + * }); + * + * @constructor + * @param {webdriver.Locator} locator An element locator. + * @param {ElementFinder=} opt_parentElementFinder The element finder previous to + * this. (i.e. opt_parentElementFinder.all(locator) => this) + * @return {ElementArrayFinder} + */ + var ElementArrayFinder = function(locator, opt_parentElementFinder) { + if (!locator) { + throw new Error('Locator cannot be empty'); } - return base; + this.locator_ = locator; + this.parentElementFinder_ = opt_parentElementFinder || null; }; /** - * The element function returns an Element Finder. Element Finders do - * not actually attempt to find the element until a method is called on them, - * which means they can be set up in helper files before the page is - * available. + * Returns the array of WebElements represented by this ElementArrayFinder. + * + * @alias element.all(locator).getWebElements() + * @return {Array.} + */ + ElementArrayFinder.prototype.getWebElements = function() { + if (this.parentElementFinder_) { + var parentWebElement = this.parentElementFinder_.getWebElement(); + if (this.locator_.findElementsOverride) { + return this.locator_.findElementsOverride(ptor.driver, parentWebElement); + } else { + return parentWebElement.findElements(this.locator_); + } + } else { + ptor.waitForAngular(); + if (this.locator_.findElementsOverride) { + return this.locator_.findElementsOverride(ptor.driver); + } else { + return ptor.driver.findElements(this.locator_); + } + } + }; + + /** + * Get an element found by the locator by index. The index starts at 0. + * This does not actually retrieve the underlying element. + * + * @alias element.all(locator).get(index) + * @view + *
    + *
  • First
  • + *
  • Second
  • + *
  • Third
  • + *
+ * + * @example + * var list = element.all(by.css('.items li')); + * expect(list.get(0).getText()).toBe('First'); + * expect(list.get(1).getText()).toBe('Second'); + * + * @param {number} index Element index. + * @return {ElementFinder} finder representing element at the given index. + */ + ElementArrayFinder.prototype.get = function(index) { + return new ElementFinder(this.locator_, this.parentElementFinder_, null, index); + }; + + /** + * Get the first matching element for the locator. This does not actually + * retrieve the underlying element. + * + * @alias element.all(locator).first() + * @view + *
    + *
  • First
  • + *
  • Second
  • + *
  • Third
  • + *
+ * + * @example + * var first = element.all(by.css('.items li')).first(); + * expect(first.getText()).toBe('First'); + * + * @return {ElementFinder} finder representing the first matching element + */ + ElementArrayFinder.prototype.first = function() { + return this.get(0); + }; + + /** + * Get the last matching element for the locator. This does not actually + * retrieve the underlying element. + * + * @alias element.all(locator).last() + * @view + *
    + *
  • First
  • + *
  • Second
  • + *
  • Third
  • + *
+ * + * @example + * var last = element.all(by.css('.items li')).last(); + * expect(last.getText()).toBe('Third'); + * + * @return {ElementFinder} finder representing the last matching element + */ + ElementArrayFinder.prototype.last = function() { + return this.get(-1); + }; + + /** + * Count the number of elements found by the locator. + * + * @alias element.all(locator).count() + * @view + *
    + *
  • First
  • + *
  • Second
  • + *
  • Third
  • + *
+ * + * @example + * var list = element.all(by.css('.items li')); + * expect(list.count()).toBe(3); + * + * @return {!webdriver.promise.Promise} A promise which resolves to the + * number of elements matching the locator. + */ + ElementArrayFinder.prototype.count = function() { + return this.getWebElements().then(function(arr) { + return arr.length; + }); + }; + + /** + * Represents the ElementArrayFinder as an array of ElementFinders. + * + * @return {Array.} Return a promise, which resolves to a list + * of ElementFinders specified by the locator. + */ + ElementArrayFinder.prototype.asElementFinders_ = function() { + var self = this; + return this.getWebElements().then(function(arr) { + var list = []; + arr.forEach(function(webElem, index) { + list.push(new ElementFinder(self.locator_, self.parentElementFinder_, null, index)); + }); + return list; + }); + }; + + /** + * Find the elements specified by the locator. The input function is passed + * to the resulting promise, which resolves to an array of ElementFinders. + * + * @alias element.all(locator).then(thenFunction) + * @view + *
    + *
  • First
  • + *
  • Second
  • + *
  • Third
  • + *
+ * + * @example + * element.all(by.css('.items li')).then(function(arr) { + * expect(arr.length).toEqual(3); + * }); + * + * @param {function(Array.)} fn + * + * @type {webdriver.promise.Promise} a promise which will resolve to + * an array of ElementFinders matching the locator. + */ + ElementArrayFinder.prototype.then = function(fn) { + return this.asElementFinders_().then(fn); + }; + + /** + * Calls the input function on each ElementFinder found by the locator. + * + * @alias element.all(locator).each(eachFunction) + * @view + *
    + *
  • First
  • + *
  • Second
  • + *
  • Third
  • + *
+ * + * @example + * element.all(by.css('.items li')).each(function(element) { + * // Will print First, Second, Third. + * element.getText().then(console.log); + * }); + * + * @param {function(ElementFinder)} fn Input function + */ + ElementArrayFinder.prototype.each = function(fn) { + return this.asElementFinders_().then(function(arr) { + arr.forEach(function(elementFinder) { + fn(elementFinder); + }); + }); + }; + + /** + * Apply a map function to each element found using the locator. The + * callback receives the ElementFinder as the first argument and the index as + * a second arg. + * + * @alias element.all(locator).map(mapFunction) + * @view + *
    + *
  • First
  • + *
  • Second
  • + *
  • Third
  • + *
+ * + * @example + * var items = element.all(by.css('.items li')).map(function(elm, index) { + * return { + * index: index, + * text: elm.getText(), + * class: elm.getAttribute('class') + * }; + * }); + * expect(items).toEqual([ + * {index: 0, text: 'First', class: 'one'}, + * {index: 1, text: 'Second', class: 'two'}, + * {index: 2, text: 'Third', class: 'three'} + * ]); + * + * @param {function(ElementFinder, number)} mapFn Map function that + * will be applied to each element. + * @return {!webdriver.promise.Promise} A promise that resolves to an array + * of values returned by the map function. + */ + ElementArrayFinder.prototype.map = function(mapFn) { + return this.asElementFinders_().then(function(arr) { + var list = []; + 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); + }); + }); + return list; + }); + }; + + /** + * 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. + * + * 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 + * until an action is called, which means they can be set up in helper files + * before the page is available. * * @alias element(locator) * @view @@ -99,398 +360,290 @@ var buildElementHelper = function(ptor, opt_usingChain) { * input.sendKeys('123'); * expect(input.getAttribute('value')).toBe('Foo123'); * + * @constructor * @param {webdriver.Locator} locator An element locator. + * @param {ElementFinder=} opt_parentElementFinder The element finder previous + * to this. (i.e. opt_parentElementFinder.element(locator) => this) + * @param {webdriver.promise.Promise} opt_actionResult The promise which + * will be retrieved with then. Resolves to the latest action result, + * or null if no action has been called. + * @param {number=} opt_index The index of the element to retrieve. null means + * retrieve the only element, while -1 means retrieve the last element * @return {ElementFinder} */ - var element = function(locator) { - var elementFinder = {}; - - var webElementFns = WEB_ELEMENT_FUNCTIONS.concat( - ['findElements', 'isElementPresent', 'evaluate', 'allowAnimations']); - webElementFns.forEach(function(fnName) { - elementFinder[fnName] = function() { - var callerError = new Error(); - var args = arguments; - - return using().findElement(locator).then(function(element) { - return element[fnName].apply(element, args).then(null, function(e) { - e.stack = e.stack + '\n' + callerError.stack; - throw e; - }); - }); - }; + var ElementFinder = function(locator, opt_parentElementFinder, opt_actionResult, opt_index) { + if (!locator) { + throw new Error ('Locator cannot be empty'); + } + this.locator_ = locator; + this.parentElementFinder_ = opt_parentElementFinder || null; + this.actionResult_ = opt_actionResult || webdriver.promise.fulfilled(null); + this.opt_index_ = opt_index; + + var self = this; + WEB_ELEMENT_FUNCTIONS.forEach(function(fnName) { + if(!self[fnName]) { + self[fnName] = function() { + var webElem = self.getWebElement(); + var actionResult = webElem[fnName].apply(webElem, arguments); + return new ElementFinder( + locator, opt_parentElementFinder, + actionResult, opt_index); + }; + } }); + }; + util.inherits(ElementFinder, webdriver.promise.Promise); - // This is a special case since it doesn't return a promise, instead it - // returns a WebElement. - elementFinder.findElement = function(subLocator) { - return using().findElement(locator).findElement(subLocator); - }; - - /** - * Returns the specified WebElement. Throws the WebDriver error if the - * element doesn't exist. - * - * @alias element(locator).find() - * @return {webdriver.WebElement} - */ - elementFinder.find = function() { - return using().findElement(locator); - }; - - /** - * Determine whether an element is present on the page. - * - * @alias element(locator).isPresent() - * - * @view - * {{person.name}} - * - * @example - * // Element exists. - * expect(element(by.binding('person.name')).isPresent()).toBe(true); - * - * // Element not present. - * expect(element(by.binding('notPresent')).isPresent()).toBe(false); - * - * @return {!webdriver.promise.Promise} A promise which resolves to a - * boolean. - */ - elementFinder.isPresent = function() { - return using().isElementPresent(locator); - }; - - /** - * Returns the originally specified locator. - * - * @return {webdriver.Locator} The element locator. - */ - elementFinder.locator = function() { - return locator; - }; - - /** - * Calls to element may be chained to find elements within a parent. - * - * @alias element(locator).element(locator) - * @view - *
- *
- * Child text - *
{{person.phone}}
- *
- *
- * - * @example - * // Chain 2 element calls. - * var child = element(by.css('.parent')). - * element(by.css('.child')); - * expect(child.getText()).toBe('Child text\n555-123-4567'); - * - * // Chain 3 element calls. - * var triple = element(by.css('.parent')). - * element(by.css('.child')). - * element(by.binding('person.phone')); - * expect(triple.getText()).toBe('555-123-4567'); - * - * @param {Protractor} ptor - * @param {Array.=} opt_usingChain - * @return {function(webdriver.Locator): ElementFinder} - */ - elementFinder.element = - buildElementHelper(ptor, usingChain.concat(locator)); - - /** - * Shortcut for chaining css element finders. - * - * @alias element(locator).$(cssSelector) - * @view - *
- *
- * Child text - *
{{person.phone}}
- *
- *
- * - * @example - * // Chain 2 element calls. - * var child = element(by.css('.parent')).$('.child'); - * expect(child.getText()).toBe('Child text\n555-123-4567'); - * - * // Chain 3 element calls. - * var triple = $('.parent').$('.child').$('.grandchild'); - * expect(triple.getText()).toBe('555-123-4567'); - * - * @param {string} cssSelector A css selector. - * @return {ElementFinder} - */ - elementFinder.$ = function(cssSelector) { - return buildElementHelper(ptor, usingChain.concat(locator))( - webdriver.By.css(cssSelector)); - }; - - elementFinder.$$ = function(cssSelector) { - return buildElementHelper(ptor, usingChain).all( - webdriver.By.css(cssSelector)); - }; + /** + * Calls to element may be chained to find elements within a parent. + * + * @alias element(locator).element(locator) + * @view + *
+ *
+ * Child text + *
{{person.phone}}
+ *
+ *
+ * + * @example + * // Chain 2 element calls. + * var child = element(by.css('.parent')). + * element(by.css('.child')); + * expect(child.getText()).toBe('Child text\n555-123-4567'); + * + * // Chain 3 element calls. + * var triple = element(by.css('.parent')). + * element(by.css('.child')). + * element(by.binding('person.phone')); + * expect(triple.getText()).toBe('555-123-4567'); + * + * @param {webdriver.Locator} subLocator + * @return {ElementFinder} + */ + ElementFinder.prototype.element = function(subLocator) { + return new ElementFinder(subLocator, this); + }; - return elementFinder; + /** + * Calls to element may be chained to find an array of elements within a parent. + * + * @alias element(locator).all(locator) + * @view + *
+ *
    + *
  • First
  • + *
  • Second
  • + *
  • Third
  • + *
+ *
+ * + * @example + * var items = element(by.css('.parent')).all(by.tagName('li')) + * + * @param {webdriver.Locator} subLocator + * @return {ElementArrayFinder} + */ + ElementFinder.prototype.all = function(subLocator) { + return new ElementArrayFinder(subLocator, this); }; /** - * element.all is used for operations on an array of elements (as opposed - * to a single element). + * Shortcut for querying the document directly with css. * - * @alias element.all(locator) + * @alias $(cssSelector) * @view - *
    - *
  • First
  • - *
  • Second
  • - *
  • Third
  • - *
+ *
+ * First + * Second + *
* * @example - * element.all(by.css('.items li')).then(function(items) { - * expect(items.length).toBe(3); - * expect(items[0].getText()).toBe('First'); - * }); + * var item = $('.count .two'); + * expect(item.getText()).toBe('Second'); * - * @param {webdriver.Locator} locator - * @return {ElementArrayFinder} + * @param {string} selector A css selector + * @return {ElementFinder} which identifies the located + * {@link webdriver.WebElement} */ - element.all = function(locator) { - var elementArrayFinder = {}; - - /** - * Count the number of elements found by the locator. - * - * @alias element.all(locator).count() - * @view - *
    - *
  • First
  • - *
  • Second
  • - *
  • Third
  • - *
- * - * @example - * var list = element.all(by.css('.items li')); - * expect(list.count()).toBe(3); - * - * @return {!webdriver.promise.Promise} A promise which resolves to the - * number of elements matching the locator. - */ - elementArrayFinder.count = function() { - return using().findElements(locator).then(function(arr) { - return arr.length; - }); - }; + ElementFinder.prototype.$ = function(selector) { + return new ElementFinder(webdriver.By.css(selector), this); + }; - /** - * Get an element found by the locator by index. The index starts at 0. - * - * @alias element.all(locator).get(index) - * @view - *
    - *
  • First
  • - *
  • Second
  • - *
  • Third
  • - *
- * - * @example - * var list = element.all(by.css('.items li')); - * expect(list.get(0).getText()).toBe('First'); - * expect(list.get(1).getText()).toBe('Second'); - * - * @param {number} index Element index. - * @return {webdriver.WebElement} The element at the given index - */ - elementArrayFinder.get = function(index) { - var id = using().findElements(locator).then(function(arr) { - return arr[index]; - }); - return ptor.wrapWebElement(new webdriver.WebElement(ptor.driver, id)); - }; + /** + * Shortcut for querying the document directly with css. + * + * @alias $$(cssSelector) + * @view + *
+ * First + * Second + *
+ * + * @example + * // The following protractor expressions are equivalent. + * var list = element.all(by.css('.count span')); + * expect(list.count()).toBe(2); + * + * list = $$('.count span'); + * expect(list.count()).toBe(2); + * expect(list.get(0).getText()).toBe('First'); + * expect(list.get(1).getText()).toBe('Second'); + * + * @param {string} selector a css selector + * @return {ElementArrayFinder} which identifies the + * array of the located {@link webdriver.WebElement}s. + */ + ElementFinder.prototype.$$ = function(selector) { + return new ElementArrayFinder(webdriver.By.css(selector), this); + }; - /** - * Get the first element found using the locator. - * - * @alias element.all(locator).first() - * @view - *
    - *
  • First
  • - *
  • Second
  • - *
  • Third
  • - *
- * - * @example - * var list = element.all(by.css('.items li')); - * expect(list.first().getText()).toBe('First'); - * - * @return {webdriver.WebElement} The first matching element - */ - elementArrayFinder.first = function() { - var id = using().findElements(locator).then(function(arr) { - if (!arr.length) { - throw new Error('No element found using locator: ' + locator.message); - } - return arr[0]; - }); - return ptor.wrapWebElement(new webdriver.WebElement(ptor.driver, id)); - }; + /** + * Determine whether the element is present on the page. + * + * @alias element(locator).isPresent() + * + * @view + * {{person.name}} + * + * @example + * // Element exists. + * expect(element(by.binding('person.name')).isPresent()).toBe(true); + * + * // Element not present. + * expect(element(by.binding('notPresent')).isPresent()).toBe(false); + * + * @return {ElementFinder} which resolves to whether + * the element is present on the page. + */ + ElementFinder.prototype.isPresent = function() { + var isPresent = new ElementArrayFinder( + this.locator_, this.parentElementFinder_).count().then(function(count) { + return !!count; + }); + return new ElementFinder( + this.locator_, this.parentElementFinder_, isPresent, this.opt_index_); + }; - /** - * Get the last matching element for the locator. - * - * @alias element.all(locator).last() - * @view - *
    - *
  • First
  • - *
  • Second
  • - *
  • Third
  • - *
- * - * @example - * var list = element.all(by.css('.items li')); - * expect(list.last().getText()).toBe('Third'); - * - * @return {webdriver.WebElement} the last matching element - */ - elementArrayFinder.last = function() { - var id = using().findElements(locator).then(function(arr) { - return arr[arr.length - 1]; - }); - return ptor.wrapWebElement(new webdriver.WebElement(ptor.driver, id)); - }; + /** + * Override for WebElement.prototype.isElementPresent so that protractor waits + * for Angular to settle before making the check. + * + * @see ElementFinder.isPresent + * @return {ElementFinder} which resolves to whether + * the element is present on the page. + */ + ElementFinder.prototype.isElementPresent = function(subLocator) { + return this.element(subLocator).isPresent(); + }; - /** - * Find the elements specified by the locator. The input function is passed - * to the resulting promise, which resolves to an array of WebElements. - * - * @alias element.all(locator).then(thenFunction) - * @view - *
    - *
  • First
  • - *
  • Second
  • - *
  • Third
  • - *
- * - * @example - * element.all(by.css('.items li')).then(function(arr) { - * expect(arr.length).toEqual(3); - * }); - * - * @param {function(Array.)} fn - * - * @type {webdriver.promise.Promise} a promise which will resolve to - * an array of WebElements matching the locator. - */ - elementArrayFinder.then = function(fn) { - return using().findElements(locator).then(fn); - }; + /** + * @return {webdriver.Locator} + */ + ElementFinder.prototype.locator = function() { + return this.locator_; + }; - /** - * Calls the input function on each WebElement found by the locator. - * - * @alias element.all(locator).each(eachFunction) - * @view - *
    - *
  • First
  • - *
  • Second
  • - *
  • Third
  • - *
- * - * @example - * element.all(by.css('.items li')).each(function(element) { - * // Will print First, Second, Third. - * element.getText().then(console.log); - * }); - * - * @param {function(webdriver.WebElement)} fn Input function - */ - elementArrayFinder.each = function(fn) { - using().findElements(locator).then(function(arr) { - arr.forEach(function(webElem) { - fn(webElem); - }); - }); - }; + /** + * Returns the WebElement represented by this ElementFinder. + * Throws the WebDriver error if the element doesn't exist. + * If index is null, it makes sure that there is only one underlying + * WebElement described by the chain of locators and issues a warning + * otherwise. If index is not null, it retrieves the WebElement specified by + * the index. + * + * @example + * The following three expressions are equivalent. + * - element(by.css('.parent')).getWebElement(); + * - browser.waitForAngular(); browser.driver.findElement(by.css('.parent')); + * - browser.findElement(by.css('.parent')) + * + * @alias element(locator).getWebElement() + * @return {webdriver.WebElement} + */ + ElementFinder.prototype.getWebElement = function() { + var self = this; + var webElementsPromise = new ElementArrayFinder( + this.locator_, this.parentElementFinder_).getWebElements(); + + var id = webElementsPromise.then(function(arr) { + if (!arr.length) { + throw new Error('No element found using locator: ' + self.locator_.message); + } + var index = self.opt_index_; + if (index == null) { + // index null means we make sure there is only one element + if (arr.length > 1) { + console.log('warning: more than one element found for locator ' + + self.locator_.message + '- you may need to be more specific'); + } + index = 0; + } else if (index === -1) { + // -1 is special and means last + index = arr.length - 1; + } + return arr[index]; + }); + return new webdriver.WebElement(ptor.driver, id); + }; - /** - * Apply a map function to each element found using the locator. The - * callback receives the web element as the first argument and the index as - * a second arg. - * - * @alias element.all(locator).map(mapFunction) - * @view - *
    - *
  • First
  • - *
  • Second
  • - *
  • Third
  • - *
- * - * @example - * var items = element.all(by.css('.items li')).map(function(elm, index) { - * return { - * index: index, - * text: elm.getText(), - * class: elm.getAttribute('class') - * }; - * }); - * expect(items).toEqual([ - * {index: 0, text: 'First', class: 'one'}, - * {index: 1, text: 'Second', class: 'two'}, - * {index: 2, text: 'Third', class: 'three'} - * ]); - * - * @param {function(webdriver.WebElement, number)} mapFn Map function that - * will be applied to each element. - * @return {!webdriver.promise.Promise} A promise that resolves to an array - * of values returned by the map function. - */ - elementArrayFinder.map = function(mapFn) { - return using().findElements(locator).then(function(arr) { - var list = []; - arr.forEach(function(webElem, index) { - var mapResult = mapFn(webElem, index); - // All nested arrays and objects will also be fully resolved. - webdriver.promise.fullyResolved(mapResult).then(function(resolved) { - list.push(resolved); - }); - }); - return list; - }); - }; + /** + * Evaluates the input as if it were on the scope of the current element. + * @param {string} expression + * + * @return {ElementFinder} which resolves to the + * evaluated expression. The result will be resolved as in + * {@link webdriver.WebDriver.executeScript}. In summary - primitives will + * be resolved as is, functions will be converted to string, and elements + * will be returned as a WebElement. + */ + ElementFinder.prototype.evaluate = function(expression) { + var webElement = this.getWebElement(); + var evaluatedResult = webElement.getDriver().executeScript( + clientSideScripts.evaluate, webElement, expression); + return new ElementFinder(this.locator_, this.parentElementFinder_, + evaluatedResult, this.opt_index_); + }; - return elementArrayFinder; + /** + * Determine if animation is allowed on the current element. + * @param {string} value + * + * @return {ElementFinder} which resolves to whether animation is allowed. + */ + ElementFinder.prototype.allowAnimations = function(value) { + var webElement = this.getWebElement(); + var allowAnimationsResult = webElement.getDriver().executeScript( + clientSideScripts.allowAnimations, webElement, value); + return new ElementFinder(this.locator_, this.parentElementFinder_, + allowAnimationsResult, this.opt_index_); }; - return element; -}; + /** + * 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) { + return this.actionResult_.then(function() { + return fn.apply(null, arguments); + }); + }; -/** - * Build the helper '$' function for a given instance of Protractor. - * - * @private - * @param {Protractor} ptor - * @return {function(string): ElementFinder} - */ -var buildCssHelper = function(ptor) { - return function(cssSelector) { - return buildElementHelper(ptor)(webdriver.By.css(cssSelector)); + var element = function(locator) { + return new ElementFinder(locator); }; -}; -/** - * Build the helper '$$' function for a given instance of Protractor. - * - * @private - * @param {Protractor} ptor - * @return {function(string): ElementArrayFinder} - */ -var buildMultiCssHelper = function(ptor) { - return function(cssSelector) { - return buildElementHelper(ptor).all(webdriver.By.css(cssSelector)); + element.all = function(locator) { + return new ElementArrayFinder(locator); }; + + return element; }; /** @@ -500,22 +653,23 @@ var buildMultiCssHelper = function(ptor) { * scope. * @constructor */ -var Protractor = function(webdriver, opt_baseUrl, opt_rootElement) { +var Protractor = function(webdriverInstance, opt_baseUrl, opt_rootElement) { // These functions should delegate to the webdriver instance, but should // wait for Angular to sync up before performing the action. This does not // include functions which are overridden by protractor below. var methodsToSync = ['getCurrentUrl', 'getPageSource', 'getTitle']; // Mix all other driver functionality into Protractor. - for (var method in webdriver) { - if(!this[method] && typeof webdriver[method] == 'function') { + for (var method in webdriverInstance) { + if(!this[method] && typeof webdriverInstance[method] == 'function') { if (methodsToSync.indexOf(method) !== -1) { - mixin(this, webdriver, method, this.waitForAngular.bind(this)); + mixin(this, webdriverInstance, method, this.waitForAngular.bind(this)); } else { - mixin(this, webdriver, method); + mixin(this, webdriverInstance, method); } } } + var self = this; /** * The wrapped webdriver instance. Use this to interact with pages that do @@ -523,7 +677,7 @@ var Protractor = function(webdriver, opt_baseUrl, opt_rootElement) { * * @type {webdriver.WebDriver} */ - this.driver = webdriver; + this.driver = webdriverInstance; /** * Helper function for finding elements. @@ -533,18 +687,22 @@ var Protractor = function(webdriver, opt_baseUrl, opt_rootElement) { this.element = buildElementHelper(this); /** - * Helper function for finding elements by css. + * Shorthand function for finding elements by css. * * @type {function(string): ElementFinder} */ - this.$ = buildCssHelper(this); + this.$ = function(selector) { + return self.element(webdriver.By.css(selector)); + }; /** - * Helper function for finding arrays of elements by css. + * Shorthand function for finding arrays of elements by css. * * @type {function(string): ElementArrayFinder} */ - this.$$ = buildMultiCssHelper(this); + this.$$ = function(selector) { + return self.all(webdriver.By.css(selector)); + }; /** * All get methods will be resolved against this base URL. Relative URLs are = @@ -627,184 +785,13 @@ Protractor.prototype.waitForAngular = function() { }); }; -// TODO: activeelement also returns a WebElement. - -/** - * Wrap a webdriver.WebElement with protractor specific functionality. - * - * @param {webdriver.WebElement} element - * @return {webdriver.WebElement} the wrapped web element. - */ -Protractor.prototype.wrapWebElement = function(element) { - // We want to be able to used varArgs in function signatures for clarity. - // jshint unused: false - var thisPtor = this; - // Before any of the WebElement functions, Protractor will wait to make sure - // Angular is synced up. - var originalFns = {}; - WEB_ELEMENT_FUNCTIONS.forEach(function(name) { - originalFns[name] = element[name]; - element[name] = function() { - thisPtor.waitForAngular(); - return originalFns[name].apply(element, arguments); - }; - }); - - var originalFindElement = element.findElement; - var originalFindElements = element.findElements; - var originalIsElementPresent = element.isElementPresent; - - /** - * Shortcut for querying the document directly with css. - * - * @alias $(cssSelector) - * @view - *
- * First - * Second - *
- * - * @example - * var item = $('.count .two'); - * expect(item.getText()).toBe('Second'); - * - * @param {string} selector A css selector - * @see webdriver.WebElement.findElement - * @return {!webdriver.WebElement} - */ - element.$ = function(selector) { - var locator = webdriver.By.css(selector); - return thisPtor.findElement(locator); - }; - - /** - * @see webdriver.WebElement.findElement - * @return {!webdriver.WebElement} - */ - element.findElement = function(locator, varArgs) { - thisPtor.waitForAngular(); - - var found; - if (locator.findElementsOverride) { - found = thisPtor.findElementsOverrideHelper_(element, locator); - } else { - found = originalFindElement.apply(element, arguments); - } - - return thisPtor.wrapWebElement(found); - }; - - /** - * Shortcut for querying the document directly with css. - * - * @alias $$(cssSelector) - * @view - *
- * First - * Second - *
- * - * @example - * // The following protractor expressions are equivalent. - * var list = element.all(by.css('.count span')); - * expect(list.count()).toBe(2); - * - * list = $$('.count span'); - * expect(list.count()).toBe(2); - * expect(list.get(0).getText()).toBe('First'); - * expect(list.get(1).getText()).toBe('Second'); - * - * @param {string} selector a css selector - * @see webdriver.WebElement.findElements - * @return {!webdriver.promise.Promise} A promise that will be resolved to an - * array of the located {@link webdriver.WebElement}s. - */ - element.$$ = function(selector) { - var locator = webdriver.By.css(selector); - return thisPtor.findElements(locator); - }; - - /** - * @see webdriver.WebElement.findElements - * @return {!webdriver.promise.Promise} A promise that will be resolved to an - * array of the located {@link webdriver.WebElement}s. - */ - element.findElements = function(locator, varArgs) { - thisPtor.waitForAngular(); - - var found; - if (locator.findElementsOverride) { - found = locator.findElementsOverride(element.getDriver(), element); - } else { - found = originalFindElements.apply(element, arguments); - } - - return found.then(function(elems) { - for (var i = 0; i < elems.length; ++i) { - thisPtor.wrapWebElement(elems[i]); - } - - return elems; - }); - }; - - /** - * @see webdriver.WebElement.isElementPresent - * @return {!webdriver.promise.Promise} A promise that will be resolved with - * whether an element could be located on the page. - */ - element.isElementPresent = function(locator, varArgs) { - thisPtor.waitForAngular(); - if (locator.findElementsOverride) { - return locator.findElementsOverride(element.getDriver(), element). - then(function (arr) { - return !!arr.length; - }); - } - return originalIsElementPresent.apply(element, arguments); - }; - - /** - * Evaluates the input as if it were on the scope of the current element. - * @param {string} expression - * - * @return {!webdriver.promise.Promise} A promise that will resolve to the - * evaluated expression. The result will be resolved as in - * {@link webdriver.WebDriver.executeScript}. In summary - primitives will - * be resolved as is, functions will be converted to string, and elements - * will be returned as a WebElement. - */ - element.evaluate = function(expression) { - thisPtor.waitForAngular(); - return element.getDriver().executeScript(clientSideScripts.evaluate, - element, expression); - }; - - element.allowAnimations = function(value) { - thisPtor.waitForAngular(); - return element.getDriver().executeScript(clientSideScripts.allowAnimations, - element, value); - }; - - return element; -}; - /** * Waits for Angular to finish rendering before searching for elements. * @see webdriver.WebDriver.findElement * @return {!webdriver.WebElement} */ -Protractor.prototype.findElement = function(locator, varArgs) { - var found; - this.waitForAngular(); - - if (locator.findElementsOverride) { - found = this.findElementsOverrideHelper_(null, locator); - } else { - found = this.driver.findElement(locator, varArgs); - } - - return this.wrapWebElement(found); +Protractor.prototype.findElement = function(locator) { + return this.element(locator).getWebElement(); }; /** @@ -813,23 +800,8 @@ Protractor.prototype.findElement = function(locator, varArgs) { * @return {!webdriver.promise.Promise} A promise that will be resolved to an * array of the located {@link webdriver.WebElement}s. */ -Protractor.prototype.findElements = function(locator, varArgs) { - var self = this, found; - this.waitForAngular(); - - if (locator.findElementsOverride) { - found = locator.findElementsOverride(this.driver); - } else { - found = this.driver.findElements(locator, varArgs); - } - - return found.then(function(elems) { - for (var i = 0; i < elems.length; ++i) { - self.wrapWebElement(elems[i]); - } - - return elems; - }); +Protractor.prototype.findElements = function(locator) { + return this.element.all(locator).getWebElements(); }; /** @@ -838,14 +810,10 @@ Protractor.prototype.findElements = function(locator, varArgs) { * @return {!webdriver.promise.Promise} A promise that will resolve to whether * the element is present on the page. */ -Protractor.prototype.isElementPresent = function(locatorOrElement, varArgs) { - this.waitForAngular(); - if (locatorOrElement.findElementsOverride) { - return locatorOrElement.findElementsOverride(this.driver).then(function(arr) { - return !!arr.length; - }); - } - return this.driver.isElementPresent(locatorOrElement, varArgs); +Protractor.prototype.isElementPresent = function(locatorOrElement) { + var element = (locatorOrElement instanceof webdriver.promise.Promise) ? + locatorOrElement : this.element(locatorOrElement); + return element.isPresent(); }; /** @@ -1122,35 +1090,6 @@ Protractor.prototype.pause = function() { flow.timeout(1000, 'waiting for debugger to attach'); }; -/** - * Builds a single web element from a locator with a findElementsOverride. - * Throws an error if an element is not found, and issues a warning - * if more than one element is described by the selector. - * - * @private - * @param {webdriver.WebElement} using A WebElement to scope the find, - * or null. - * @param {webdriver.Locator} locator - * @return {webdriver.WebElement} - */ -Protractor.prototype.findElementsOverrideHelper_ = function(using, locator) { - // We need to return a WebElement, so we construct one using a promise - // which will resolve to a WebElement. - return new webdriver.WebElement( - this.driver, - locator.findElementsOverride(this.driver, using).then(function(arr) { - if (!arr.length) { - throw new Error('No element found using locator: ' + locator.message); - } - if (arr.length > 1) { - console.log('warning: more than one element found for locator ' + - locator.message + - '- you may need to be more specific'); - } - return arr[0]; - })); -}; - /** * Create a new instance of Protractor by wrapping a webdriver instance. * diff --git a/spec/basic/elements_spec.js b/spec/basic/elements_spec.js index 9238f2da8..549ce803f 100644 --- a/spec/basic/elements_spec.js +++ b/spec/basic/elements_spec.js @@ -27,6 +27,18 @@ describe('ElementFinder', function() { expect(name.getText()).toEqual('Jane'); }); + it('should chain element actions', function() { + browser.get('index.html#/form'); + + var usernameInput = element(by.model('username')); + var name = element(by.binding('username')); + + expect(name.getText()).toEqual('Anon'); + + usernameInput.clear().sendKeys('Jane'); + expect(name.getText()).toEqual('Jane'); + }); + it('chained call should wait to grab the WebElement until a method is called', function() { // These should throw no error before a page is loaded. @@ -68,18 +80,14 @@ describe('ElementFinder', function() { expect(elems.length).toEqual(4); }); - element(by.id('baz')). - element.all(by.binding('item')). - then(function(elems) { - expect(elems.length).toEqual(2); - }); + element(by.id('baz')).all(by.binding('item')).then(function(elems) { + expect(elems.length).toEqual(2); + }); }); - it('should wait to grab multiple chained elements', - function() { + it('should wait to grab multiple chained elements', function() { // These should throw no error before a page is loaded. - var reused = element(by.id('baz')). - element.all(by.binding('item')); + var reused = element(by.id('baz')).all(by.binding('item')); browser.get('index.html#/conflict'); @@ -88,6 +96,21 @@ describe('ElementFinder', function() { expect(reused.last().getText()).toEqual('Inner other: innerbarbaz'); }); + it('should wait to grab elements chained by index', function() { + // These should throw no error before a page is loaded. + var reused = element(by.id('baz')).all(by.binding('item')); + var first = reused.first(); + var second = reused.get(1); + var last = reused.last(); + + browser.get('index.html#/conflict'); + + expect(reused.count()).toEqual(2); + expect(first.getText()).toEqual('Inner: inner'); + expect(second.getText()).toEqual('Inner other: innerbarbaz'); + expect(last.getText()).toEqual('Inner other: innerbarbaz'); + }); + it('should determine element presence properly with chaining', function() { browser.get('index.html#/conflict'); expect(element(by.id('baz')). @@ -228,6 +251,7 @@ describe('ElementFinder', function() { var byCss = by.css('body'); var byBinding = by.binding('greet'); + expect(element(byCss).locator()).toEqual(byCss); expect(element(byBinding).locator()).toEqual(byBinding); }); @@ -254,60 +278,10 @@ describe('shortcut css notation', function() { beforeEach(function() { browser.get('index.html#/bindings'); }); - - it('$ should be equivalent to by.css', function() { - var shortcut = $('.planet-info'); - var noShortcut = element(by.css('.planet-info')); - - expect(protractor.WebElement.equals(shortcut.find(), noShortcut.find())). - toBe(true); - }); - - it('$$ should be equivalent to by.css', function() { - var shortcut = element.all(by.css('option')); - var noShortcut = $$('option'); - shortcut.then(function(optionsFromShortcut) { - noShortcut.then(function(optionsFromLongForm) { - expect(optionsFromShortcut.length).toEqual(optionsFromLongForm.length); - - for (var i = 0; i < optionsFromLongForm.length; ++i) { - expect(protractor.WebElement.equals( - optionsFromLongForm[i], optionsFromShortcut[i])). - toBe(true); - } - }); - }); - }); - - it('$ chained should be equivalent to by.css', function() { - var select = element(by.css('select')); - var shortcut = select.$('option[value="4"]'); - var noShortcut = select.element(by.css('option[value="4"]')); - - expect(protractor.WebElement.equals(shortcut.find(), noShortcut.find())). - toBe(true); - }); - - it('$$ chained should be equivalent to by.css', function() { - var select = element(by.css('select')); - var shortcut = select.element.all(by.css('option')); - var noShortcut = select.$$('option'); - shortcut.then(function(optionsFromShortcut) { - noShortcut.then(function(optionsFromLongForm) { - expect(optionsFromShortcut.length).toEqual(optionsFromLongForm.length); - - for (var i = 0; i < optionsFromLongForm.length; ++i) { - expect(protractor.WebElement.equals( - optionsFromLongForm[i], optionsFromShortcut[i])). - toBe(true); - } - }); - }); - }); - + it('should chain $$ with $', function() { var withoutShortcutCount = - element(by.css('select')).element.all(by.css('option')).then(function(options) { + element(by.css('select')).all(by.css('option')).then(function(options) { return options.length; }); var withShortcutCount = $('select').$$('option').count(); @@ -315,83 +289,3 @@ describe('shortcut css notation', function() { expect(withoutShortcutCount).toEqual(withShortcutCount); }); }); - -describe('wrapping WebElements', function() { - var verifyMethodsAdded = function(result) { - expect(typeof result.evaluate).toBe('function'); - expect(typeof result.$).toBe('function'); - expect(typeof result.$$).toBe('function'); - }; - - beforeEach(function() { - browser.get('index.html#/bindings'); - }); - - describe('when found via #findElement', function() { - it('should wrap the result', function() { - browser.findElement(by.binding('planet.name')).then(verifyMethodsAdded); - - browser.findElement(by.css('option[value="4"]')).then(verifyMethodsAdded); - }); - - describe('when found with global element', function() { - it('should wrap the result', function() { - element(by.binding('planet.name')).find().then(verifyMethodsAdded); - element(by.css('option[value="4"]')).find().then(verifyMethodsAdded); - }); - }); - }); - - describe('when found via #findElements', function() { - it('should wrap the results', function() { - browser.findElements(by.binding('planet.name')).then(function(results) { - results.forEach(verifyMethodsAdded); - }); - browser.findElements(by.css('option[value="4"]')).then(function(results) { - results.forEach(verifyMethodsAdded); - }); - }); - - describe('when found with global element.all', function() { - it('should wrap the result', function() { - element.all(by.binding('planet.name')).then(function(results) { - results.forEach(verifyMethodsAdded); - }); - element.all(by.binding('planet.name')).get(0).then(verifyMethodsAdded); - element.all(by.binding('planet.name')).first().then(verifyMethodsAdded); - element.all(by.binding('planet.name')).last().then(verifyMethodsAdded); - element.all(by.css('option[value="4"]')).then(function(results) { - results.forEach(verifyMethodsAdded); - }); - }); - }); - }); - - describe('when chaining with another element', function() { - var info; - - beforeEach(function() { - info = browser.findElement(by.css('.planet-info')); - }); - - describe('when found via #findElement', function() { - it('should wrap the result', function() { - info.findElement(by.binding('planet.name')).then(verifyMethodsAdded); - - info.findElement(by.css('div:last-child')).then(verifyMethodsAdded); - }); - }); - - describe('when querying for many elements', function() { - it('should wrap the result', function() { - info.findElements(by.binding('planet.name')).then(function(results) { - results.forEach(verifyMethodsAdded); - }); - - info.findElements(by.css('div:last-child')).then(function(results) { - results.forEach(verifyMethodsAdded); - }); - }); - }); - }); -}); From ad33da8ecc76322cee36a7344042a52ff33c280c Mon Sep 17 00:00:00 2001 From: Julie Date: Wed, 4 Jun 2014 11:13:13 -0700 Subject: [PATCH 016/612] test(selenium): add a mock driver provider for integration testing With `mockSelenium: true` in the config, an instance of WebDriver using a fake executor will be used. This executor returns a successful test response for any command, and never connects to a real browser. --- lib/driverProviders/mock.js | 67 +++++++++++++++++++++++++++++++++++++ lib/runner.js | 2 ++ spec/unit/runner_test.js | 39 ++------------------- 3 files changed, 71 insertions(+), 37 deletions(-) create mode 100644 lib/driverProviders/mock.js diff --git a/lib/driverProviders/mock.js b/lib/driverProviders/mock.js new file mode 100644 index 000000000..1bc55fbd5 --- /dev/null +++ b/lib/driverProviders/mock.js @@ -0,0 +1,67 @@ +/* + * This is an mock implementation of the Driver Provider. + * It returns a fake webdriver and never actually contacts a selenium + * server. + */ +var webdriver = require('selenium-webdriver'), + q = require('q'); +/** + * @constructor + */ +var MockExecutor = function() { + this.driver_ = null; +}; + +/** + * @param {!webdriver.Command} command The command to execute. + * @param {function(Error, !bot.response.ResponseObject=)} callback the function + * to invoke when the command response is ready. + */ +MockExecutor.prototype.execute = function(command, callback) { + callback(null, { + status: '0', + value: 'test_response' + }); +}; + +var MockDriverProvider = function(config) { + this.config_ = config; +}; + +/** + * Configure and launch (if applicable) the object's environment. + * @public + * @return {q.promise} A promise which will resolve immediately. + */ +MockDriverProvider.prototype.setupEnv = function() { + return q.fcall(function() {}); +}; + +/** + * Teardown and destroy the environment and do any associated cleanup. + * + * @public + * @return {q.promise} A promise which will resolve immediately. + */ +MockDriverProvider.prototype.teardownEnv = function() { + var deferred = q.defer(); + this.driver_.quit().then(function() { + deferred.resolve(); + }); + return deferred.promise; +}; + +/** + * Retrieve the webdriver for the runner. + * @public + * @return webdriver instance + */ +MockDriverProvider.prototype.getDriver = function() { + this.driver_ = new webdriver.WebDriver('test_session_id', new MockExecutor()); + return this.driver_; +}; + +// new instance w/ each include +module.exports = function(config) { + return new MockDriverProvider(config); +}; diff --git a/lib/runner.js b/lib/runner.js index 1f3100e78..6050c6c49 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -113,6 +113,8 @@ Runner.prototype.loadDriverProvider_ = function() { runnerPath = './driverProviders/sauce'; } else if (this.config_.seleniumServerJar) { runnerPath = './driverProviders/local'; + } else if (this.config_.mockSelenium) { + runnerPath = './driverProviders/mock'; } else { runnerPath = './driverProviders/local'; } diff --git a/spec/unit/runner_test.js b/spec/unit/runner_test.js index 6d7aa1944..81dfa13b7 100644 --- a/spec/unit/runner_test.js +++ b/spec/unit/runner_test.js @@ -2,35 +2,6 @@ var Runner = require('../../lib/runner'); var q = require('q'); describe('the Protractor runner', function() { - var mockDriverProvider; - - beforeEach(function() { - mockDriverProvider = jasmine.createSpyObj( - 'mockDriverProvider', - ['setupEnv', 'teardownEnv', 'getDriver']); - - mockDriverProvider.setupEnv.andCallFake(function() { - return q.fcall(function() {}); - }); - - mockDriverProvider.teardownEnv.andCallFake(function() { - return q.fcall(function() {}); - }); - - mockDriverProvider.getDriver.andReturn({ - // A fake driver. - manage: function() { - return { - timeouts: function() { - return { - setScriptTimeout: function() {} - }; - } - }; - } - }); - }); - it('should export its config', function() { var config = { foo: 'bar', @@ -43,21 +14,17 @@ describe('the Protractor runner', function() { it('should run', function(done) { var config = { + mockSelenium: true, specs: ['*.js'], framework: 'debugprint' }; var exitCode; - Runner.prototype.loadDriverProvider_ = function() { - this.driverprovider_ = mockDriverProvider; - }; Runner.prototype.exit_ = function(exit) { exitCode = exit; }; var runner = new Runner(config); runner.run().then(function() { - expect(mockDriverProvider.setupEnv).toHaveBeenCalled(); - expect(mockDriverProvider.teardownEnv).toHaveBeenCalled(); expect(exitCode).toEqual(0); done(); }); @@ -65,13 +32,11 @@ describe('the Protractor runner', function() { it('should fail with no specs', function() { var config = { + mockSelenium: true, specs: [], framework: 'simpleprint' }; var exitCode; - Runner.prototype.loadDriverProvider_ = function() { - this.driverprovider_ = mockDriverProvider; - }; Runner.prototype.exit_ = function(exit) { exitCode = exit; }; From e20530e540c1d7572a1f7ed2e886511c1a9cfa3d Mon Sep 17 00:00:00 2001 From: Hank Duan Date: Wed, 4 Jun 2014 13:53:29 -0700 Subject: [PATCH 017/612] chore(test): remove shardingConf.js from test.js --- lib/driverProviders/sauce.js | 3 ++- scripts/test.js | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/driverProviders/sauce.js b/lib/driverProviders/sauce.js index 0d530a0ad..7e976fd9e 100644 --- a/lib/driverProviders/sauce.js +++ b/lib/driverProviders/sauce.js @@ -60,7 +60,8 @@ SauceDriverProvider.prototype.setupEnv = function() { // Append filename to capabilities.name so that it's easier to identify tests if (this.config_.capabilities.name && this.config_.capabilities.shardTestFiles) { - this.config_.capabilities.name += (':' + this.config_.specs.toString().replace(/^.*[\\\/]/, '')); + this.config_.capabilities.name += ( + ':' + this.config_.specs.toString().replace(/^.*[\\\/]/, '')); } util.puts('Using SauceLabs selenium server at ' + diff --git a/scripts/test.js b/scripts/test.js index c5d897260..7401cab5b 100755 --- a/scripts/test.js +++ b/scripts/test.js @@ -6,7 +6,6 @@ var spawn = require('child_process').spawn; var scripts = [ 'node lib/cli.js spec/basicConf.js', 'node lib/cli.js spec/multiConf.js', - 'node lib/cli.js spec/shardingConf.js', 'node lib/cli.js spec/altRootConf.js', 'node lib/cli.js spec/onPrepareConf.js', 'node lib/cli.js spec/onPrepareFileConf.js', From 857b3aed235f7a3260305cc36ed79be3c9a1b2d0 Mon Sep 17 00:00:00 2001 From: Hank Duan Date: Wed, 4 Jun 2014 16:00:09 -0700 Subject: [PATCH 018/612] chore(release): version bump and changelog for 0.24.0. --- CHANGELOG.md | 136 ++++++++++ docs/api.md | 754 +++++++++++++++++++++++++-------------------------- package.json | 4 +- 3 files changed, 501 insertions(+), 393 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1097cee7d..87b3d8309 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,139 @@ +# 0.24.0 +_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._ + +## Features + +- ([7299155](https://github.com/angular/protractor/commit/729915554cfa440bda0eec8a1c4bf423f4089481)) + feat(sauceprovider): append spec filename to capabilities.name + +- ([f22456d](https://github.com/angular/protractor/commit/f22456d3cf0768a577371776d716b8888a74397d)) + refactor(jasminewd): use jasminewd from its own node module + + The Jasmine Webdriver Adapter is now its own npm module. The code has been moved to + http://www.github.com/angular/jasminewd. + + Remove the code from Protractor, and add a dependency on jasminewd@1.0.0. + +- ([f23565d](https://github.com/angular/protractor/commit/f23565d5db4fbb102cfec8311ce9dfaee52e9113)) + feat(protractor): new API allowAnimations(bool) on protractor elements. + +- ([876a3c0](https://github.com/angular/protractor/commit/876a3c04c07a9f8d97e1edca3ec1f76e51e1a310)) + feat(runner): support running dart2js spec files + + This commit supports running Dart2JS output in NodeJS. Officially, Dart2JS in supposed to only + generate code for running in a real webbrowser. With this patch, the dart2js code can also be + executed in NodeJS. + + Ref: + https://code.google.com/p/dart/source/browse/branches/bleeding_edge/dart/sdk/lib/js/dart2js/js_dart2js.dart?spec=svn32943&r=32943#487 + +- ([8d46e21](https://github.com/angular/protractor/commit/8d46e210b91ed1521f6692a2cf35f60740c0ace6)) + feat(runner): support sourcemaps in spec files + + This feature allows folks who are generating their spec files from a different language to see + stack traces that use the line numbers from their sources before translation. + + This commit introduces a dependency on the `source-map-support` library. + + For general information about sourcemaps, refer: + - http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/ + - https://github.com/evanw/node-source-map-support + - https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/view + +## Bug Fixes + +- ([56daa54](https://github.com/angular/protractor/commit/56daa54e2e269064bd44bc05ed0bbf2c44657ca8)) + fix(clientsidescripts): convert non-Error exceptions to Errors + + If any functions called by clientSideScripts throws a an exception that doesn't inherit from + `Error`, the stack trace is completely unhelpful and the message is just "unknown error."  This + commit wraps such errors into + `Error` instances so that we have meaningful stack traces and the correct exception message.  + (e.g. This is the common case when running dart2js code.  This commit gives us the Dart stack + trace and exception message.) + + In addition, I've pushed the construction of the string to install into the browser into + clientsidescripts.js. + +- ([00c6abe](https://github.com/angular/protractor/commit/00c6abef16c47868974eed8ad1a4c38494b2a504)) + fix(element): fix WebElement.$ using the incorrect By object + + Closes #852 + +- ([0500b2c](https://github.com/angular/protractor/commit/0500b2c3b2698fe41bedf694b92aad884f3b0d0e)) + fix(navigation): ignore unknown JS errors when looking for the URL + + This should address #841 + + Ignoring the error and trying again has worked for all of my test cases, and the error has never + occurred more than once in a row. + +- ([c8c85e0](https://github.com/angular/protractor/commit/c8c85e0d94d7a7211b000650f01af714663611ad)) + fix(locators): fix by.repeater finding all rows for IE + + Previously, element.all(by.repeater('foo in foos')) would find non-element nodes for + ng-repeat-start elements, which could cause IEDriver to fall over if the test tried to get text + from those nodes. + +## Breaking Changes + +- ([3c0e727](https://github.com/angular/protractor/commit/3c0e727136ab3d397c1a9a2bb02692d0aeb9be40)) + refactor(protractor): reorganize internal structure of elementFinder/webelement + + - Allow chaining of actions (i.e. `element(By.x).clear().sendKeys('abc)`) + - first(), last(), and get(index) are not executed immediately, allowing + them to be placed in page objects + - Rework the way that elementFinder and wrappedWebElement is represented + - Breaking changes: + - element.all is chained differently + ``` + Before: element(By.x).element.all(By.y) + Now: element(By.x).all(By.y) + + However, using element.all without chaining did not change, + i.e. `element.all(By.x)` + ``` + + - Changed the way for retrieving underlying webElements + ``` + Before: element(By.x).find(), element(By.x).findElement(By.y), + and element(By.x).findElements(By.y) + Now: element(By.x).getWebElement(), + element(By.x).element(By.y).getWebElement(), + and element(By.x).element(By.y).getWebElements(), + respectively + ``` + - browser.findElement returns a raw WebElement so $, $$, and + evaluate will no longer be available + +- ([fbfc72b](https://github.com/angular/protractor/commit/fbfc72bad15667990232bb9ff1da503e03d16230)) + feat(launcher): Add support for maxSession + + - add support for maxSession and capability-specific specs + - cleaned up launcher (refactored out taskScheduler.js) + - Breaking change: + - changed the config to shard test files; also sharding is specific to + capabilities now + ``` + Before: config.splitTestsBetweenCapabilities + Now: config.capabilities.shardTestFiles or config.multiCapabilities[index].shardTestFiles + ``` + +- ([9e5d9e4](https://github.com/angular/protractor/commit/9e5d9e4abb7d0928e6092a711fda527554994be7)) + feat(locators): remove deprecated locator APIs + + This is a **breaking change**. The following deprecated Locator APIs have been removed. + + - `by.input` + - `by.select` + - `by.selectedOption` + - `by.textarea` + + `input`, `select`, and `textarea` can be replaced by `by.model`. + + `element(by.selectedOption('foo'))` can be replaced by + `element(by.model('foo')).$('option:checked')` + # 0.23.1 _Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._ diff --git a/docs/api.md b/docs/api.md index cf8e4871d..be933f0be 100644 --- a/docs/api.md +++ b/docs/api.md @@ -5,28 +5,28 @@ Protractor API 0.23.1 ##protractor -* [element](#api-element) -* [elementFinder.find](#api-elementfinder-find) -* [elementFinder.isPresent](#api-elementfinder-ispresent) -* [elementFinder.locator](#api-elementfinder-locator) -* [elementFinder.element](#api-elementfinder-element) -* [elementFinder.$](#api-elementfinder-$) -* [element.all](#api-element-all) -* [elementArrayFinder.count](#api-elementarrayfinder-count) -* [elementArrayFinder.get](#api-elementarrayfinder-get) -* [elementArrayFinder.first](#api-elementarrayfinder-first) -* [elementArrayFinder.last](#api-elementarrayfinder-last) -* [elementArrayFinder.each](#api-elementarrayfinder-each) -* [elementArrayFinder.map](#api-elementarrayfinder-map) +* [ElementArrayFinder](#api-elementarrayfinder) +* [ElementArrayFinder.prototype.getWebElements](#api-elementarrayfinder-prototype-getwebelements) +* [ElementArrayFinder.prototype.get](#api-elementarrayfinder-prototype-get) +* [ElementArrayFinder.prototype.first](#api-elementarrayfinder-prototype-first) +* [ElementArrayFinder.prototype.last](#api-elementarrayfinder-prototype-last) +* [ElementArrayFinder.prototype.count](#api-elementarrayfinder-prototype-count) +* [ElementArrayFinder.prototype.each](#api-elementarrayfinder-prototype-each) +* [ElementArrayFinder.prototype.map](#api-elementarrayfinder-prototype-map) +* [ElementFinder](#api-elementfinder) +* [ElementFinder.prototype.element](#api-elementfinder-prototype-element) +* [ElementFinder.prototype.all](#api-elementfinder-prototype-all) +* [ElementFinder.prototype.$](#api-elementfinder-prototype-$) +* [ElementFinder.prototype.$$](#api-elementfinder-prototype-$$) +* [ElementFinder.prototype.isPresent](#api-elementfinder-prototype-ispresent) +* [ElementFinder.prototype.isElementPresent](#api-elementfinder-prototype-iselementpresent) +* [ElementFinder.prototype.locator](#api-elementfinder-prototype-locator) +* [ElementFinder.prototype.getWebElement](#api-elementfinder-prototype-getwebelement) +* [ElementFinder.prototype.evaluate](#api-elementfinder-prototype-evaluate) +* [ElementFinder.prototype.allowAnimations](#api-elementfinder-prototype-allowanimations) +* [ElementFinder.prototype.then](#api-elementfinder-prototype-then) * [Protractor](#api-protractor) * [Protractor.prototype.waitForAngular](#api-protractor-prototype-waitforangular) -* [Protractor.prototype.wrapWebElement](#api-protractor-prototype-wrapwebelement) -* [element.$](#api-element-$) -* [element.findElement](#api-element-findelement) -* [element.$$](#api-element-$$) -* [element.findElements](#api-element-findelements) -* [element.isElementPresent](#api-element-iselementpresent) -* [element.evaluate](#api-element-evaluate) * [Protractor.prototype.findElement](#api-protractor-prototype-findelement) * [Protractor.prototype.findElements](#api-protractor-prototype-findelements) * [Protractor.prototype.isElementPresent](#api-protractor-prototype-iselementpresent) @@ -48,13 +48,9 @@ Protractor API 0.23.1 * [ProtractorBy.prototype.addLocator](#api-protractorby-prototype-addlocator) * [ProtractorBy.prototype.binding](#api-protractorby-prototype-binding) * [ProtractorBy.prototype.exactBinding](#api-protractorby-prototype-exactbinding) -* [ProtractorBy.prototype.select](#api-protractorby-prototype-select) -* [ProtractorBy.prototype.selectedOption](#api-protractorby-prototype-selectedoption) -* [ProtractorBy.prototype.input](#api-protractorby-prototype-input) * [ProtractorBy.prototype.model](#api-protractorby-prototype-model) * [ProtractorBy.prototype.buttonText](#api-protractorby-prototype-buttontext) * [ProtractorBy.prototype.partialButtonText](#api-protractorby-prototype-partialbuttontext) -* [ProtractorBy.prototype.textarea](#api-protractorby-prototype-textarea) * [ProtractorBy.prototype.repeater](#api-protractorby-prototype-repeater) * [ProtractorBy.prototype.cssContainingText](#api-protractorby-prototype-csscontainingtext) @@ -152,35 +148,27 @@ Protractor API 0.23.1 * [webdriver.UnhandledAlertError](#api-webdriver-unhandledalerterror) * [webdriver.UnhandledAlertError.prototype.getAlert](#api-webdriver-unhandledalerterror-prototype-getalert) -##[element](https://github.com/angular/protractor/blob/master/lib/protractor.js#L74) -#### Use as: element(locator) -The element function returns an Element Finder. Element Finders do -not actually attempt to find the element until a method is called on them, -which means they can be set up in helper files before the page is -available. +##[ElementArrayFinder](https://github.com/angular/protractor/blob/master/lib/protractor.js#L66) +#### Use as: element.all(locator) +ElementArrayFinder is used for operations on an array of elements (as opposed +to a single element). ###Example ```html -{{person.name}} - - +
    +
  • First
  • +
  • Second
  • +
  • Third
  • +
``` ```javascript -// Find element with {{scopeVar}} syntax. -element(by.binding('person.name')).getText().then(function(name) { - expect(name).toBe('Foo'); +element.all(by.css('.items li')).then(function(items) { + expect(items.length).toBe(3); + expect(items[0].getText()).toBe('First'); }); - -// Find element with ng-bind="scopeVar" syntax. -expect(element(by.binding('person.email')).getText()).toBe('foo@bar.com'); - -// Find by model. -var input = element(by.model('person.name')); -input.sendKeys('123'); -expect(input.getAttribute('value')).toBe('Foo123'); ``` @@ -190,6 +178,7 @@ expect(input.getAttribute('value')).toBe('Foo123'); Param | Type | Description --- | --- | --- locator | webdriver.Locator | An element locator. +opt_parentElementFinder | [ElementFinder](#elementfinder)= | The element finder previous to this. (i.e. opt_parentElementFinder.all(locator) => this) @@ -198,59 +187,12 @@ locator | webdriver.Locator | An element locator. Type | Description --- | --- -ElementFinder | - - -##[elementFinder.find](https://github.com/angular/protractor/blob/master/lib/protractor.js#L128) -#### Use as: element(locator).find() -Returns the specified WebElement. Throws the WebDriver error if the -element doesn't exist. - - - - - - -###Returns - -Type | Description ---- | --- -[webdriver.WebElement](#webdriverwebelement) | - - -##[elementFinder.isPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L139) -#### Use as: element(locator).isPresent() -Determine whether an element is present on the page. - - -###Example - -```html -{{person.name}} -``` - -```javascript -// Element exists. -expect(element(by.binding('person.name')).isPresent()).toBe(true); - -// Element not present. -expect(element(by.binding('notPresent')).isPresent()).toBe(false); -``` - - - - - -###Returns - -Type | Description ---- | --- -!webdriver.promise.Promise | A promise which resolves to a boolean. +[ElementArrayFinder](#elementarrayfinder) | -##[elementFinder.locator](https://github.com/angular/protractor/blob/master/lib/protractor.js#L161) - -Returns the originally specified locator. +##[ElementArrayFinder.prototype.getWebElements](https://github.com/angular/protractor/blob/master/lib/protractor.js#L98) +#### Use as: element.all(locator).getWebElements() +Returns the array of WebElements represented by this ElementArrayFinder. @@ -261,36 +203,29 @@ Returns the originally specified locator. Type | Description --- | --- -webdriver.Locator | The element locator. +Array.<webdriver.WebElement> | -##[elementFinder.element](https://github.com/angular/protractor/blob/master/lib/protractor.js#L170) -#### Use as: element(locator).element(locator) -Calls to element may be chained to find elements within a parent. +##[ElementArrayFinder.prototype.get](https://github.com/angular/protractor/blob/master/lib/protractor.js#L122) +#### Use as: element.all(locator).get(index) +Get an element found by the locator by index. The index starts at 0. +This does not actually retrieve the underlying element. ###Example ```html -
-
- Child text -
{{person.phone}}
-
-
+
    +
  • First
  • +
  • Second
  • +
  • Third
  • +
``` ```javascript -// Chain 2 element calls. -var child = element(by.css('.parent')). - element(by.css('.child')); -expect(child.getText()).toBe('Child text\n555-123-4567'); - -// Chain 3 element calls. -var triple = element(by.css('.parent')). - element(by.css('.child')). - element(by.binding('person.phone')); -expect(triple.getText()).toBe('555-123-4567'); +var list = element.all(by.css('.items li')); +expect(list.get(0).getText()).toBe('First'); +expect(list.get(1).getText()).toBe('Second'); ``` @@ -299,8 +234,7 @@ expect(triple.getText()).toBe('555-123-4567'); Param | Type | Description --- | --- | --- -ptor | [Protractor](#protractor) | -opt_usingChain | Array.<webdriver.Locator>= | +index | number | Element index. @@ -309,57 +243,45 @@ opt_usingChain | Array.<webdriver.Locator>= | Type | Description --- | --- -function(webdriver.Locator): ElementFinder | +[ElementFinder](#elementfinder) | finder representing element at the given index. -##[elementFinder.$](https://github.com/angular/protractor/blob/master/lib/protractor.js#L201) -#### Use as: element(locator).$(cssSelector) -Shortcut for chaining css element finders. +##[ElementArrayFinder.prototype.first](https://github.com/angular/protractor/blob/master/lib/protractor.js#L146) +#### Use as: element.all(locator).first() +Get the first matching element for the locator. This does not actually +retrieve the underlying element. ###Example ```html -
-
- Child text -
{{person.phone}}
-
-
+
    +
  • First
  • +
  • Second
  • +
  • Third
  • +
``` ```javascript -// Chain 2 element calls. -var child = element(by.css('.parent')).$('.child'); -expect(child.getText()).toBe('Child text\n555-123-4567'); - -// Chain 3 element calls. -var triple = $('.parent').$('.child').$('.grandchild'); -expect(triple.getText()).toBe('555-123-4567'); +var first = element.all(by.css('.items li')).first(); +expect(first.getText()).toBe('First'); ``` -###Params - -Param | Type | Description ---- | --- | --- -cssSelector | string | A css selector. - - ###Returns Type | Description --- | --- -ElementFinder | +[ElementFinder](#elementfinder) | finder representing the first matching element -##[element.all](https://github.com/angular/protractor/blob/master/lib/protractor.js#L238) -#### Use as: element.all(locator) -element.all is used for operations on an array of elements (as opposed -to a single element). +##[ElementArrayFinder.prototype.last](https://github.com/angular/protractor/blob/master/lib/protractor.js#L168) +#### Use as: element.all(locator).last() +Get the last matching element for the locator. This does not actually +retrieve the underlying element. ###Example @@ -373,31 +295,22 @@ to a single element). ``` ```javascript -element.all(by.css('.items li')).then(function(items) { - expect(items.length).toBe(3); - expect(items[0].getText()).toBe('First'); -}); +var last = element.all(by.css('.items li')).last(); +expect(last.getText()).toBe('Third'); ``` -###Params - -Param | Type | Description ---- | --- | --- -locator | webdriver.Locator | - - ###Returns Type | Description --- | --- -ElementArrayFinder | +[ElementFinder](#elementfinder) | finder representing the last matching element -##[elementArrayFinder.count](https://github.com/angular/protractor/blob/master/lib/protractor.js#L262) +##[ElementArrayFinder.prototype.count](https://github.com/angular/protractor/blob/master/lib/protractor.js#L190) #### Use as: element.all(locator).count() Count the number of elements found by the locator. @@ -428,9 +341,9 @@ Type | Description !webdriver.promise.Promise | A promise which resolves to the number of elements matching the locator. -##[elementArrayFinder.get](https://github.com/angular/protractor/blob/master/lib/protractor.js#L286) -#### Use as: element.all(locator).get(index) -Get an element found by the locator by index. The index starts at 0. +##[ElementArrayFinder.prototype.each](https://github.com/angular/protractor/blob/master/lib/protractor.js#L257) +#### Use as: element.all(locator).each(eachFunction) +Calls the input function on each ElementFinder found by the locator. ###Example @@ -444,9 +357,10 @@ Get an element found by the locator by index. The index starts at 0. ``` ```javascript -var list = element.all(by.css('.items li')); -expect(list.get(0).getText()).toBe('First'); -expect(list.get(1).getText()).toBe('Second'); +element.all(by.css('.items li')).each(function(element) { + // Will print First, Second, Third. + element.getText().then(console.log); +}); ``` @@ -455,100 +369,148 @@ expect(list.get(1).getText()).toBe('Second'); Param | Type | Description --- | --- | --- -index | number | Element index. +fn | function([ElementFinder](#elementfinder)) | Input function -###Returns -Type | Description ---- | --- -[webdriver.WebElement](#webdriverwebelement) | The element at the given index - - -##[elementArrayFinder.first](https://github.com/angular/protractor/blob/master/lib/protractor.js#L312) -#### Use as: element.all(locator).first() -Get the first element found using the locator. +##[ElementArrayFinder.prototype.map](https://github.com/angular/protractor/blob/master/lib/protractor.js#L284) +#### Use as: element.all(locator).map(mapFunction) +Apply a map function to each element found using the locator. The +callback receives the ElementFinder as the first argument and the index as +a second arg. ###Example ```html
    -
  • First
  • -
  • Second
  • -
  • Third
  • +
  • First
  • +
  • Second
  • +
  • Third
``` ```javascript -var list = element.all(by.css('.items li')); -expect(list.first().getText()).toBe('First'); +var items = element.all(by.css('.items li')).map(function(elm, index) { + return { + index: index, + text: elm.getText(), + class: elm.getAttribute('class') + }; +}); +expect(items).toEqual([ + {index: 0, text: 'First', class: 'one'}, + {index: 1, text: 'Second', class: 'two'}, + {index: 2, text: 'Third', class: 'three'} +]); ``` +###Params + +Param | Type | Description +--- | --- | --- +mapFn | function([ElementFinder](#elementfinder), number) | Map function that will be applied to each element. + + ###Returns Type | Description --- | --- -[webdriver.WebElement](#webdriverwebelement) | The first matching element +!webdriver.promise.Promise | A promise that resolves to an array of values returned by the map function. -##[elementArrayFinder.last](https://github.com/angular/protractor/blob/master/lib/protractor.js#L339) -#### Use as: element.all(locator).last() -Get the last matching element for the locator. +##[ElementFinder](https://github.com/angular/protractor/blob/master/lib/protractor.js#L330) +#### Use as: element(locator) +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. + +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 +until an action is called, which means they can be set up in helper files +before the page is available. ###Example ```html -
    -
  • First
  • -
  • Second
  • -
  • Third
  • -
+{{person.name}} + + ``` ```javascript -var list = element.all(by.css('.items li')); -expect(list.last().getText()).toBe('Third'); +// Find element with {{scopeVar}} syntax. +element(by.binding('person.name')).getText().then(function(name) { + expect(name).toBe('Foo'); +}); + +// Find element with ng-bind="scopeVar" syntax. +expect(element(by.binding('person.email')).getText()).toBe('foo@bar.com'); + +// Find by model. +var input = element(by.model('person.name')); +input.sendKeys('123'); +expect(input.getAttribute('value')).toBe('Foo123'); ``` +###Params + +Param | Type | Description +--- | --- | --- +locator | webdriver.Locator | An element locator. +opt_parentElementFinder | [ElementFinder](#elementfinder)= | The element finder previous to this. (i.e. opt_parentElementFinder.element(locator) => this) +opt_actionResult | webdriver.promise.Promise | The promise which will be retrieved with then. Resolves to the latest action result, or null if no action has been called. +opt_index | number= | The index of the element to retrieve. null means retrieve the only element, while -1 means retrieve the last element + + ###Returns Type | Description --- | --- -[webdriver.WebElement](#webdriverwebelement) | the last matching element +[ElementFinder](#elementfinder) | -##[elementArrayFinder.each](https://github.com/angular/protractor/blob/master/lib/protractor.js#L389) -#### Use as: element.all(locator).each(eachFunction) -Calls the input function on each WebElement found by the locator. +##[ElementFinder.prototype.element](https://github.com/angular/protractor/blob/master/lib/protractor.js#L398) +#### Use as: element(locator).element(locator) +Calls to element may be chained to find elements within a parent. ###Example ```html -
    -
  • First
  • -
  • Second
  • -
  • Third
  • -
+
+
+ Child text +
{{person.phone}}
+
+
``` ```javascript -element.all(by.css('.items li')).each(function(element) { - // Will print First, Second, Third. - element.getText().then(console.log); -}); +// Chain 2 element calls. +var child = element(by.css('.parent')). + element(by.css('.child')); +expect(child.getText()).toBe('Child text\n555-123-4567'); + +// Chain 3 element calls. +var triple = element(by.css('.parent')). + element(by.css('.child')). + element(by.binding('person.phone')); +expect(triple.getText()).toBe('555-123-4567'); ``` @@ -557,42 +519,37 @@ element.all(by.css('.items li')).each(function(element) { Param | Type | Description --- | --- | --- -fn | function([webdriver.WebElement](#webdriverwebelement)) | Input function +subLocator | webdriver.Locator | +###Returns -##[elementArrayFinder.map](https://github.com/angular/protractor/blob/master/lib/protractor.js#L416) -#### Use as: element.all(locator).map(mapFunction) -Apply a map function to each element found using the locator. The -callback receives the web element as the first argument and the index as -a second arg. +Type | Description +--- | --- +[ElementFinder](#elementfinder) | + + +##[ElementFinder.prototype.all](https://github.com/angular/protractor/blob/master/lib/protractor.js#L429) +#### Use as: element(locator).all(locator) +Calls to element may be chained to find an array of elements within a parent. ###Example ```html -
    -
  • First
  • -
  • Second
  • -
  • Third
  • -
+
+
    +
  • First
  • +
  • Second
  • +
  • Third
  • +
+
``` ```javascript -var items = element.all(by.css('.items li')).map(function(elm, index) { - return { - index: index, - text: elm.getText(), - class: elm.getAttribute('class') - }; -}); -expect(items).toEqual([ - {index: 0, text: 'First', class: 'one'}, - {index: 1, text: 'Second', class: 'two'}, - {index: 2, text: 'Third', class: 'three'} -]); +var items = element(by.css('.parent')).all(by.tagName('li')) ``` @@ -601,7 +558,7 @@ expect(items).toEqual([ Param | Type | Description --- | --- | --- -mapFn | function([webdriver.WebElement](#webdriverwebelement), number) | Map function that will be applied to each element. +subLocator | webdriver.Locator | @@ -610,13 +567,27 @@ mapFn | function([webdriver.WebElement](#webdriverwebelement), number) | Map fun Type | Description --- | --- -!webdriver.promise.Promise | A promise that resolves to an array of values returned by the map function. +[ElementArrayFinder](#elementarrayfinder) | -##[Protractor](https://github.com/angular/protractor/blob/master/lib/protractor.js#L494) +##[ElementFinder.prototype.$](https://github.com/angular/protractor/blob/master/lib/protractor.js#L452) +#### Use as: $(cssSelector) +Shortcut for querying the document directly with css. + +###Example +```html +
+ First + Second +
+``` +```javascript +var item = $('.count .two'); +expect(item.getText()).toBe('Second'); +``` @@ -624,20 +595,7 @@ Type | Description Param | Type | Description --- | --- | --- -webdriver | [webdriver.WebDriver](#webdriverwebdriver) | -opt_baseUrl | string= | A base URL to run get requests against. -opt_rootElement | string= | Selector element that has an ng-app in scope. - - - - - -##[Protractor.prototype.waitForAngular](https://github.com/angular/protractor/blob/master/lib/protractor.js#L589) - -Instruct webdriver to wait until Angular has finished rendering and has -no outstanding $http calls before continuing. - - +selector | string | A css selector @@ -646,13 +604,33 @@ no outstanding $http calls before continuing. Type | Description --- | --- -!webdriver.promise.Promise | A promise that will resolve to the scripts return value. +[ElementFinder](#elementfinder) | which identifies the located [webdriver.WebElement](#webdriverwebelement) -##[Protractor.prototype.wrapWebElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L630) +##[ElementFinder.prototype.$$](https://github.com/angular/protractor/blob/master/lib/protractor.js#L474) +#### Use as: $$(cssSelector) +Shortcut for querying the document directly with css. + + +###Example + +```html +
+ First + Second +
+``` -Wrap a webdriver.WebElement with protractor specific functionality. +```javascript +// The following protractor expressions are equivalent. +var list = element.all(by.css('.count span')); +expect(list.count()).toBe(2); +list = $$('.count span'); +expect(list.count()).toBe(2); +expect(list.get(0).getText()).toBe('First'); +expect(list.get(1).getText()).toBe('Second'); +``` @@ -660,7 +638,7 @@ Wrap a webdriver.WebElement with protractor specific functionality. Param | Type | Description --- | --- | --- -element | [webdriver.WebElement](#webdriverwebelement) | +selector | string | a css selector @@ -669,35 +647,45 @@ element | [webdriver.WebElement](#webdriverwebelement) | Type | Description --- | --- -[webdriver.WebElement](#webdriverwebelement) | the wrapped web element. +[ElementArrayFinder](#elementarrayfinder) | which identifies the array of the located [webdriver.WebElement](#webdriverwebelement)s. -##[element.$](https://github.com/angular/protractor/blob/master/lib/protractor.js#L655) -#### Use as: $(cssSelector) -Shortcut for querying the document directly with css. +##[ElementFinder.prototype.isPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L502) +#### Use as: element(locator).isPresent() +Determine whether the element is present on the page. ###Example ```html -
- First - Second -
+{{person.name}} ``` ```javascript -var item = $('.count .two'); -expect(item.getText()).toBe('Second'); +// Element exists. +expect(element(by.binding('person.name')).isPresent()).toBe(true); + +// Element not present. +expect(element(by.binding('notPresent')).isPresent()).toBe(false); ``` -###Params -Param | Type | Description ---- | --- | --- -selector | string | A css selector + +###Returns + +Type | Description +--- | --- +[ElementFinder](#elementfinder) | which resolves to whether the element is present on the page. + + +##[ElementFinder.prototype.isElementPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L529) + +Override for WebElement.prototype.isElementPresent so that protractor waits +for Angular to settle before making the check. + + @@ -706,10 +694,10 @@ selector | string | A css selector Type | Description --- | --- -![webdriver.WebElement](#webdriverwebelement) | +[ElementFinder](#elementfinder) | which resolves to whether the element is present on the page. -##[element.findElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L678) +##[ElementFinder.prototype.locator](https://github.com/angular/protractor/blob/master/lib/protractor.js#L541) @@ -722,33 +710,43 @@ Type | Description Type | Description --- | --- -![webdriver.WebElement](#webdriverwebelement) | +webdriver.Locator | -##[element.$$](https://github.com/angular/protractor/blob/master/lib/protractor.js#L695) -#### Use as: $$(cssSelector) -Shortcut for querying the document directly with css. +##[ElementFinder.prototype.getWebElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L548) +#### Use as: element(locator).getWebElement() +Returns the WebElement represented by this ElementFinder. +Throws the WebDriver error if the element doesn't exist. +If index is null, it makes sure that there is only one underlying +WebElement described by the chain of locators and issues a warning +otherwise. If index is not null, it retrieves the WebElement specified by +the index. ###Example -```html -
- First - Second -
+```javascript +The following three expressions are equivalent. + - element(by.css('.parent')).getWebElement(); + - browser.waitForAngular(); browser.driver.findElement(by.css('.parent')); + - browser.findElement(by.css('.parent')) ``` -```javascript -// The following protractor expressions are equivalent. -var list = element.all(by.css('.count span')); -expect(list.count()).toBe(2); -list = $$('.count span'); -expect(list.count()).toBe(2); -expect(list.get(0).getText()).toBe('First'); -expect(list.get(1).getText()).toBe('Second'); -``` + + + +###Returns + +Type | Description +--- | --- +[webdriver.WebElement](#webdriverwebelement) | + + +##[ElementFinder.prototype.evaluate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L591) + +Evaluates the input as if it were on the scope of the current element. + @@ -756,7 +754,7 @@ expect(list.get(1).getText()).toBe('Second'); Param | Type | Description --- | --- | --- -selector | string | a css selector +expression | string | @@ -765,14 +763,21 @@ selector | string | a css selector Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved to an array of the located [webdriver.WebElement](#webdriverwebelement)s. +[ElementFinder](#elementfinder) | which resolves to the evaluated expression. The result will be resolved as in {@link webdriver.WebDriver.executeScript}. In summary - primitives will be resolved as is, functions will be converted to string, and elements will be returned as a WebElement. + + +##[ElementFinder.prototype.allowAnimations](https://github.com/angular/protractor/blob/master/lib/protractor.js#L609) +Determine if animation is allowed on the current element. -##[element.findElements](https://github.com/angular/protractor/blob/master/lib/protractor.js#L725) +###Params +Param | Type | Description +--- | --- | --- +value | string | @@ -781,15 +786,23 @@ Type | Description Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved to an array of the located [webdriver.WebElement](#webdriverwebelement)s. +[ElementFinder](#elementfinder) | which resolves to whether animation is allowed. -##[element.isElementPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L749) +##[ElementFinder.prototype.then](https://github.com/angular/protractor/blob/master/lib/protractor.js#L623) +Access the underlying actionResult of ElementFinder. Implementation allows +ElementFinder to be used as a webdriver.promise.Promise +###Params + +Param | Type | Description +--- | --- | --- +fn | function(webdriver.promise.Promise) | Function which takes the value of the underlying actionResult. + @@ -797,12 +810,12 @@ Type | Description Type | Description --- | --- -!webdriver.promise.Promise | A promise that will be resolved with whether an element could be located on the page. +webdriver.promise.Promise | Promise which contains the results of evaluating fn. -##[element.evaluate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L765) +##[Protractor](https://github.com/angular/protractor/blob/master/lib/protractor.js#L649) + -Evaluates the input as if it were on the scope of the current element. @@ -811,7 +824,20 @@ Evaluates the input as if it were on the scope of the current element. Param | Type | Description --- | --- | --- -expression | string | +webdriver | [webdriver.WebDriver](#webdriverwebdriver) | +opt_baseUrl | string= | A base URL to run get requests against. +opt_rootElement | string= | Selector element that has an ng-app in scope. + + + + + +##[Protractor.prototype.waitForAngular](https://github.com/angular/protractor/blob/master/lib/protractor.js#L749) + +Instruct webdriver to wait until Angular has finished rendering and has +no outstanding $http calls before continuing. + + @@ -820,10 +846,10 @@ expression | string | Type | Description --- | --- -!webdriver.promise.Promise | A promise that will resolve to the evaluated expression. The result will be resolved as in {@link webdriver.WebDriver.executeScript}. In summary - primitives will be resolved as is, functions will be converted to string, and elements will be returned as a WebElement. +!webdriver.promise.Promise | A promise that will resolve to the scripts return value. -##[Protractor.prototype.findElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L784) +##[Protractor.prototype.findElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L788) Waits for Angular to finish rendering before searching for elements. @@ -839,7 +865,7 @@ Type | Description ![webdriver.WebElement](#webdriverwebelement) | -##[Protractor.prototype.findElements](https://github.com/angular/protractor/blob/master/lib/protractor.js#L802) +##[Protractor.prototype.findElements](https://github.com/angular/protractor/blob/master/lib/protractor.js#L797) Waits for Angular to finish rendering before searching for elements. @@ -855,7 +881,7 @@ Type | Description !webdriver.promise.Promise | A promise that will be resolved to an array of the located [webdriver.WebElement](#webdriverwebelement)s. -##[Protractor.prototype.isElementPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L827) +##[Protractor.prototype.isElementPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L807) Tests if an element is present on the page. @@ -871,7 +897,7 @@ Type | Description !webdriver.promise.Promise | A promise that will resolve to whether the element is present on the page. -##[Protractor.prototype.addMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L843) +##[Protractor.prototype.addMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L819) Add a module to load before Angular whenever Protractor.get is called. Modules will be registered after existing modules already on the page, @@ -893,7 +919,7 @@ varArgs | ...* | Any additional arguments will be provided to the script and may -##[Protractor.prototype.clearMockModules](https://github.com/angular/protractor/blob/master/lib/protractor.js#L861) +##[Protractor.prototype.clearMockModules](https://github.com/angular/protractor/blob/master/lib/protractor.js#L837) Clear the list of registered mock modules. @@ -903,7 +929,7 @@ Clear the list of registered mock modules. -##[Protractor.prototype.removeMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L870) +##[Protractor.prototype.removeMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L846) Remove a registered mock module. @@ -920,7 +946,7 @@ name | !string | The name of the module to remove. -##[Protractor.prototype.get](https://github.com/angular/protractor/blob/master/lib/protractor.js#L881) +##[Protractor.prototype.get](https://github.com/angular/protractor/blob/master/lib/protractor.js#L857) See webdriver.WebDriver.get @@ -943,7 +969,7 @@ opt_timeout | number= | Number of seconds to wait for Angular to start. -##[Protractor.prototype.refresh](https://github.com/angular/protractor/blob/master/lib/protractor.js#L949) +##[Protractor.prototype.refresh](https://github.com/angular/protractor/blob/master/lib/protractor.js#L937) See webdriver.WebDriver.refresh @@ -965,7 +991,7 @@ opt_timeout | number= | Number of seconds to wait for Angular to start. -##[Protractor.prototype.navigate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L972) +##[Protractor.prototype.navigate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L960) Mixin navigation methods back into the navigation object so that they are invoked as before, i.e. driver.navigate().refresh() @@ -976,7 +1002,7 @@ they are invoked as before, i.e. driver.navigate().refresh() -##[Protractor.prototype.setLocation](https://github.com/angular/protractor/blob/master/lib/protractor.js#L982) +##[Protractor.prototype.setLocation](https://github.com/angular/protractor/blob/master/lib/protractor.js#L970) Browse to another page using in-page navigation. @@ -999,7 +1025,7 @@ Type | Description !webdriver.promise.Promise | A promise that will resolve once page has been changed. -##[Protractor.prototype.getLocationAbsUrl](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1000) +##[Protractor.prototype.getLocationAbsUrl](https://github.com/angular/protractor/blob/master/lib/protractor.js#L988) Returns the current absolute url from AngularJS. @@ -1009,7 +1035,7 @@ Returns the current absolute url from AngularJS. -##[Protractor.prototype.debugger](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1008) +##[Protractor.prototype.debugger](https://github.com/angular/protractor/blob/master/lib/protractor.js#L996) Pauses the test and injects some helper functions into the browser, so that debugging may be done in the browser console. @@ -1034,7 +1060,7 @@ debugger. -##[Protractor.prototype.pause](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1043) +##[Protractor.prototype.pause](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1021) Beta (unstable) pause function for debugging webdriver tests. Use browser.pause() in your test to enter the protractor debugger from that @@ -1175,67 +1201,7 @@ Type | Description {findElementsOverride: findElementsOverride, message: string} | -##[ProtractorBy.prototype.select](https://github.com/angular/protractor/blob/master/lib/locators.js#L122) - -**DEPRECATED** Use 'model' instead. - - -###Example - -```html - -``` - -```javascript -element(by.select('user')); -``` - - - - - - -##[ProtractorBy.prototype.selectedOption](https://github.com/angular/protractor/blob/master/lib/locators.js#L141) - - - - -###Example - -```html - -``` - -```javascript -element(by.selectedOption("user")); -``` - - - - - - -##[ProtractorBy.prototype.input](https://github.com/angular/protractor/blob/master/lib/locators.js#L158) - -**DEPRECATED** Use 'model' instead. - - -###Example - -```html - -``` - -```javascript -element(by.input('user')); -``` - - - - - - -##[ProtractorBy.prototype.model](https://github.com/angular/protractor/blob/master/lib/locators.js#L176) +##[ProtractorBy.prototype.model](https://github.com/angular/protractor/blob/master/lib/locators.js#L122) #### Use as: by.model(modelName) Find an element by ng-model expression. @@ -1264,7 +1230,7 @@ model | string | ng-model expression. -##[ProtractorBy.prototype.buttonText](https://github.com/angular/protractor/blob/master/lib/locators.js#L200) +##[ProtractorBy.prototype.buttonText](https://github.com/angular/protractor/blob/master/lib/locators.js#L146) Find a button by text. @@ -1297,7 +1263,7 @@ Type | Description {findElementsOverride: findElementsOverride, message: string} | -##[ProtractorBy.prototype.partialButtonText](https://github.com/angular/protractor/blob/master/lib/locators.js#L223) +##[ProtractorBy.prototype.partialButtonText](https://github.com/angular/protractor/blob/master/lib/locators.js#L169) Find a button by partial text. @@ -1330,27 +1296,7 @@ Type | Description {findElementsOverride: findElementsOverride, message: string} | -##[ProtractorBy.prototype.textarea](https://github.com/angular/protractor/blob/master/lib/locators.js#L247) - -**DEPRECATED** Use 'model' instead. - - -###Example - -```html - -``` - -```javascript -element(by.textarea('user')); -``` - - - - - - -##[ProtractorBy.prototype.repeater](https://github.com/angular/protractor/blob/master/lib/locators.js#L265) +##[ProtractorBy.prototype.repeater](https://github.com/angular/protractor/blob/master/lib/locators.js#L193) Find elements inside an ng-repeat. @@ -1362,6 +1308,14 @@ Find elements inside an ng-repeat. {{cat.name}} {{cat.age}} + +
+ +
+
+

{{book.name}}

+

{{book.blurb}}

+
``` ```javascript @@ -1377,8 +1331,26 @@ var ages = element.all( by.repeater('cat in pets').column('{{cat.age}}')); // Returns a promise that resolves to an array of WebElements containing -// all rows of the repeater. +// all top level elements repeated by the repeater. For 2 pets rows resolves +// to an array of 2 elements. var rows = element.all(by.repeater('cat in pets')); + +// Returns a promise that resolves to an array of WebElements containing all +// the elements with a binding to the book's name. +var divs = element.all(by.repeater('book in library').column('book.name')); + +// Returns a promise that resolves to an array of WebElements containing +// the DIVs for the second book. +var bookInfo = element.all(by.repeater('book in library').row(1)); + +// Returns the H4 for the first book's name. +var firstBookName = element(by.repeater('book in library'). + row(0).column('{{book.name}}')); + +// Returns a promise that resolves to an array of WebElements containing +// all top level elements repeated by the repeater. For 2 books divs +// resolves to an array of 4 elements. +var divs = element.all(by.repeater('book in library')); ``` @@ -1386,7 +1358,7 @@ var rows = element.all(by.repeater('cat in pets')); -##[ProtractorBy.prototype.cssContainingText](https://github.com/angular/protractor/blob/master/lib/locators.js#L344) +##[ProtractorBy.prototype.cssContainingText](https://github.com/angular/protractor/blob/master/lib/locators.js#L298) Find elements by CSS which contain a certain string. diff --git a/package.json b/package.json index f278f0f86..e35903a36 100644 --- a/package.json +++ b/package.json @@ -53,10 +53,10 @@ "start": "testapp/scripts/web-server.js" }, "license": "MIT", - "version": "0.23.1", + "version": "0.24.0", "webdriverVersions": { "selenium": "2.42.0", "chromedriver": "2.10", - "iedriver": "2.41.0" + "iedriver": "2.42.0" } } From 1548273f9971e0228a2a886370b0337e5d6eb411 Mon Sep 17 00:00:00 2001 From: Julie Date: Wed, 4 Jun 2014 13:50:47 -0700 Subject: [PATCH 019/612] docs(release): udpate release notes with more useful links and version info --- release.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/release.md b/release.md index c77d5f845..31e86769b 100644 --- a/release.md +++ b/release.md @@ -5,7 +5,11 @@ Say the previous release was 0.0.J, the current release is 0.0.K, and the next r - Check that features and bug fixes are in by looking at the milestone tag for 0.0.K. Create a milestone for 0.0.L, and bump anything that doesn't need to be finished from 0.0.K to 0.0.L. - - Check if there are new versions of selenium, chromedriver, or latest browsers that the configuration needs to be updated against. + - Check if there are new versions of [selenium and iedriver](http://selenium-release.storage.googleapis.com/index.html), [chromedriver](http://chromedriver.storage.googleapis.com/index.html), or [latest browsers](https://saucelabs.com/platforms) that the configuration needs to be updated against. We test against the latest two versions of Chrome, Firefox, and IE. + + - The latest selenium version should be used in spec/ciConf.js and spec/smokeConf.js. + - The versions in package.json/webdriverVersions should be up to date, and you should run `webdriver-manager update` locally. + - The latest version of Chrome and Firefox should be used in spec/ciConf.js. All other browsers we support should be listed in spec/smokeConf.js. - Make sure [Travis](https://travis-ci.org/angular/protractor/builds) is passing @@ -33,7 +37,7 @@ Say the previous release was 0.0.J, the current release is 0.0.K, and the next r - Push to github - - Push tags to github (`git push upstream --tags`) + - Push tags to github (`git push --tags`) - Verify that the changelog and tags look sane on github From ac34a77e391e103912e4a4097b5b9ef7a22c148f Mon Sep 17 00:00:00 2001 From: Julie Date: Wed, 4 Jun 2014 16:40:34 -0700 Subject: [PATCH 020/612] docs(api): match the version for the api docs The version doesn't update properly with our current release process because the tagging comes after generating new docs. We should fix this. Manually correcting for now. --- docs/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api.md b/docs/api.md index be933f0be..768823ca6 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,4 +1,4 @@ -Protractor API 0.23.1 +Protractor API 0.24.0 ============== From 72668fe5ebbdc8126ff16887814f763198128ab5 Mon Sep 17 00:00:00 2001 From: Julie Date: Thu, 5 Jun 2014 16:02:31 -0700 Subject: [PATCH 021/612] fix(cssShortcut): fix $$ global throwing error --- lib/protractor.js | 2 +- spec/basic/elements_spec.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/protractor.js b/lib/protractor.js index 563b77654..653715931 100644 --- a/lib/protractor.js +++ b/lib/protractor.js @@ -701,7 +701,7 @@ var Protractor = function(webdriverInstance, opt_baseUrl, opt_rootElement) { * @type {function(string): ElementArrayFinder} */ this.$$ = function(selector) { - return self.all(webdriver.By.css(selector)); + return self.element.all(webdriver.By.css(selector)); }; /** diff --git a/spec/basic/elements_spec.js b/spec/basic/elements_spec.js index 549ce803f..1253760e9 100644 --- a/spec/basic/elements_spec.js +++ b/spec/basic/elements_spec.js @@ -278,6 +278,12 @@ describe('shortcut css notation', function() { beforeEach(function() { browser.get('index.html#/bindings'); }); + + it('should grab by css', function() { + expect($('.planet-info').getText()). + toEqual(element(by.css('.planet-info')).getText()); + expect($$('option').count()).toEqual(element.all(by.css('option')).count()); + }); it('should chain $$ with $', function() { var withoutShortcutCount = From 13373f5de18690e1994b32e092105cfe3ad1753d Mon Sep 17 00:00:00 2001 From: Julie Date: Mon, 9 Jun 2014 15:13:23 -0700 Subject: [PATCH 022/612] fix(launcher): output error messages when child processes exit with error Version 0.24.0 introduced a bug where child processes would error without outputting the error message. Fix. See #902. --- lib/launcher.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/launcher.js b/lib/launcher.js index 5ebde181e..a102f5f7c 100644 --- a/lib/launcher.js +++ b/lib/launcher.js @@ -93,11 +93,13 @@ var init = function(configFile, additionalConfig) { }); this.process.on('error', function(err) { + self.reporter.flush(); log_('Runner Process(' + self.process.pid + ') Error: ' + err); launcherExitCode = 1; }); this.process.on('exit', function(code) { + self.reporter.flush(); if (code) { log_('Runner Process Exited With Error Code: ' + code); launcherExitCode = 1; @@ -224,7 +226,7 @@ var TaskReporter_ = function(task, pid) { TaskReporter_.prototype.reportHeader_ = function() { var capability = this.task.capability; var eol = require('os').EOL; - var output = '------------------------------------' + eol; + var output = eol + '------------------------------------' + eol; output += 'PID: ' + this.pid + ' (capability: '; output += (capability.browserName) ? capability.browserName : ''; @@ -267,9 +269,17 @@ TaskReporter_.prototype.logStderr = function(stderr) { */ TaskReporter_.prototype.testsDone = function(failedCount) { this.failedCount = failedCount; + this.flush(); +}; + +/** + * Flushes the buffer to stdout. + */ +TaskReporter_.prototype.flush = function(failedCount) { if (this.buffer) { // Flush buffer if nonempty process.stdout.write(this.buffer); + this.buffer = ''; } }; From 59af936e1ef6e21432f7876144554db145083d46 Mon Sep 17 00:00:00 2001 From: Uri Shaked Date: Sat, 7 Jun 2014 04:16:49 +0300 Subject: [PATCH 023/612] fix(locators): Missing information in warning/error messages Webdriver's built-in locators (such as `by.css()`) appeared as 'undefined' in protractor's messages. For instance, if a locator matched multiple elements, protractor would print the following message: 'warning: more than one element found for locator undefined- you may need to be more specific'. --- lib/protractor.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/protractor.js b/lib/protractor.js index 653715931..66c2d9a0e 100644 --- a/lib/protractor.js +++ b/lib/protractor.js @@ -568,15 +568,16 @@ var buildElementHelper = function(ptor) { this.locator_, this.parentElementFinder_).getWebElements(); var id = webElementsPromise.then(function(arr) { + var locatorMessage = (self.locator_.message || self.locator_.toString()); if (!arr.length) { - throw new Error('No element found using locator: ' + self.locator_.message); + throw new Error('No element found using locator: ' + locatorMessage); } var index = self.opt_index_; if (index == null) { // index null means we make sure there is only one element if (arr.length > 1) { console.log('warning: more than one element found for locator ' + - self.locator_.message + '- you may need to be more specific'); + locatorMessage + ' - you may need to be more specific'); } index = 0; } else if (index === -1) { From 01926e720c1423ba5d680f174483bf49006ab6f0 Mon Sep 17 00:00:00 2001 From: Julie Date: Mon, 9 Jun 2014 16:03:18 -0700 Subject: [PATCH 024/612] style(launcher): remove unused variable. --- lib/launcher.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/launcher.js b/lib/launcher.js index a102f5f7c..58089a988 100644 --- a/lib/launcher.js +++ b/lib/launcher.js @@ -275,7 +275,7 @@ TaskReporter_.prototype.testsDone = function(failedCount) { /** * Flushes the buffer to stdout. */ -TaskReporter_.prototype.flush = function(failedCount) { +TaskReporter_.prototype.flush = function() { if (this.buffer) { // Flush buffer if nonempty process.stdout.write(this.buffer); From 3a5ece3d29a4f77da6f6dd70adda34121d46c010 Mon Sep 17 00:00:00 2001 From: Julie Date: Mon, 9 Jun 2014 16:06:50 -0700 Subject: [PATCH 025/612] docs(release): API generation should be done after version bump --- release.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release.md b/release.md index 31e86769b..336fd1a43 100644 --- a/release.md +++ b/release.md @@ -17,8 +17,6 @@ Say the previous release was 0.0.J, the current release is 0.0.K, and the next r - Make sure .gitignore and .npmignore are updated with any new files that need to be ignored. - - Generate new API documentation. Run `scripts/generate-docs.js` - - Update package.json with a version bump. If the changes are only bug fixes, increment the patch (e.g. 0.0.5 -> 0.0.6), otherwise increment the minor version. - Update CHANGELOG.md. @@ -31,6 +29,8 @@ Say the previous release was 0.0.J, the current release is 0.0.K, and the next r - Breaking changes should be in their own section and include before/after examples of how to fix code that needs to change. + - Generate new API documentation. Run `scripts/generate-docs.js` + - Make a commit with the API and package.json changes titled chore(release): version bump and changelog for 0.0.K. - Tag the release with `git tag 0.0.K` From 24248d65efc4bdc9bf61dd8833595226b44232fa Mon Sep 17 00:00:00 2001 From: Julie Date: Mon, 9 Jun 2014 16:10:04 -0700 Subject: [PATCH 026/612] chore(release): version bump, changelog, and API for 0.24.1 --- CHANGELOG.md | 24 ++++++++++++++++++++++++ docs/api.md | 38 +++++++++++++++++++------------------- package.json | 2 +- 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87b3d8309..c4caf86cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +# 0.24.1 +_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._ + +## Bug Fixes + +- ([59af936](https://github.com/angular/protractor/commit/59af936e1ef6e21432f7876144554db145083d46)) + fix(locators): Missing information in warning/error messages + + Webdriver's built-in locators (such as `by.css()`) appeared as + 'undefined' in protractor's messages. + + For instance, if a locator matched multiple elements, protractor would print the following + message: 'warning: more than one element found for locator undefined- you may need to be more + specific'. + +- ([13373f5](https://github.com/angular/protractor/commit/13373f5de18690e1994b32e092105cfe3ad1753d)) + fix(launcher): output error messages when child processes exit with error + + Version 0.24.0 introduced a bug where child processes would error without outputting the error + message. Fix. See #902. + +- ([72668fe](https://github.com/angular/protractor/commit/72668fe5ebbdc8126ff16887814f763198128ab5)) + fix(cssShortcut): fix $$ global throwing error + # 0.24.0 _Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._ diff --git a/docs/api.md b/docs/api.md index 768823ca6..16b890a78 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,4 +1,4 @@ -Protractor API 0.24.0 +Protractor API 0.24.1 ============== @@ -743,7 +743,7 @@ Type | Description [webdriver.WebElement](#webdriverwebelement) | -##[ElementFinder.prototype.evaluate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L591) +##[ElementFinder.prototype.evaluate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L592) Evaluates the input as if it were on the scope of the current element. @@ -766,7 +766,7 @@ Type | Description [ElementFinder](#elementfinder) | which resolves to the evaluated expression. The result will be resolved as in {@link webdriver.WebDriver.executeScript}. In summary - primitives will be resolved as is, functions will be converted to string, and elements will be returned as a WebElement. -##[ElementFinder.prototype.allowAnimations](https://github.com/angular/protractor/blob/master/lib/protractor.js#L609) +##[ElementFinder.prototype.allowAnimations](https://github.com/angular/protractor/blob/master/lib/protractor.js#L610) Determine if animation is allowed on the current element. @@ -789,7 +789,7 @@ Type | Description [ElementFinder](#elementfinder) | which resolves to whether animation is allowed. -##[ElementFinder.prototype.then](https://github.com/angular/protractor/blob/master/lib/protractor.js#L623) +##[ElementFinder.prototype.then](https://github.com/angular/protractor/blob/master/lib/protractor.js#L624) Access the underlying actionResult of ElementFinder. Implementation allows ElementFinder to be used as a webdriver.promise.Promise @@ -813,7 +813,7 @@ Type | Description webdriver.promise.Promise | Promise which contains the results of evaluating fn. -##[Protractor](https://github.com/angular/protractor/blob/master/lib/protractor.js#L649) +##[Protractor](https://github.com/angular/protractor/blob/master/lib/protractor.js#L650) @@ -832,7 +832,7 @@ opt_rootElement | string= | Selector element that has an ng-app in scope. -##[Protractor.prototype.waitForAngular](https://github.com/angular/protractor/blob/master/lib/protractor.js#L749) +##[Protractor.prototype.waitForAngular](https://github.com/angular/protractor/blob/master/lib/protractor.js#L750) Instruct webdriver to wait until Angular has finished rendering and has no outstanding $http calls before continuing. @@ -849,7 +849,7 @@ Type | Description !webdriver.promise.Promise | A promise that will resolve to the scripts return value. -##[Protractor.prototype.findElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L788) +##[Protractor.prototype.findElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L789) Waits for Angular to finish rendering before searching for elements. @@ -865,7 +865,7 @@ Type | Description ![webdriver.WebElement](#webdriverwebelement) | -##[Protractor.prototype.findElements](https://github.com/angular/protractor/blob/master/lib/protractor.js#L797) +##[Protractor.prototype.findElements](https://github.com/angular/protractor/blob/master/lib/protractor.js#L798) Waits for Angular to finish rendering before searching for elements. @@ -881,7 +881,7 @@ Type | Description !webdriver.promise.Promise | A promise that will be resolved to an array of the located [webdriver.WebElement](#webdriverwebelement)s. -##[Protractor.prototype.isElementPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L807) +##[Protractor.prototype.isElementPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L808) Tests if an element is present on the page. @@ -897,7 +897,7 @@ Type | Description !webdriver.promise.Promise | A promise that will resolve to whether the element is present on the page. -##[Protractor.prototype.addMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L819) +##[Protractor.prototype.addMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L820) Add a module to load before Angular whenever Protractor.get is called. Modules will be registered after existing modules already on the page, @@ -919,7 +919,7 @@ varArgs | ...* | Any additional arguments will be provided to the script and may -##[Protractor.prototype.clearMockModules](https://github.com/angular/protractor/blob/master/lib/protractor.js#L837) +##[Protractor.prototype.clearMockModules](https://github.com/angular/protractor/blob/master/lib/protractor.js#L838) Clear the list of registered mock modules. @@ -929,7 +929,7 @@ Clear the list of registered mock modules. -##[Protractor.prototype.removeMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L846) +##[Protractor.prototype.removeMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L847) Remove a registered mock module. @@ -946,7 +946,7 @@ name | !string | The name of the module to remove. -##[Protractor.prototype.get](https://github.com/angular/protractor/blob/master/lib/protractor.js#L857) +##[Protractor.prototype.get](https://github.com/angular/protractor/blob/master/lib/protractor.js#L858) See webdriver.WebDriver.get @@ -969,7 +969,7 @@ opt_timeout | number= | Number of seconds to wait for Angular to start. -##[Protractor.prototype.refresh](https://github.com/angular/protractor/blob/master/lib/protractor.js#L937) +##[Protractor.prototype.refresh](https://github.com/angular/protractor/blob/master/lib/protractor.js#L938) See webdriver.WebDriver.refresh @@ -991,7 +991,7 @@ opt_timeout | number= | Number of seconds to wait for Angular to start. -##[Protractor.prototype.navigate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L960) +##[Protractor.prototype.navigate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L961) Mixin navigation methods back into the navigation object so that they are invoked as before, i.e. driver.navigate().refresh() @@ -1002,7 +1002,7 @@ they are invoked as before, i.e. driver.navigate().refresh() -##[Protractor.prototype.setLocation](https://github.com/angular/protractor/blob/master/lib/protractor.js#L970) +##[Protractor.prototype.setLocation](https://github.com/angular/protractor/blob/master/lib/protractor.js#L971) Browse to another page using in-page navigation. @@ -1025,7 +1025,7 @@ Type | Description !webdriver.promise.Promise | A promise that will resolve once page has been changed. -##[Protractor.prototype.getLocationAbsUrl](https://github.com/angular/protractor/blob/master/lib/protractor.js#L988) +##[Protractor.prototype.getLocationAbsUrl](https://github.com/angular/protractor/blob/master/lib/protractor.js#L989) Returns the current absolute url from AngularJS. @@ -1035,7 +1035,7 @@ Returns the current absolute url from AngularJS. -##[Protractor.prototype.debugger](https://github.com/angular/protractor/blob/master/lib/protractor.js#L996) +##[Protractor.prototype.debugger](https://github.com/angular/protractor/blob/master/lib/protractor.js#L997) Pauses the test and injects some helper functions into the browser, so that debugging may be done in the browser console. @@ -1060,7 +1060,7 @@ debugger. -##[Protractor.prototype.pause](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1021) +##[Protractor.prototype.pause](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1022) Beta (unstable) pause function for debugging webdriver tests. Use browser.pause() in your test to enter the protractor debugger from that diff --git a/package.json b/package.json index e35903a36..d77fa50a0 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "start": "testapp/scripts/web-server.js" }, "license": "MIT", - "version": "0.24.0", + "version": "0.24.1", "webdriverVersions": { "selenium": "2.42.0", "chromedriver": "2.10", From 46ef24b3bd4fe18c4b135f94e8a6d830f3a97f8d Mon Sep 17 00:00:00 2001 From: Syu Kato Date: Fri, 13 Jun 2014 17:20:08 +0900 Subject: [PATCH 027/612] style(api): fix typo --- docs/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api.md b/docs/api.md index 16b890a78..c49874594 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1685,7 +1685,7 @@ driver.executeAsyncScript( 'var callback = arguments[arguments.length - 1];' + 'mailClient.getComposeWindowWidget().onload(callback);'); driver.switchTo().frame('composeWidget'); -driver.findElement(By.id('to')).sendKEys('dog@example.com'); +driver.findElement(By.id('to')).sendKeys('dog@example.com'); Example #3: Injecting a XMLHttpRequest and waiting for the result. In this From 31d42a3875c5b95893d8a20d00dc5365c289ff98 Mon Sep 17 00:00:00 2001 From: Hank Duan Date: Mon, 16 Jun 2014 14:36:28 -0700 Subject: [PATCH 028/612] fix(protractor): throw index-out-of-bounds See https://github.com/angular/protractor/issues/915 - to make error more specific instead of propagate later --- lib/protractor.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/protractor.js b/lib/protractor.js index 66c2d9a0e..f43dbf2a0 100644 --- a/lib/protractor.js +++ b/lib/protractor.js @@ -584,6 +584,12 @@ var buildElementHelper = function(ptor) { // -1 is special and means last index = arr.length - 1; } + + if (index >= arr.length) { + throw new Error('Index out of bound. Trying to access index:' + index + + ', but locator: ' + locatorMessage + ' only has ' + + arr.length + ' elements'); + } return arr[index]; }); return new webdriver.WebElement(ptor.driver, id); From a43f98391d36cead7378d1dd26f54248f39300b7 Mon Sep 17 00:00:00 2001 From: Hank Duan Date: Mon, 16 Jun 2014 18:36:46 -0700 Subject: [PATCH 029/612] fix(protractor): make ElementFinder.then resolve to itself instead of null --- lib/protractor.js | 12 ++++++++---- package.json | 2 +- spec/basic/locators_spec.js | 20 ++++++++++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/protractor.js b/lib/protractor.js index f43dbf2a0..bc24738b4 100644 --- a/lib/protractor.js +++ b/lib/protractor.js @@ -377,7 +377,7 @@ var buildElementHelper = function(ptor) { } this.locator_ = locator; this.parentElementFinder_ = opt_parentElementFinder || null; - this.actionResult_ = opt_actionResult || webdriver.promise.fulfilled(null); + this.opt_actionResult_ = opt_actionResult; this.opt_index_ = opt_index; var self = this; @@ -637,9 +637,13 @@ var buildElementHelper = function(ptor) { * evaluating fn. */ ElementFinder.prototype.then = function(fn) { - return this.actionResult_.then(function() { - return fn.apply(null, arguments); - }); + if (this.opt_actionResult_) { + return this.opt_actionResult_.then(function() { + return fn.apply(null, arguments); + }); + } else { + return fn(this); + } }; var element = function(locator) { diff --git a/package.json b/package.json index d77fa50a0..5871f51bd 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "request": "~2.36.0", "selenium-webdriver": "2.42.0", "minijasminenode": "0.4.0", - "jasminewd": "1.0.0", + "jasminewd": "1.0.1", "saucelabs": "~0.1.0", "glob": "~3.2", "adm-zip": "0.4.4", diff --git a/spec/basic/locators_spec.js b/spec/basic/locators_spec.js index ad0cf6297..879cf6578 100644 --- a/spec/basic/locators_spec.js +++ b/spec/basic/locators_spec.js @@ -10,6 +10,26 @@ describe('locators', function() { expect(greeting.getText()).toEqual('Hiya'); }); + it('should allow custom expectations to expect an element', function() { + this.addMatchers({ + toHaveText: function(actualText) { + return this.actual.getText().then(function(expectedText) { + return expectedText === actualText; + }); + } + }); + + expect(element(by.binding('{{greeting}}'))).toHaveText('Hiya'); + }); + + it('ElementFinder.then should resolve to itself', function() { + var elem = element(by.binding('{{greeting}}')); + + elem.then(function(elem2) { + expect(elem).toEqual(elem2); + }); + }); + it('should find a binding by partial match', function() { var greeting = element(by.binding('greet')); From a69fe830becd6f570a3b0355847e12f9baeef67d Mon Sep 17 00:00:00 2001 From: Julie Date: Tue, 17 Jun 2014 10:10:53 -0700 Subject: [PATCH 030/612] test(count): test that element.all.count returns 0 if no elements are found --- spec/basic/elements_spec.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/basic/elements_spec.js b/spec/basic/elements_spec.js index 1253760e9..1341181ee 100644 --- a/spec/basic/elements_spec.js +++ b/spec/basic/elements_spec.js @@ -133,6 +133,12 @@ describe('ElementFinder', function() { expect(element.all(by.model('color')).count()).toEqual(3); }); + it('should return 0 when counting no elements', function() { + browser.get('index.html#/form'); + + expect(element.all(by.binding('doesnotexist')).count()).toEqual(0); + }); + it('should get an element from an array', function() { var colorList = element.all(by.model('color')); From 8582b195ed0f4d48a0d9513017b21e99f8feb2fe Mon Sep 17 00:00:00 2001 From: Hank Duan Date: Tue, 17 Jun 2014 14:51:08 -0700 Subject: [PATCH 031/612] chore(release): version bump and changelog for 0.24.2 --- CHANGELOG.md | 14 ++++++++++++++ docs/api.md | 40 ++++++++++++++++++++-------------------- package.json | 2 +- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4caf86cd..3d38d79d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# 0.24.2 +_Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._ + +## Bug Fixes + +- ([a43f983](https://github.com/angular/protractor/commit/a43f98391d36cead7378d1dd26f54248f39300b7)) + fix(protractor): make ElementFinder.then resolve to itself instead of null + +- ([31d42a3](https://github.com/angular/protractor/commit/31d42a3875c5b95893d8a20d00dc5365c289ff98)) + fix(protractor): throw index-out-of-bounds + + See https://github.com/angular/protractor/issues/915 + - to make error more specific instead of propagate later + # 0.24.1 _Note: Major version 0 releases are for initial development, and backwards incompatible changes may be introduced at any time._ diff --git a/docs/api.md b/docs/api.md index c49874594..b35a46947 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,4 +1,4 @@ -Protractor API 0.24.1 +Protractor API 0.24.2 ============== @@ -743,7 +743,7 @@ Type | Description [webdriver.WebElement](#webdriverwebelement) | -##[ElementFinder.prototype.evaluate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L592) +##[ElementFinder.prototype.evaluate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L598) Evaluates the input as if it were on the scope of the current element. @@ -766,7 +766,7 @@ Type | Description [ElementFinder](#elementfinder) | which resolves to the evaluated expression. The result will be resolved as in {@link webdriver.WebDriver.executeScript}. In summary - primitives will be resolved as is, functions will be converted to string, and elements will be returned as a WebElement. -##[ElementFinder.prototype.allowAnimations](https://github.com/angular/protractor/blob/master/lib/protractor.js#L610) +##[ElementFinder.prototype.allowAnimations](https://github.com/angular/protractor/blob/master/lib/protractor.js#L616) Determine if animation is allowed on the current element. @@ -789,7 +789,7 @@ Type | Description [ElementFinder](#elementfinder) | which resolves to whether animation is allowed. -##[ElementFinder.prototype.then](https://github.com/angular/protractor/blob/master/lib/protractor.js#L624) +##[ElementFinder.prototype.then](https://github.com/angular/protractor/blob/master/lib/protractor.js#L630) Access the underlying actionResult of ElementFinder. Implementation allows ElementFinder to be used as a webdriver.promise.Promise @@ -813,7 +813,7 @@ Type | Description webdriver.promise.Promise | Promise which contains the results of evaluating fn. -##[Protractor](https://github.com/angular/protractor/blob/master/lib/protractor.js#L650) +##[Protractor](https://github.com/angular/protractor/blob/master/lib/protractor.js#L660) @@ -832,7 +832,7 @@ opt_rootElement | string= | Selector element that has an ng-app in scope. -##[Protractor.prototype.waitForAngular](https://github.com/angular/protractor/blob/master/lib/protractor.js#L750) +##[Protractor.prototype.waitForAngular](https://github.com/angular/protractor/blob/master/lib/protractor.js#L760) Instruct webdriver to wait until Angular has finished rendering and has no outstanding $http calls before continuing. @@ -849,7 +849,7 @@ Type | Description !webdriver.promise.Promise | A promise that will resolve to the scripts return value. -##[Protractor.prototype.findElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L789) +##[Protractor.prototype.findElement](https://github.com/angular/protractor/blob/master/lib/protractor.js#L799) Waits for Angular to finish rendering before searching for elements. @@ -865,7 +865,7 @@ Type | Description ![webdriver.WebElement](#webdriverwebelement) | -##[Protractor.prototype.findElements](https://github.com/angular/protractor/blob/master/lib/protractor.js#L798) +##[Protractor.prototype.findElements](https://github.com/angular/protractor/blob/master/lib/protractor.js#L808) Waits for Angular to finish rendering before searching for elements. @@ -881,7 +881,7 @@ Type | Description !webdriver.promise.Promise | A promise that will be resolved to an array of the located [webdriver.WebElement](#webdriverwebelement)s. -##[Protractor.prototype.isElementPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L808) +##[Protractor.prototype.isElementPresent](https://github.com/angular/protractor/blob/master/lib/protractor.js#L818) Tests if an element is present on the page. @@ -897,7 +897,7 @@ Type | Description !webdriver.promise.Promise | A promise that will resolve to whether the element is present on the page. -##[Protractor.prototype.addMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L820) +##[Protractor.prototype.addMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L830) Add a module to load before Angular whenever Protractor.get is called. Modules will be registered after existing modules already on the page, @@ -919,7 +919,7 @@ varArgs | ...* | Any additional arguments will be provided to the script and may -##[Protractor.prototype.clearMockModules](https://github.com/angular/protractor/blob/master/lib/protractor.js#L838) +##[Protractor.prototype.clearMockModules](https://github.com/angular/protractor/blob/master/lib/protractor.js#L848) Clear the list of registered mock modules. @@ -929,7 +929,7 @@ Clear the list of registered mock modules. -##[Protractor.prototype.removeMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L847) +##[Protractor.prototype.removeMockModule](https://github.com/angular/protractor/blob/master/lib/protractor.js#L857) Remove a registered mock module. @@ -946,7 +946,7 @@ name | !string | The name of the module to remove. -##[Protractor.prototype.get](https://github.com/angular/protractor/blob/master/lib/protractor.js#L858) +##[Protractor.prototype.get](https://github.com/angular/protractor/blob/master/lib/protractor.js#L868) See webdriver.WebDriver.get @@ -969,7 +969,7 @@ opt_timeout | number= | Number of seconds to wait for Angular to start. -##[Protractor.prototype.refresh](https://github.com/angular/protractor/blob/master/lib/protractor.js#L938) +##[Protractor.prototype.refresh](https://github.com/angular/protractor/blob/master/lib/protractor.js#L948) See webdriver.WebDriver.refresh @@ -991,7 +991,7 @@ opt_timeout | number= | Number of seconds to wait for Angular to start. -##[Protractor.prototype.navigate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L961) +##[Protractor.prototype.navigate](https://github.com/angular/protractor/blob/master/lib/protractor.js#L971) Mixin navigation methods back into the navigation object so that they are invoked as before, i.e. driver.navigate().refresh() @@ -1002,7 +1002,7 @@ they are invoked as before, i.e. driver.navigate().refresh() -##[Protractor.prototype.setLocation](https://github.com/angular/protractor/blob/master/lib/protractor.js#L971) +##[Protractor.prototype.setLocation](https://github.com/angular/protractor/blob/master/lib/protractor.js#L981) Browse to another page using in-page navigation. @@ -1025,7 +1025,7 @@ Type | Description !webdriver.promise.Promise | A promise that will resolve once page has been changed. -##[Protractor.prototype.getLocationAbsUrl](https://github.com/angular/protractor/blob/master/lib/protractor.js#L989) +##[Protractor.prototype.getLocationAbsUrl](https://github.com/angular/protractor/blob/master/lib/protractor.js#L999) Returns the current absolute url from AngularJS. @@ -1035,7 +1035,7 @@ Returns the current absolute url from AngularJS. -##[Protractor.prototype.debugger](https://github.com/angular/protractor/blob/master/lib/protractor.js#L997) +##[Protractor.prototype.debugger](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1007) Pauses the test and injects some helper functions into the browser, so that debugging may be done in the browser console. @@ -1060,7 +1060,7 @@ debugger. -##[Protractor.prototype.pause](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1022) +##[Protractor.prototype.pause](https://github.com/angular/protractor/blob/master/lib/protractor.js#L1032) Beta (unstable) pause function for debugging webdriver tests. Use browser.pause() in your test to enter the protractor debugger from that @@ -1685,7 +1685,7 @@ driver.executeAsyncScript( 'var callback = arguments[arguments.length - 1];' + 'mailClient.getComposeWindowWidget().onload(callback);'); driver.switchTo().frame('composeWidget'); -driver.findElement(By.id('to')).sendKeys('dog@example.com'); +driver.findElement(By.id('to')).sendKEys('dog@example.com'); Example #3: Injecting a XMLHttpRequest and waiting for the result. In this diff --git a/package.json b/package.json index 5871f51bd..46e1665cb 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "start": "testapp/scripts/web-server.js" }, "license": "MIT", - "version": "0.24.1", + "version": "0.24.2", "webdriverVersions": { "selenium": "2.42.0", "chromedriver": "2.10", From 4e1cfe5ad0f22947d21b4ebecd7cd05e0319af1a Mon Sep 17 00:00:00 2001 From: Hank Duan Date: Tue, 17 Jun 2014 14:22:23 -0700 Subject: [PATCH 032/612] feature(launcher): aggregate failures at the end --- lib/launcher.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/launcher.js b/lib/launcher.js index 58089a988..857c704be 100644 --- a/lib/launcher.js +++ b/lib/launcher.js @@ -191,6 +191,7 @@ var reporter = { }, reportSummary: function() { + var totalFailures = 0; this.taskReporters_.forEach(function(taskReporter) { var capability = taskReporter.task.capability; var shortName = (capability.browserName) ? capability.browserName : ''; @@ -198,10 +199,14 @@ var reporter = { shortName += (' #' + taskReporter.task.taskId); if (taskReporter.failedCount) { log_(shortName + ' failed ' + taskReporter.failedCount + ' test(s)'); + totalFailures += taskReporter.failedCount; } else { log_(shortName + ' passed'); } }); + if (this.taskReporters_.length > 1 && totalFailures) { + log_('overall: ' + totalFailures + ' failure(s)'); + } } }; From f9082d0460c7b6465d53c37f326a0f66412c21ce Mon Sep 17 00:00:00 2001 From: Hank Duan Date: Tue, 17 Jun 2014 12:31:00 -0700 Subject: [PATCH 033/612] fix(clientsidescripts): make exactBinding more exact See https://github.com/angular/protractor/issues/925 --- lib/clientsidescripts.js | 2 +- lib/locators.js | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/clientsidescripts.js b/lib/clientsidescripts.js index bf328048b..8cd771c19 100644 --- a/lib/clientsidescripts.js +++ b/lib/clientsidescripts.js @@ -53,7 +53,7 @@ functions.findBindings = function(binding, exactMatch, using) { if(dataBinding) { var bindingName = dataBinding.exp || dataBinding[0].exp || dataBinding; if (exactMatch) { - var matcher = new RegExp('([^a-zA-Z\\d]|$)' + binding + '([^a-zA-Z\\d]|^)'); + var matcher = new RegExp('({|\\s|$|\\|)' + binding + '(}|\\s|^|\\|)'); if (matcher.test(bindingName)) { matches.push(bindings[i]); } diff --git a/lib/locators.js b/lib/locators.js index 6553e4515..4c3c7e442 100644 --- a/lib/locators.js +++ b/lib/locators.js @@ -103,8 +103,19 @@ ProtractorBy.prototype.binding = function(bindingDescriptor) { * Find an element by exact binding. * * @alias by.exactBinding() - * Same as by.binding() except this does not allow for partial matches + * @view + * {{ person.name }} + * + * {{person_phone|uppercase}} * + * @example + * expect(element(by.exactBinding('person.name')).isPresent()).toBe(true); + * expect(element(by.exactBinding('person-email')).isPresent()).toBe(true); + * expect(element(by.exactBinding('person')).isPresent()).toBe(false); + * expect(element(by.exactBinding('person_phone')).isPresent()).toBe(true); + * expect(element(by.exactBinding('person_phone|uppercase')).isPresent()).toBe(true); + * expect(element(by.exactBinding('phone')).isPresent()).toBe(false); + * * @param {string} bindingDescriptor * @return {{findElementsOverride: findElementsOverride, message: string}} */ From ff3d5ebc071a8806259f5da20018f2d937409455 Mon Sep 17 00:00:00 2001 From: Leo Gallucci Date: Mon, 16 Jun 2014 01:05:28 -0300 Subject: [PATCH 034/612] feat(locators): add toString() wrapper for this.message --- lib/locators.js | 63 +++++++++++++++++++++++++++++++++-------------- lib/protractor.js | 2 +- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/lib/locators.js b/lib/locators.js index 4c3c7e442..a0aea3f5b 100644 --- a/lib/locators.js +++ b/lib/locators.js @@ -65,7 +65,10 @@ ProtractorBy.prototype.addLocator = function(name, script) { return driver.findElements( webdriver.By.js.apply(webdriver.By, findElementArguments)); }, - message: 'by.' + name + '("' + Array.prototype.join.call(locatorArguments, '", "') + '")' + toString: function toString() { + return 'by.' + name + '("' + Array.prototype. + join.call(locatorArguments, '", "') + '")'; + } }; }; }; @@ -86,7 +89,7 @@ ProtractorBy.prototype.addLocator = function(name, script) { * expect(span2.getText()).toBe('foo@bar.com'); * * @param {string} bindingDescriptor - * @return {{findElementsOverride: findElementsOverride, message: string}} + * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} */ ProtractorBy.prototype.binding = function(bindingDescriptor) { return { @@ -95,7 +98,9 @@ ProtractorBy.prototype.binding = function(bindingDescriptor) { webdriver.By.js(clientSideScripts.findBindings, bindingDescriptor, false, using)); }, - message: 'by.binding("' + bindingDescriptor + '")' + toString: function toString() { + return 'by.binding("' + bindingDescriptor + '")'; + } }; }; @@ -117,7 +122,7 @@ ProtractorBy.prototype.binding = function(bindingDescriptor) { * expect(element(by.exactBinding('phone')).isPresent()).toBe(false); * * @param {string} bindingDescriptor - * @return {{findElementsOverride: findElementsOverride, message: string}} + * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} */ ProtractorBy.prototype.exactBinding = function(bindingDescriptor) { return { @@ -126,7 +131,9 @@ ProtractorBy.prototype.exactBinding = function(bindingDescriptor) { webdriver.By.js(clientSideScripts.findBindings, bindingDescriptor, true, using)); }, - message: 'by.exactBinding("' + bindingDescriptor + '")' + toString: function toString() { + return 'by.exactBinding("' + bindingDescriptor + '")'; + } }; }; @@ -150,7 +157,9 @@ ProtractorBy.prototype.model = function(model) { return driver.findElements( webdriver.By.js(clientSideScripts.findByModel, model, using)); }, - message: 'by.model("' + model + '")' + toString: function toString() { + return 'by.model("' + model + '")'; + } }; }; @@ -164,7 +173,7 @@ ProtractorBy.prototype.model = function(model) { * element(by.buttonText('Save')); * * @param {string} searchText - * @return {{findElementsOverride: findElementsOverride, message: string}} + * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} */ ProtractorBy.prototype.buttonText = function(searchText) { return { @@ -173,7 +182,9 @@ ProtractorBy.prototype.buttonText = function(searchText) { webdriver.By.js(clientSideScripts.findByButtonText, searchText, using)); }, - message: 'by.buttonText("' + searchText + '")' + toString: function toString() { + return 'by.buttonText("' + searchText + '")'; + } }; }; @@ -187,7 +198,7 @@ ProtractorBy.prototype.buttonText = function(searchText) { * element(by.partialButtonText('Save')); * * @param {string} searchText - * @return {{findElementsOverride: findElementsOverride, message: string}} + * @return {{findElementsOverride: findElementsOverride, toString: Function|string}} */ ProtractorBy.prototype.partialButtonText = function(searchText) { return { @@ -196,7 +207,9 @@ ProtractorBy.prototype.partialButtonText = function(searchText) { webdriver.By.js(clientSideScripts.findByPartialButtonText, searchText, using)); }, - message: 'by.partialButtonText("' + searchText + '")' + toString: function toString() { + return 'by.partialButtonText("' + searchText + '")'; + } }; }; @@ -259,7 +272,9 @@ ProtractorBy.prototype.repeater = function(repeatDescriptor) { webdriver.By.js(clientSideScripts.findAllRepeaterRows, repeatDescriptor, using)); }, - message: 'by.repeater("' + repeatDescriptor + '")', + toString: function toString() { + return 'by.repeater("' + repeatDescriptor + '")'; + }, row: function(index) { return { findElementsOverride: function(driver, using) { @@ -267,7 +282,9 @@ ProtractorBy.prototype.repeater = function(repeatDescriptor) { webdriver.By.js(clientSideScripts.findRepeaterRows, repeatDescriptor, index, using)); }, - message: 'by.repeater(' + repeatDescriptor + '").row("' + index + '")"', + toString: function toString() { + return 'by.repeater(' + repeatDescriptor + '").row("' + index + '")"'; + }, column: function(binding) { return { findElementsOverride: function(driver, using) { @@ -275,8 +292,10 @@ ProtractorBy.prototype.repeater = function(repeatDescriptor) { webdriver.By.js(clientSideScripts.findRepeaterElement, repeatDescriptor, index, binding, using)); }, - message: 'by.repeater("' + repeatDescriptor + '").row("' + index + - '").column("' + binding + '")' + toString: function toString() { + return 'by.repeater("' + repeatDescriptor + '").row("' + index + + '").column("' + binding + '")'; + } }; } }; @@ -288,8 +307,10 @@ ProtractorBy.prototype.repeater = function(repeatDescriptor) { webdriver.By.js(clientSideScripts.findRepeaterColumn, repeatDescriptor, binding, using)); }, - message: 'by.repeater("' + repeatDescriptor + '").column("' + binding + - '")', + toString: function toString() { + return 'by.repeater("' + repeatDescriptor + '").column("' + + binding + '")'; + }, row: function(index) { return { findElementsOverride: function(driver, using) { @@ -297,8 +318,10 @@ ProtractorBy.prototype.repeater = function(repeatDescriptor) { webdriver.By.js(clientSideScripts.findRepeaterElement, repeatDescriptor, index, binding, using)); }, - message: 'by.repeater("' + repeatDescriptor + '").column("' + - binding + '").row("' + index + '")' + toString: function toString() { + return 'by.repeater("' + repeatDescriptor + '").column("' + + binding + '").row("' + index + '")'; + } }; } }; @@ -326,7 +349,9 @@ ProtractorBy.prototype.cssContainingText = function(cssSelector, searchText) { webdriver.By.js(clientSideScripts.findByCssContainingText, cssSelector, searchText, using)); }, - message: 'by.cssContainingText("' + cssSelector + '", "' + searchText + '")' + toString: function toString() { + return 'by.cssContainingText("' + cssSelector + '", "' + searchText + '")'; + } }; }; diff --git a/lib/protractor.js b/lib/protractor.js index bc24738b4..f6f745563 100644 --- a/lib/protractor.js +++ b/lib/protractor.js @@ -568,7 +568,7 @@ var buildElementHelper = function(ptor) { this.locator_, this.parentElementFinder_).getWebElements(); var id = webElementsPromise.then(function(arr) { - var locatorMessage = (self.locator_.message || self.locator_.toString()); + var locatorMessage = self.locator_.toString(); if (!arr.length) { throw new Error('No element found using locator: ' + locatorMessage); } From b7994ca03c8346e196ead9b969d3bdf9164cf57f Mon Sep 17 00:00:00 2001 From: Julie Date: Tue, 17 Jun 2014 17:04:03 -0700 Subject: [PATCH 035/612] docs(contributing): consolidate contributing docs Now, all in CONTRIBUTING.md --- CONTRIBUTING.md | 30 +++++++++++++++++++++++++++--- docs/contributing.md | 29 ----------------------------- 2 files changed, 27 insertions(+), 32 deletions(-) delete mode 100644 docs/contributing.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7560a028b..67e79c930 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,31 @@ your application. If there's an error, please include the error text. Please format code and markup in your issue using [github markdown](https://help.github.com/articles/github-flavored-markdown). -Pull Requests -------------- +Contributing to Source Code (Pull Requests) +=========================================== -Follow [these guidelines](https://github.com/angular/protractor/blob/master/docs/contributing.md). +Loosely, follow the [Angular contribution rules](http://docs.angularjs.org/misc/contribute). + +Protractor specific rules +------------------------- + + * JavaScript style should generally follow the [Google JS style guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml). + * Wrap code at 80 chars. + * Document public methods with jsdoc. + * Be consistent with the code around you! + +Commit Messages +--------------- + +Protractor follows AngularJS's [commit message format](https://docs.google.com/a/google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#heading=h.z8a3t6ehl060). + +In summary, this style is + + (): + + + +