protractor
Advanced tools
Comparing version 0.9.0 to 0.10.0
@@ -0,1 +1,75 @@ | ||
# 0.10.0 | ||
_Note: Major version 0 releases are for initial development, and backwards compatible changes may be introduced at any time._ | ||
## Features | ||
- ([881759e](https://github.com/angular/protractor/commit/881759e77462dc8e1001eb77008668ae6dc552cd)) feat(timeouts): add a unique error message when waitForAngular times out | ||
To improve the readability of error messages, when waitForAngular times out | ||
it now produces a custom message. This should help clarify confusion | ||
for pages that continually poll using $interval. This change also adds more | ||
documentation on timeouts. See issue #109. | ||
- ([37e0f1a](https://github.com/angular/protractor/commit/37e0f1af196c3c0bf54dcddf0088a8c16602e5f2)) feat(install selenium): better communication in the install script | ||
Adds better messages in the selenium server install script, and also | ||
makes the script output a 'start' executable which can be used to quickly | ||
start up the selenium standalone. *not yet windows friendly*. Closes #108. | ||
- ([b32f5a5](https://github.com/angular/protractor/commit/b32f5a59169f1324271bd5abc09c17fcd9c4f249)) feat(config): add exmples for dealing with log-in | ||
Adds examples for how to log in when the login page is not written | ||
in Angular. New examples are in spec/login. | ||
- ([1b7675a](https://github.com/angular/protractor/commit/1b7675aab7846bee54117876887bfec07ce31745)) feat(cli): add an onPrepare callback to the config | ||
This onPrepare callback is useful when you want to do something with | ||
protractor before running the specs. For example, you might want | ||
to monkey-patch protractor with custom functions used by all the | ||
specs, or add the protractor instance to the globals. | ||
An example usage is shown in the spec/onPrepareConf.js file and its | ||
associated spec. | ||
## Bug fixes | ||
- ([256b21c](https://github.com/angular/protractor/commit/256b21cf8c744a200892e6b7f9172150b2f4fe8d)) fix(cli): allow passing the config file before the options | ||
The cli usage says: | ||
> USAGE: protractor configFile [options] | ||
However, the options passed as argument are merged into the default | ||
configuration as soon as the configFile is met in the args parsing | ||
loop. | ||
This fix merges the options in the default configuration only after | ||
the loop, allowing to pass the options to the cli before or after, | ||
or around the config file. | ||
- ([6223825](https://github.com/angular/protractor/commit/62238252c7fc68c6a5941883f6a272e95997a8ff)) fix(jasminewd): allow use of custom matchers | ||
Using jasmine.Matchers.prototype to generate the chained methods for | ||
expect() calls is flawed because it does not pick up custom matchers | ||
defined using addMatcher. Instead, use either the matchersClass for | ||
the current spec or from the environment. | ||
- ([c22fc38](https://github.com/angular/protractor/commit/c22fc387bc0ef7a07371e023d39a6ce58dfa56c9)) fix(sync): getCurrentUrl and friends should sync with Angular first | ||
getCurrentUrl, getPageSource, and getTitle should sync with Angular | ||
before executing. Closes #92. | ||
- ([dd06756](https://github.com/angular/protractor/commit/dd067561cf9fe0a765e98605b9ebdd8fbfef04d3)) fix(clientsidescripts): findElements and isElementPresent for protractor.By.select | ||
- ([c607459](https://github.com/angular/protractor/commit/c60745945c6514e25403783eab3de5873e15758b)) fix (navigation): The defer label should appear before other window names, | ||
not after. | ||
- ([806f381](https://github.com/angular/protractor/commit/806f38113c675a26171776a559a20bf3899aa2cc)) Fix: findElements() and isElementPresent() now work for protractor.By.input. | ||
Closes #79. | ||
## Breaking changes | ||
- ([881759e](https://github.com/angular/protractor/commit/881759e77462dc8e1001eb77008668ae6dc552cd)) feat(timeouts): add a unique error message when waitForAngular times out | ||
This changes the default script timeout from 100 seconds down to 11. Tests | ||
which relied on extremely long timeouts will need to change the default script | ||
timeout with `driver.manage().timeouts().setScriptTimeout(<bigNumber>)`. | ||
# 0.9.0 | ||
@@ -2,0 +76,0 @@ |
@@ -107,5 +107,11 @@ Debugging Protractor Tests | ||
Webdriver has a timeout for script execution, which can be set with | ||
`driver.manage().timeouts().setScriptTimeout`. Protractor sets this to 100 | ||
seconds by default, so usually Jasmine will time out first. | ||
`driver.manage().timeouts().setScriptTimeout`. Protractor sets this to 11 | ||
seconds by default. | ||
Protractor attempts to synchronize with your page before performing actions. | ||
This means waiting for all $timeout or $http requests to resolve, as well as | ||
letting the current $digest cycle finish. If your page has not synchronized | ||
within the script execution timeout, Protractor will fail with the message | ||
'Timed out waiting for Protractor to synchronize with the page'. | ||
If your website uses $timeout or $http to continuously poll, Protractor will | ||
@@ -112,0 +118,0 @@ interpret that as your site being busy and will time out on all requests. See |
@@ -11,1 +11,14 @@ FAQ | ||
never be registered as completely loaded. Discussion of this is in [issue 59](https://github.com/angular/protractor/issues/49). | ||
_How do I deal with my log-in page?_ | ||
If your app needs log-in, there are a couple ways to deal with it. If your login | ||
page is not written with Angular, you'll need to interact with it via | ||
unwrapped webdriver, which can be accessed like `ptor.driver.get()`. | ||
You can put your log-in code into an `onPrepare` function, which will be run | ||
once before any of your tests. See [this example](https://github.com/angular/protractor/blob/master/spec/login/viaConfigConf.js). | ||
If you would like to do your login in your test suite itself, see | ||
[this example](https://github.com/angular/protractor/blob/master/spec/login/viaTestSpec.js). |
@@ -10,9 +10,13 @@ Getting Started | ||
When writing tests, it's important to remember that Protractor is a wrapper | ||
around [WebDriverJS](https://code.google.com/p/selenium/wiki/WebDriverJs). When | ||
writing tests, keep the following in mind: | ||
around [WebDriverJS](https://code.google.com/p/selenium/wiki/WebDriverJs). | ||
Selenium-Webdriver is a is a browser automation framework. Tests are written | ||
with the WebDriver API, which communicates with a Selenium server to control | ||
the browser under test. | ||
When writing tests, keep the following in mind: | ||
- The test code and scripts running in the browser are separated, and only | ||
communicate through the [WebDriver wire protocol](https://code.google.com/p/selenium/wiki/JsonWireProtocol). | ||
- WebDriver commands are scheduled on a control flow and return promises, not | ||
primitive values. See the [control flow doc](docs/control-flow.md) for more | ||
primitive values. See the [control flow doc](/control-flow.md) for more | ||
info. | ||
@@ -40,3 +44,3 @@ - To run tests, WebDriverJS needs to talk to a selenium standalone server | ||
java -jar selenium/selenium-server-standalone-2.35.0.jar -Dwebdriver.chrome.driver=./selenium/chromedriver | ||
./selenium/start | ||
@@ -104,2 +108,3 @@ Protractor is now available as a command line program which takes one argument, | ||
}); | ||
}); | ||
``` | ||
@@ -106,0 +111,0 @@ |
@@ -93,3 +93,6 @@ /** | ||
var promises = {}; | ||
for (matcher in jasmine.Matchers.prototype) { | ||
var env = jasmine.getEnv(); | ||
var matchersClass = env.currentSpec.matchersClass || env.matchersClass; | ||
for (matcher in matchersClass.prototype) { | ||
promises[matcher] = wrapMatcher(matcher, actualPromise); | ||
@@ -96,0 +99,0 @@ }; |
@@ -37,2 +37,7 @@ require('../index.js'); | ||
}); | ||
}, | ||
getBigNumber: function() { | ||
return flow.execute(function() { | ||
return webdriver.promise.fulfilled(1111); | ||
}); | ||
} | ||
@@ -100,4 +105,10 @@ }; | ||
expect(fakeDriver.getValueB()).toEqual('b'); | ||
}) | ||
}); | ||
it('should allow the use of custom matchers', function() { | ||
expect(500).toBeLotsMoreThan(3); | ||
expect(fakeDriver.getBigNumber()).toBeLotsMoreThan(33); | ||
}); | ||
// Uncomment to see timeout failures. | ||
@@ -104,0 +115,0 @@ // it('should timeout after 200ms', function() { |
@@ -12,3 +12,3 @@ var util = require('util'); | ||
var args = process.argv.slice(2); | ||
var configDir; | ||
var configDir, configPath; | ||
@@ -149,4 +149,5 @@ var merge = function(into, from) { | ||
driver.manage().timeouts().setScriptTimeout(100000); | ||
driver.getSession().then(function(session) { | ||
driver.manage().timeouts().setScriptTimeout(11000); | ||
id = session.getId(); | ||
@@ -159,3 +160,3 @@ | ||
// Set up the Jasmine WebDriver Adapter | ||
// Set up the Jasmine WebDriver Adapter. | ||
require('../jasminewd'); | ||
@@ -167,3 +168,17 @@ | ||
minijn.executeSpecs(options); | ||
// Let the configuration configure the protractor instance before running | ||
// the tests. | ||
webdriver.promise.controlFlow().execute(function() { | ||
if (config.onPrepare) { | ||
if (typeof config.onPrepare == 'function') { | ||
config.onPrepare(); | ||
} else if (typeof config.onPrepare == 'string') { | ||
require(path.resolve(process.cwd(), config.onPrepare)); | ||
} else { | ||
throw 'config.onPrepare must be a string or function'; | ||
} | ||
} | ||
}).then(function() { | ||
minijn.executeSpecs(options); | ||
}); | ||
}); | ||
@@ -237,5 +252,3 @@ } | ||
default: | ||
var configPath = path.resolve(process.cwd(), arg); | ||
merge(config, require(configPath).config); | ||
merge (config, commandLineConfig); | ||
configPath = path.resolve(process.cwd(), arg); | ||
configDir = path.dirname(configPath); | ||
@@ -246,2 +259,5 @@ break; | ||
merge(config, require(configPath).config); | ||
merge(config, commandLineConfig); | ||
var sauceAccount; | ||
@@ -248,0 +264,0 @@ if (config.sauceUser && config.sauceKey) { |
@@ -5,2 +5,5 @@ var url = require('url'); | ||
var clientSideScripts = require('./clientsidescripts.js'); | ||
var ProtractorBy = require('./locators.js').ProtractorBy; | ||
var DEFER_LABEL = 'NG_DEFER_BOOTSTRAP!'; | ||
@@ -16,283 +19,2 @@ | ||
/** | ||
* All scripts to be run on the client via executeAsyncScript or | ||
* executeScript should be put here. These scripts are transmitted over | ||
* the wire using their toString representation, and cannot reference | ||
* external variables. They can, however use the array passed in to | ||
* arguments. Instead of params, all functions on clientSideScripts | ||
* should list the arguments array they expect. | ||
*/ | ||
var clientSideScripts = {}; | ||
/** | ||
* Wait until Angular has finished rendering and has | ||
* no outstanding $http calls before continuing. | ||
* | ||
* arguments[0] {string} The selector housing an ng-app | ||
* arguments[1] {function} callback | ||
*/ | ||
clientSideScripts.waitForAngular = function() { | ||
var el = document.querySelector(arguments[0]); | ||
var callback = arguments[1]; | ||
angular.element(el).injector().get('$browser'). | ||
notifyWhenNoOutstandingRequests(callback); | ||
}; | ||
/** | ||
* Find an element in the page by their angular binding. | ||
* | ||
* arguments[0] {Element} The scope of the search. | ||
* arguments[1] {string} The binding, e.g. {{cat.name}}. | ||
* | ||
* @return {WebElement} The element containing the binding. | ||
*/ | ||
clientSideScripts.findBinding = function() { | ||
var using = arguments[0] || document; | ||
var binding = arguments[1]; | ||
var bindings = using.getElementsByClassName('ng-binding'); | ||
var matches = []; | ||
for (var i = 0; i < bindings.length; ++i) { | ||
var bindingName = angular.element(bindings[i]).data().$binding[0].exp || | ||
angular.element(bindings[i]).data().$binding; | ||
if (bindingName.indexOf(binding) != -1) { | ||
matches.push(bindings[i]); | ||
} | ||
} | ||
return matches[0]; // We can only return one with webdriver.findElement. | ||
}; | ||
/** | ||
* Find a list of elements in the page by their angular binding. | ||
* | ||
* arguments[0] {Element} The scope of the search. | ||
* arguments[1] {string} The binding, e.g. {{cat.name}}. | ||
* | ||
* @return {Array.<WebElement>} The elements containing the binding. | ||
*/ | ||
clientSideScripts.findBindings = function() { | ||
var using = arguments[0] || document; | ||
var binding = arguments[1]; | ||
var bindings = using.getElementsByClassName('ng-binding'); | ||
var matches = []; | ||
for (var i = 0; i < bindings.length; ++i) { | ||
var bindingName = angular.element(bindings[i]).data().$binding[0].exp || | ||
angular.element(bindings[i]).data().$binding; | ||
if (bindingName.indexOf(binding) != -1) { | ||
matches.push(bindings[i]); | ||
} | ||
} | ||
return matches; // Return the whole array for webdriver.findElements. | ||
}; | ||
/** | ||
* Find a row within an ng-repeat. | ||
* | ||
* arguments[0] {Element} The scope of the search. | ||
* arguments[1] {string} The text of the repeater, e.g. 'cat in cats'. | ||
* arguments[2] {number} The row index. | ||
* | ||
* @return {Element} The row element. | ||
*/ | ||
clientSideScripts.findRepeaterRow = function() { | ||
var using = arguments[0] || document; | ||
var repeater = arguments[1]; | ||
var index = arguments[2]; | ||
var rows = []; | ||
var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:']; | ||
for (var p = 0; p < prefixes.length; ++p) { | ||
var attr = prefixes[p] + 'repeat'; | ||
var repeatElems = using.querySelectorAll('[' + attr + ']'); | ||
attr = attr.replace(/\\/g, ''); | ||
for (var i = 0; i < repeatElems.length; ++i) { | ||
if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) { | ||
rows.push(repeatElems[i]); | ||
} | ||
} | ||
} | ||
return rows[index - 1]; | ||
}; | ||
/** | ||
* Find an element within an ng-repeat by its row and column. | ||
* | ||
* arguments[0] {Element} The scope of the search. | ||
* arguments[1] {string} The text of the repeater, e.g. 'cat in cats'. | ||
* arguments[2] {number} The row index. | ||
* arguments[3] {string} The column binding, e.g. '{{cat.name}}'. | ||
* | ||
* @return {Element} The element. | ||
*/ | ||
clientSideScripts.findRepeaterElement = function() { | ||
var matches = []; | ||
var using = arguments[0] || document; | ||
var repeater = arguments[1]; | ||
var index = arguments[2]; | ||
var binding = arguments[3]; | ||
var rows = []; | ||
var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:']; | ||
for (var p = 0; p < prefixes.length; ++p) { | ||
var attr = prefixes[p] + 'repeat'; | ||
var repeatElems = using.querySelectorAll('[' + attr + ']'); | ||
attr = attr.replace(/\\/g, ''); | ||
for (var i = 0; i < repeatElems.length; ++i) { | ||
if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) { | ||
rows.push(repeatElems[i]); | ||
} | ||
} | ||
} | ||
var row = rows[index - 1]; | ||
var bindings = []; | ||
if (row.className.indexOf('ng-binding') != -1) { | ||
bindings.push(row); | ||
} | ||
var childBindings = row.getElementsByClassName('ng-binding'); | ||
for (var i = 0; i < childBindings.length; ++i) { | ||
bindings.push(childBindings[i]); | ||
} | ||
for (var i = 0; i < bindings.length; ++i) { | ||
var bindingName = angular.element(bindings[i]).data().$binding[0].exp || | ||
angular.element(bindings[i]).data().$binding; | ||
if (bindingName.indexOf(binding) != -1) { | ||
matches.push(bindings[i]); | ||
} | ||
} | ||
// We can only return one with webdriver.findElement. | ||
return matches[0]; | ||
}; | ||
/** | ||
* Find the elements in a column of an ng-repeat. | ||
* | ||
* arguments[0] {Element} The scope of the search. | ||
* arguments[1] {string} The text of the repeater, e.g. 'cat in cats'. | ||
* arguments[2] {string} The column binding, e.g. '{{cat.name}}'. | ||
* | ||
* @return {Array.<Element>} The elements in the column. | ||
*/ | ||
clientSideScripts.findRepeaterColumn = function() { | ||
var matches = []; | ||
var using = arguments[0] || document; | ||
var repeater = arguments[1]; | ||
var binding = arguments[2]; | ||
var rows = []; | ||
var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:']; | ||
for (var p = 0; p < prefixes.length; ++p) { | ||
var attr = prefixes[p] + 'repeat'; | ||
var repeatElems = using.querySelectorAll('[' + attr + ']'); | ||
attr = attr.replace(/\\/g, ''); | ||
for (var i = 0; i < repeatElems.length; ++i) { | ||
if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) { | ||
rows.push(repeatElems[i]); | ||
} | ||
} | ||
} | ||
for (var i = 0; i < rows.length; ++i) { | ||
var bindings = []; | ||
if (rows[i].className.indexOf('ng-binding') != -1) { | ||
bindings.push(rows[i]); | ||
} | ||
var childBindings = rows[i].getElementsByClassName('ng-binding'); | ||
for (var k = 0; k < childBindings.length; ++k) { | ||
bindings.push(childBindings[k]); | ||
} | ||
for (var j = 0; j < bindings.length; ++j) { | ||
var bindingName = angular.element(bindings[j]).data().$binding[0].exp || | ||
angular.element(bindings[j]).data().$binding; | ||
if (bindingName.indexOf(binding) != -1) { | ||
matches.push(bindings[j]); | ||
} | ||
} | ||
} | ||
return matches; | ||
}; | ||
/** | ||
* Find an input element by model name. | ||
* | ||
* arguments[0] {Element} The scope of the search. | ||
* arguments[1] {string} The model name. | ||
* | ||
* @return {Element} The first matching input element. | ||
*/ | ||
clientSideScripts.findInput = function() { | ||
var using = arguments[0] || document; | ||
var model = arguments[1]; | ||
var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:']; | ||
for (var p = 0; p < prefixes.length; ++p) { | ||
var selector = 'input[' + prefixes[p] + 'model="' + model + '"]'; | ||
var inputs = using.querySelectorAll(selector); | ||
if (inputs.length) { | ||
return inputs[0]; | ||
} | ||
} | ||
}; | ||
/** | ||
* Find an select element by model name. | ||
* | ||
* arguments[0] {Element} The scope of the search. | ||
* arguments[1] {string} The model name. | ||
* | ||
* @return {Element} The first matching select element. | ||
*/ | ||
clientSideScripts.findSelect = function() { | ||
var using = arguments[0] || document; | ||
var model = arguments[1]; | ||
var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:']; | ||
for (var p = 0; p < prefixes.length; ++p) { | ||
var selector = 'select[' + prefixes[p] + 'model="' + model + '"]'; | ||
var inputs = using.querySelectorAll(selector); | ||
if (inputs.length) { | ||
return inputs[0]; | ||
} | ||
} | ||
}; | ||
/** | ||
* Find an selected option element by model name. | ||
* | ||
* arguments[0] {Element} The scope of the search. | ||
* arguments[1] {string} The model name. | ||
* | ||
* @return {Element} The first matching input element. | ||
*/ | ||
clientSideScripts.findSelectedOption = function() { | ||
var using = arguments[0] || document; | ||
var model = arguments[1]; | ||
var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:']; | ||
for (var p = 0; p < prefixes.length; ++p) { | ||
var selector = | ||
'select[' + prefixes[p] + 'model="' + model + '"] option:checked'; | ||
var inputs = using.querySelectorAll(selector); | ||
if (inputs.length) { | ||
return inputs[0]; | ||
} | ||
} | ||
}; | ||
/** | ||
* Tests whether the angular global variable is present on a page. Retries | ||
* in case the page is just loading slowly. | ||
* | ||
* arguments none. | ||
*/ | ||
clientSideScripts.testForAngular = function() { | ||
var attempts = arguments[0]; | ||
var callback = arguments[arguments.length - 1]; | ||
var check = function(n) { | ||
if (window.angular && window.angular.resumeBootstrap) { | ||
callback(true); | ||
} else if (n < 1) { | ||
callback(false); | ||
} else { | ||
window.setTimeout(function() {check(n - 1)}, 1000); | ||
} | ||
}; | ||
check(attempts); | ||
}; | ||
/** | ||
* Mix a function from one object onto another. The function will still be | ||
@@ -303,5 +25,9 @@ * called in the context of the original object. | ||
* @param {string} fnName | ||
* @param {function=} setupFn | ||
*/ | ||
var mixin = function(to, from, fnName) { | ||
var mixin = function(to, from, fnName, setupFn) { | ||
to[fnName] = function() { | ||
if (setupFn) { | ||
setupFn(); | ||
} | ||
return from[fnName].apply(from, arguments); | ||
@@ -311,14 +37,23 @@ } | ||
/** | ||
* @param {webdriver.WebDriver} webdriver | ||
* @param {string=} opt_baseUrl A base URL to run get requests against. | ||
* @param {string=body} opt_rootElement Selector el that has an ng-app in scope | ||
* @param {string=body} opt_rootElement Selector element that has an ng-app in | ||
* scope. | ||
* @constructor | ||
*/ | ||
var Protractor = function(webdriver, 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 foo in webdriver) { | ||
if(!this[foo] && typeof webdriver[foo] == 'function') { | ||
mixin(this, webdriver, foo); | ||
for (var method in webdriver) { | ||
if(!this[method] && typeof webdriver[method] == 'function') { | ||
if (methodsToSync.indexOf(method) !== -1) { | ||
mixin(this, webdriver, method, this.waitForAngular.bind(this)); | ||
} else { | ||
mixin(this, webdriver, method); | ||
} | ||
} | ||
@@ -344,3 +79,3 @@ } | ||
/** | ||
* The css selector for anmelement on which to find Angular. This is usually | ||
* The css selector for an element on which to find Angular. This is usually | ||
* 'body' but if your ng-app is on a subsection of the page it may be | ||
@@ -381,3 +116,10 @@ * a subelement. | ||
return this.driver.executeAsyncScript( | ||
clientSideScripts.waitForAngular, this.rootEl); | ||
clientSideScripts.waitForAngular, this.rootEl).then(null, function(err) { | ||
if (!/asynchronous script timeout/.test(err.message)) { | ||
throw err; | ||
} | ||
var timeout = /[\d\.]*\ seconds/.exec(err.message); | ||
throw 'Timed out waiting for Protractor to synchronize with ' + | ||
'the page after ' + timeout; | ||
}); | ||
}; | ||
@@ -460,15 +202,10 @@ | ||
* evaluated expression. The result will be resolved as in | ||
* webdriver.WebDriver.executeScript. In summary - primitives will be | ||
* resolved as is, functions will be converted to string, and elements | ||
* {@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) { | ||
// TODO: put into clientSideScripts | ||
thisPtor.waitForAngular(); | ||
return element.getDriver().executeScript(function() { | ||
var element = arguments[0]; | ||
var expression = arguments[1]; | ||
return angular.element(element).scope().$eval(expression) | ||
}, element, expression); | ||
return element.getDriver().executeScript(clientSideScripts.evaluate, | ||
element, expression); | ||
}; | ||
@@ -519,2 +256,3 @@ | ||
Protractor.prototype.isElementPresent = function(locatorOrElement, varArgs) { | ||
this.waitForAngular(); | ||
if (locatorOrElement.findArrayOverride) { | ||
@@ -560,3 +298,4 @@ return locatorOrElement.findArrayOverride(this.driver).then(function(arr) { | ||
this.driver.get('about:blank'); | ||
this.driver.executeScript('window.name += "' + DEFER_LABEL + '";' + | ||
this.driver.executeScript( | ||
'window.name = "' + DEFER_LABEL + '" + window.name;' + | ||
'window.location.href = "' + destination + '"'); | ||
@@ -630,2 +369,5 @@ | ||
/** | ||
* @type {Protractor} | ||
*/ | ||
var instance; | ||
@@ -651,121 +393,4 @@ | ||
/** | ||
* The Protractor Locator. | ||
* @augments webdriver.Locator.Strategy | ||
*/ | ||
var ProtractorBy = function() {}; | ||
var WebdriverBy = function() {}; | ||
/** | ||
* webdriver's By is an enum of locator functions, so we must set it to | ||
* a prototype before inheriting from it. | ||
*/ | ||
WebdriverBy.prototype = webdriver.By; | ||
util.inherits(ProtractorBy, WebdriverBy); | ||
/** | ||
* Usage: | ||
* <span>{{status}}</span> | ||
* var status = ptor.findElement(protractor.By.binding('{{status}}')); | ||
* | ||
* Note: This ignores parent element restrictions if called with | ||
* WebElement.findElement. | ||
*/ | ||
ProtractorBy.prototype.binding = function(bindingDescriptor) { | ||
return { | ||
findOverride: function(driver, using) { | ||
return driver.findElement(webdriver.By.js(clientSideScripts.findBinding), | ||
using, bindingDescriptor); | ||
}, | ||
findArrayOverride: function(driver, using) { | ||
return driver.findElements( | ||
webdriver.By.js(clientSideScripts.findBindings), | ||
using, bindingDescriptor); | ||
} | ||
}; | ||
}; | ||
/** | ||
* Usage: | ||
* <select ng-model="user" ng-options="user.name for user in users"></select> | ||
* ptor.findElement(protractor.By.select("user")); | ||
*/ | ||
ProtractorBy.prototype.select = function(model) { | ||
return { | ||
findOverride: function(driver, using) { | ||
return driver.findElement( | ||
webdriver.By.js(clientSideScripts.findSelect), using, model); | ||
} | ||
}; | ||
}; | ||
ProtractorBy.prototype.selectedOption = function(model) { | ||
return { | ||
findOverride: function(driver, using) { | ||
return driver.findElement( | ||
webdriver.By.js(clientSideScripts.findSelectedOption), using, model); | ||
} | ||
}; | ||
}; | ||
ProtractorBy.prototype.input = function(model) { | ||
return { | ||
findOverride: function(driver, using) { | ||
return driver.findElement( | ||
webdriver.By.js(clientSideScripts.findInput), using, model); | ||
} | ||
}; | ||
}; | ||
/** | ||
* Usage: | ||
* <div ng-repeat = "cat in pets"> | ||
* <span>{{cat.name}}</span> | ||
* <span>{{cat.age}}</span> | ||
* </div> | ||
* | ||
* // Returns the DIV for the second cat. | ||
* var secondCat = ptor.findElement( | ||
* protractor.By.repeater("cat in pets").row(2)); | ||
* // Returns the SPAN for the first cat's name. | ||
* var firstCatName = ptor.findElement( | ||
* protractor.By.repeater("cat in pets").row(1).column("{{cat.name}}")); | ||
* // Returns an array of WebElements from a column | ||
* var ages = ptor.findElements( | ||
* protractor.By.repeater("cat in pets").column("{{cat.age}}")); | ||
*/ | ||
ProtractorBy.prototype.repeater = function(repeatDescriptor) { | ||
return { | ||
row: function(index) { | ||
return { | ||
findOverride: function(driver, using) { | ||
return driver.findElement( | ||
webdriver.By.js(clientSideScripts.findRepeaterRow), | ||
using, repeatDescriptor, index); | ||
}, | ||
column: function(binding) { | ||
return { | ||
findOverride: function(driver, using) { | ||
return driver.findElement( | ||
webdriver.By.js(clientSideScripts.findRepeaterElement), | ||
using, repeatDescriptor, index, binding); | ||
} | ||
}; | ||
} | ||
}; | ||
}, | ||
column: function(binding) { | ||
return { | ||
findArrayOverride: function(driver, using) { | ||
return driver.findElements( | ||
webdriver.By.js(clientSideScripts.findRepeaterColumn), | ||
using, repeatDescriptor, binding); | ||
} | ||
}; | ||
} | ||
}; | ||
}; | ||
/** | ||
* @type {ProtractorBy} | ||
*/ | ||
exports.By = new ProtractorBy(); |
@@ -34,5 +34,5 @@ { | ||
"scripts": { | ||
"test": "node lib/cli.js spec/basicConf.js; node lib/cli.js spec/altRootConf.js; node_modules/.bin/minijasminenode jasminewd/spec/adapterSpec.js" | ||
"test": "node lib/cli.js spec/basicConf.js; node lib/cli.js spec/altRootConf.js; node lib/cli.js spec/onPrepareConf.js; node_modules/.bin/minijasminenode jasminewd/spec/adapterSpec.js" | ||
}, | ||
"version": "0.9.0" | ||
"version": "0.10.0" | ||
} |
@@ -114,3 +114,3 @@ Protractor | ||
A script is included to do the download for you - run with (add the --nocd option if you do not want to install ChromeDriver) | ||
A script is included to do the download for you - run (add the --nocd option if you do not want to install ChromeDriver) | ||
@@ -121,2 +121,5 @@ ./node_modules/protractor/bin/install_selenium_standalone | ||
java -jar selenium/selenium-server-standalone-2.35.0.jar -Dwebdriver.chrome.driver=./selenium/chromedriver | ||
./selenium/start | ||
For alternate ways to download and start the selenium standalone, see | ||
[the webdriver docs](http://docs.seleniumhq.org/docs/03_webdriver.jsp#running-standalone-selenium-server-for-use-with-remotedrivers). |
@@ -33,4 +33,6 @@ // A reference configuration file. | ||
// The address of a running selenium server. | ||
seleniumAddress: 'http://localhost:4444/wd/hub', | ||
// The address of a running selenium server. If specified, Protractor will | ||
// connect to an already running instance of selenium. This usually looks like | ||
// seleniumAddress: 'http://localhost:4444/wd/hub' | ||
seleniumAddress: null | ||
@@ -62,2 +64,13 @@ // ----- What tests to run ----- | ||
// A callback function called once protractor is ready and available, and | ||
// before the specs are executed | ||
// You can specify a file containing code to run by setting onPrepare to | ||
// the filename string. | ||
onPrepare: function() { | ||
// At this point, global 'protractor' object will be set up, and jasmine | ||
// will be available. For example, you can add a Jasmine reporter with: | ||
// jasmine.getEnv().addReporter(new jasmine.JUnitXmlReporter( | ||
// 'outputdir/', true, true)); | ||
}, | ||
// ----- Options to be passed to minijasminenode ----- | ||
@@ -74,4 +87,4 @@ jasmineNodeOpts: { | ||
// Default time to wait in ms before a test fails. | ||
defaultTimeoutInterval: 5000 | ||
defaultTimeoutInterval: 30000 | ||
} | ||
}; |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
85584
1531
124
22
3