Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

protractor

Package Overview
Dependencies
Maintainers
2
Versions
103
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

protractor - npm Package Compare versions

Comparing version 2.1.0 to 2.2.0

docs/webdriver-vs-protractor.md

2

docs/api-overview.md

@@ -73,3 +73,3 @@ Working with Spec and Config Files

// Spec patterns are relative to the configuration file location passed
// to proractor (in this example conf.js).
// to protractor (in this example conf.js).
// They may include glob patterns.

@@ -76,0 +76,0 @@ specs: ['example-spec.js'],

@@ -60,11 +60,23 @@ FAQ

```javascript
by.css('.foo').evaluate('bar')
element(by.css('.foo')).evaluate('bar')
```
would return whatever `{{bar}}` is in the scope of the element with class 'foo'.
You can also execute arbitrary JavaScript in the browser with
You can also execute arbitrary JavaScript in the browser with:
```javascript
browser.executeScript('your script as a string')
browser.executeScript('your script as a string');
```
You can also pass a regular JavaScript function into `executeScript()`, for example:
```javascript
function getAngularVersion() {
return window.angular.version.full;
}
browser.executeScript(getAngularVersion).then(function (version) {
console.log(version);
});
```
How can I get hold of the browser's console?

@@ -79,3 +91,3 @@ --------------------------------------------

This will output logs from the browser console. Note that logs below the set logging level will be ignored. WebDriver does not currently support changing the logging level for browser logs.
This will output logs from the browser console. Note that logs below the set logging level will be ignored. The default level is warnings and above. To change, add a `loggingPrefs` object to your capabilities, as described [in the DesiredCapabilities docs](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#loggingpreferences-json-object).

@@ -108,6 +120,19 @@ See an example ([spec.js](https://github.com/juliemr/protractor-demo/blob/master/howtos/browserlog/spec.js)) of using this API to fail tests if the console has errors.

```
```javascript
// Note: this is using Jasmine 2.1 reporter syntax.
jasmine.getEnv().addReporter(new function() {
this.specDone = function(result) {
if (result.failedExpectations.length >0) {
// take screenshot
}
};
});
```
Note, you can also choose to take a screenshot in `afterEach`. However, because Jasmine does not execute `afterEach` for timeouts, those would not produce screenshots
* For failures of individual expectations, you can override jasmine's addMatcherResult function as such:
* For failures of individual expectations, you can override jasmine's addMatcherResult/addExpectationResult function as such:
```javascript
// Jasmine 1.3.
var originalAddMatcherResult = jasmine.Spec.prototype.addMatcherResult;

@@ -122,9 +147,20 @@ jasmine.Spec.prototype.addMatcherResult = function() {

```javascript
// Jasmine 2.1
var originalAddExpectationResult = jasmine.Spec.prototype.addExpectationResult;
jasmine.Spec.prototype.addExpectationResult = function() {
if (!arguments[0]) {
// take screenshot
// this.description and arguments[1].message can be useful to constructing the filename.
}
return originalAddExpectationResult.apply(this, arguments);
};
```
[See an example of taking screenshot on spec failures](https://github.com/juliemr/protractor-demo/blob/master/howtos/screenshot/screenshotReporter.js).
How do I produce an XML report of my test results?
--------------------------------------------------
You can use the npm package jasmine-reporters@1.0.0 and add a JUnit XML Reporter. Check out this [example (junitOutputConf.js)](https://github.com/angular/protractor/blob/master/spec/junitOutputConf.js). Note that the latest version of jasmine-reporters is for Jasmine 2.0, which is not yet supported by Protractor, so you'll need to be sure to use version 1.0.0.
You can use the npm package jasmine-reporters@1.0.0 and add a JUnit XML Reporter. Check out this [example (junitOutputConf.js)](https://github.com/angular/protractor/blob/master/spec/junitOutputConf.js). Make sure that you are using the correct version of jasmine-reporters for your version of Jasmine.

@@ -138,4 +174,25 @@ How can I catch errors such as ElementNotFound?

How can I test file uploads?
----------------------------
Via Webdriver, you can just send the absolute file path to the input with type=file. [See this example](http://stackoverflow.com/questions/21305298/how-to-upload-file-in-angularjs-e2e-protractor-testing/21314337#21314337).
If you need to test file upload on a remote server (such as Sauce Labs), [you need to set a remote file detector](https://saucelabs.com/resources/articles/selenium-file-upload). You can do this via `browser.setFileDetector()`, and you'll need access to the `selenium-webdriver` node module.
```js
var remote = require('selenium-webdriver/remote');
browser.setFileDetector(new remote.FileDetector());
```
Why is browser.debugger(); not pausing the test?
-----------------------------------------------
The most likely reason is that you are not running the test in debug mode. To do this you run: `protractor debug` followed by the path to your protractor configuration file.
I get an error: Page reload detected during async script. What does this mean?
------------------------------------------------------------------------------
This means that there was a navigation or reload event while a command was pending
on the browser. Usually, this is because a click action or navigation resulted
in a page load. Protractor is trying to wait for Angular to become stable,
but it's interrupted by the reload.
You may need to insert a `browser.wait` condition to make sure the load
is complete before continuing.

@@ -55,3 +55,3 @@ Choosing a Framework

For a full example, see Protractor’s own test: [/spec/mocha](../spec/mocha).
For a full example, see Protractor’s own test: [/spec/mocha/lib_spec.js](/spec/mocha/lib_spec.js).

@@ -81,3 +81,3 @@

For a full example, see Protractor’s own test: [/spec/cucumber](../spec/cucumber).
For a full example, see Protractor’s own test: [/spec/cucumber/lib.feature](/spec/cucumber/lib.feature).

@@ -84,0 +84,0 @@

@@ -85,3 +85,3 @@ Using Locators

Any action available in WebDriverJS on a WebElement is available on an ElementFinder. [See a full list](#/api?view=ElementFinder).
Any action available in WebDriverJS on a WebElement is available on an ElementFinder. [See a full list](http://angular.github.io/protractor/#/api?view=webdriver.WebElement).

@@ -88,0 +88,0 @@

@@ -136,3 +136,3 @@ Mobile Setup

* Run the following: `appium-doctor` and `authorize_ios` (sudo if necessary)
* You need XCode >= 4.6.3, 5.1.1 recommended. Note, iOS8 (XCode 6) does not work off the shelf (see https://github.com/appium/appium/pull/3517)
* You need XCode >= 4.6.3, 5.1.1 recommended. Note, iOS8 (XCode 6) did not work off the shelf (see https://github.com/appium/appium/pull/3517) but this was resolved [soon after Oct 13, 2014](https://github.com/appium/appium/pull/3517#issuecomment-58940157)

@@ -158,2 +158,7 @@ ###### Running Tests

additional dependencies:
```shell
npm install --save-dev wd wd-bridge
```
iPhone:

@@ -160,0 +165,0 @@ ```javascript

@@ -13,9 +13,9 @@ Protractor Plugins

##In this document:
* [Using Plugins](#using-plugins)
* [Writing Plugins](#writing-plugins)
* [Using Plugins](/docs/plugins.md#using-plugins)
* [Writing Plugins](/docs/plugins.md#writing-plugins)
* Default Plugins
* [Accessibility Plugin](#accessibility-plugin)
* [ngHint Plugin](#nghint-plugin)
* [Timeline Plugin](#timeline-plugin)
* [Console Plugin](#console-plugin-chrome-only)
* [Accessibility Plugin](/docs/plugins.md#accessibility-plugin)
* [ngHint Plugin](/docs/plugins.md#nghint-plugin)
* [Timeline Plugin](/docs/plugins.md#timeline-plugin)
* [Console Plugin](/docs/plugins.md#console-plugin-chrome-only)

@@ -89,35 +89,45 @@ Using Plugins

```js
/*
/**
* Sets up plugins before tests are run. This is called after the WebDriver
* session has been started, but before the test framework has been set up.
*
* @param {Object} config The plugin configuration object. Note that
* this is not the entire Protractor config object, just the
* entry in the plugins array for this plugin.
* @this {Object} bound to module.exports
*
* @return Object If an object is returned, it is merged with the Protractor
* result object. May return a promise.
* @throws {*} If this function throws an error, a failed assertion is added to
* the test results.
*
* @return {q.Promise=} Can return a promise, in which case protractor will wait
* for the promise to resolve before continuing. If the promise is
* rejected, a failed assertion is added to the test results.
*/
exports.setup = function(config) {};
exports.setup = function() {};
/*
/**
* This is called after the tests have been run, but before the WebDriver
* session has been terminated.
*
* @param {Object} config The plugin configuration object.
* @this {Object} bound to module.exports
*
* @return Object If an object is returned, it is merged with the Protractor
* result object. May return a promise.
* @throws {*} If this function throws an error, a failed assertion is added to
* the test results.
*
* @return {q.Promise=} Can return a promise, in which case protractor will wait
* for the promise to resolve before continuing. If the promise is
* rejected, a failed assertion is added to the test results.
*/
exports.teardown = function(config) {};
exports.teardown = function() {};
/*
/**
* Called after the test results have been finalized and any jobs have been
* updated (if applicable).
*
* @param {Object} config The plugin configuration object.
* @this {Object} bound to module.exports
*
* @return Return values are ignored.
* @throws {*} If this function throws an error, it is outputted to the console
*
* @return {q.Promise=} Can return a promise, in which case protractor will wait
* for the promise to resolve before continuing. If the promise is
* rejected, an error is logged to the console.
*/
exports.postResults = function(config) {};
exports.postResults = function() {};

@@ -128,13 +138,93 @@ /**

*
* @param {Object} config The plugin configuration object.
* @param {boolean} passed True if the test passed.
* @param {Object} testInfo information about the test which just ran.
*
* @return Object If an object is returned, it is merged with the Protractor
* result object. May return a promise.
* @this {Object} bound to module.exports
*
* @throws {*} If this function throws an error, a failed assertion is added to
* the test results.
*
* @return {q.Promise=} Can return a promise, in which case protractor will wait
* for the promise to resolve before outputting test results. Protractor
* will *not* wait before executing the next test, however. If the promise
* is rejected, a failed assertion is added to the test results.
*/
exports.postTest = function(config, passed, testInfo) {};
exports.postTest = function(passed, testInfo) {};
/**
* This is called inside browser.get() directly after the page loads, and before
* angular bootstraps.
*
* @this {Object} bound to module.exports
*
* @throws {*} If this function throws an error, a failed assertion is added to
* the test results.
*
* @return {q.Promise=} Can return a promise, in which case protractor will wait
* for the promise to resolve before continuing. If the promise is
* rejected, a failed assertion is added to the test results.
*/
exports.onPageLoad = function() {};
/**
* This is called inside browser.get() directly after angular is done
* bootstrapping/synchronizing. If browser.ignoreSynchronization is true, this
* will not be called.
*
* @this {Object} bound to module.exports
*
* @throws {*} If this function throws an error, a failed assertion is added to
* the test results.
*
* @return {q.Promise=} Can return a promise, in which case protractor will wait
* for the promise to resolve before continuing. If the promise is
* rejected, a failed assertion is added to the test results.
*/
exports.onPageStable = function() {};
/**
* Between every webdriver action, Protractor calls browser.waitForAngular() to
* make sure that Angular has no outstanding $http or $timeout calls.
* You can use waitForPromise() to have Protractor additionally wait for your
* custom promise to be resolved inside of browser.waitForAngular().
*
* @this {Object} bound to module.exports
*
* @throws {*} If this function throws an error, a failed assertion is added to
* the test results.
*
* @return {q.Promise=} Can return a promise, in which case protractor will wait
* for the promise to resolve before continuing. If the promise is
* rejected, a failed assertion is added to the test results, and protractor
* will continue onto the next command. If nothing is returned or something
* other than a promise is returned, protractor will continue onto the next
* command.
*/
exports.waitForPromise = function() {};
/**
* Between every webdriver action, Protractor calls browser.waitForAngular() to
* make sure that Angular has no outstanding $http or $timeout calls.
* You can use waitForCondition() to have Protractor additionally wait for your
* custom condition to be truthy.
*
* @this {Object} bound to module.exports
*
* @throws {*} If this function throws an error, a failed assertion is added to
* the test results.
*
* @return {q.Promise<boolean>|boolean} If truthy, Protractor will continue onto
* the next command. If falsy, webdriver will continuously re-run this
* function until it is truthy. If a rejected promise is returned, a failed
* assertion is added to the test results, and protractor will continue onto
* the next command.
*/
exports.waitForCondition = function() {};
/**
* Used when reporting results.
*
* If you do not specify this property, it will be filled in with something
* reasonable (e.g. the plugin's path)
*
* @type {string}

@@ -145,7 +235,62 @@ */

Each of these exported properties are totally optional.
Each of these exported properties are optional.
The protractor results object follows the format specified in
the [Framework documentation](../lib/frameworks/README.md).
### Provided properties and functions
Extra properties are added to your `module.exports` when Protractor loads your
plugin. These allow your plugin to do things like access its configuration
block or add test results. They are as follows:
```js
/**
* The plugin configuration object. Note that this is not the entire
* Protractor config object, just the entry in the plugins array for this
* plugin.
*
* @type {Object}
*/
exports.config;
/**
* Adds a failed assertion to the test's results.
*
* @param {string} message The error message for the failed assertion
* @param {specName: string, stackTrace: string} options Some optional extra
* information about the assertion:
* - specName The name of the spec which this assertion belongs to.
* Defaults to `PLUGIN_NAME + ' Plugin Tests'`.
* - stackTrace The stack trace for the failure. Defaults to undefined.
* Defaults to `{}`.
*
* @throws {Error} Throws an error if called after results have been reported
*/
exports.addFailure(message, options);
/**
* Adds a passed assertion to the test's results.
*
* @param {specName: string} options Extra information about the assertion:
* - specName The name of the spec which this assertion belongs to.
* Defaults to `PLUGIN_NAME + ' Plugin Tests'`.
* Defaults to `{}`.
*
* @throws {Error} Throws an error if called after results have been reported
*/
exports.addSuccess(options);
/**
* Warns the user that something is problematic.
*
* @param {string} message The message to warn the user about
* @param {specName: string} options Extra information about the assertion:
* - specName The name of the spec which this assertion belongs to.
* Defaults to `PLUGIN_NAME + ' Plugin Tests'`.
* Defaults to `{}`.
*/
exports.addWarning(message, options);
```
If you specify any of these properties in your plugin file, they will be
overwritten.
Accessibility Plugin

@@ -193,3 +338,3 @@ --------------------

},
path: 'node_modules/protractor.plugins/accessiblity'
path: 'node_modules/protractor/plugins/accessibility'
}]

@@ -211,3 +356,3 @@ }

chromeA11YDevTools: true,
path: 'node_modules/protractor/plugins/accessiblity'
path: 'node_modules/protractor/plugins/accessibility'
}]

@@ -273,3 +418,3 @@ }

console.
```js

@@ -276,0 +421,0 @@ exports.config = {

@@ -116,2 +116,6 @@ // Reference Configuration File

// User defined name for the capability that will display in the results log
// Defaults to the browser name
logName: 'Chrome - English',
// Number of times to run this set of capabilities (in parallel, unless

@@ -118,0 +122,0 @@ // limited by maxSessions). Default is 1.

@@ -11,3 +11,3 @@ Tutorial

By default, Protractor uses the [Jasmine](http://jasmine.github.io/1.3/introduction.html) test framework for its testing interface. This tutorial assumes some familiarity with Jasmine.
By default, Protractor uses the [Jasmine](http://jasmine.github.io/) test framework for its testing interface. This tutorial assumes some familiarity with Jasmine, and we will use version 2.3.

@@ -64,2 +64,3 @@ This tutorial will set up a test using a local standalone Selenium Server to control browsers. You will need to have the [Java Development Kit (JDK)](http://www.oracle.com/technetwork/java/javase/downloads/index.html) installed to run the standalone Selenium Server. Check this by running `java -version` from the command line.

exports.config = {
framework: 'jasmine2',
seleniumAddress: 'http://localhost:4444/wd/hub',

@@ -70,3 +71,3 @@ specs: ['spec.js']

This configuration tells Protractor where your test files (`specs`) are, and where to talk to your Selenium Server (`seleniumAddress`). It will use the defaults for all other configuration. Chrome is the default browser.
This configuration tells Protractor where your test files (`specs`) are, and where to talk to your Selenium Server (`seleniumAddress`). It specifies that we will be using Jasmine version 2 for the test framework. It will use the defaults for all other configuration. Chrome is the default browser.

@@ -162,2 +163,3 @@ Now run the test with

exports.config = {
framework: 'jasmine2',
seleniumAddress: 'http://localhost:4444/wd/hub',

@@ -178,2 +180,3 @@ specs: ['spec.js'],

exports.config = {
framework: 'jasmine2',
seleniumAddress: 'http://localhost:4444/wd/hub',

@@ -180,0 +183,0 @@ specs: ['spec.js'],

@@ -18,3 +18,3 @@ describe('angularjs homepage', function() {

todoList = element.all(by.repeater('todo in todos'));
todoList = element.all(by.repeater('todo in todoList.todos'));
});

@@ -28,3 +28,3 @@

it('should add a todo', function() {
var addTodo = element(by.model('todoText'));
var addTodo = element(by.model('todoList.todoText'));
var addButton = element(by.css('[value="add"]'));

@@ -31,0 +31,0 @@

@@ -34,3 +34,3 @@ /**

var optimist = require('optimist').
usage('Usage: protractor [options] [configFile]\n' +
usage('Usage: protractor [configFile] [options]\n' +
'configFile defaults to protractor.conf.js\n' +

@@ -67,2 +67,3 @@ 'The [options] object will override values from the config file.\n' +

alias('invert-grep', 'jasmineNodeOpts.invertGrep').
alias('explorer', 'elementExplorer').
string('capabilities.tunnel-identifier').

@@ -69,0 +70,0 @@ check(function(arg) {

@@ -19,2 +19,22 @@ /**

/* Wraps a function up into a string with its helper functions so that it can
* call those helper functions client side
*
* @param {function} fun The function to wrap up with its helpers
* @param {...function} The helper functions. Each function must be named
*
* @return {string} The string which, when executed, will invoke fun in such a
* way that it has access to its helper functions
*/
function wrapWithHelpers(fun) {
var helpers = Array.prototype.slice.call(arguments, 1);
if (!helpers.length) {
return fun;
}
var FunClass = Function; // Get the linter to allow this eval
return new FunClass(
helpers.join(';') + String.fromCharCode(59) +
' return (' + fun.toString() + ').apply(this, arguments);');
}
/**

@@ -94,2 +114,11 @@ * Wait until Angular has finished rendering and has

function repeaterMatch(ngRepeat, repeater, exact) {
if (exact) {
return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].
split('=')[0].trim() == repeater;
} else {
return ngRepeat.indexOf(repeater) != -1;
}
}
/**

@@ -108,12 +137,3 @@ * Find an array of elements matching a row within an ng-repeat.

*/
functions.findRepeaterRows = function(repeater, exact, index, using) {
function repeaterMatch(ngRepeat, repeater, exact) {
if (exact) {
return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].
trim() == repeater;
} else {
return ngRepeat.indexOf(repeater) != -1;
}
}
function findRepeaterRows(repeater, exact, index, using) {
using = using || document;

@@ -157,3 +177,4 @@

return [].concat(row, multiRow);
};
}
functions.findRepeaterRows = wrapWithHelpers(findRepeaterRows, repeaterMatch);

@@ -169,12 +190,3 @@ /**

*/
functions.findAllRepeaterRows = function(repeater, exact, using) {
function repeaterMatch(ngRepeat, repeater, exact) {
if (exact) {
return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].
trim() == repeater;
} else {
return ngRepeat.indexOf(repeater) != -1;
}
}
function findAllRepeaterRows(repeater, exact, using) {
using = using || document;

@@ -212,3 +224,4 @@

return rows;
};
}
functions.findAllRepeaterRows = wrapWithHelpers(findAllRepeaterRows, repeaterMatch);

@@ -227,12 +240,3 @@ /**

*/
functions.findRepeaterElement = function(repeater, exact, index, binding, using, rootSelector) {
function repeaterMatch(ngRepeat, repeater, exact) {
if (exact) {
return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].
trim() == repeater;
} else {
return ngRepeat.indexOf(repeater) != -1;
}
}
function findRepeaterElement(repeater, exact, index, binding, using, rootSelector) {
var matches = [];

@@ -322,3 +326,4 @@ var root = document.querySelector(rootSelector || 'body');

return matches;
};
}
functions.findRepeaterElement = wrapWithHelpers(findRepeaterElement, repeaterMatch);

@@ -336,12 +341,3 @@ /**

*/
functions.findRepeaterColumn = function(repeater, exact, binding, using, rootSelector) {
function repeaterMatch(ngRepeat, repeater, exact) {
if (exact) {
return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].
trim() == repeater;
} else {
return ngRepeat.indexOf(repeater) != -1;
}
}
function findRepeaterColumn(repeater, exact, binding, using, rootSelector) {
var matches = [];

@@ -429,3 +425,4 @@ var root = document.querySelector(rootSelector || 'body');

return matches;
};
}
functions.findRepeaterColumn = wrapWithHelpers(findRepeaterColumn, repeaterMatch);

@@ -659,2 +656,15 @@ /**

/**
* Retrieve the pending $http requests.
*
* @param {string} selector The selector housing an ng-app
* @return {!Array<!Object>} An array of pending http requests.
*/
functions.getPendingHttpRequests = function(selector) {
var el = document.querySelector(selector);
var $injector = angular.element(el).injector();
var $http = $injector.get('$http');
return $http.pendingRequests;
};
/* Publish all the functions as strings to pass to WebDriver's

@@ -661,0 +671,0 @@ * exec[Async]Script. In addition, also include a script that will

@@ -353,19 +353,3 @@ var webdriver = require('selenium-webdriver');

return this.getWebElements().then(function(arr) {
var list = arr.map(function(webElem) {
// Calling any method forces a staleness check
return webElem.isEnabled().then(function() {
return 1; // is present
}, function(err) {
if (err.code == webdriver.error.ErrorCode.STALE_ELEMENT_REFERENCE) {
return 0; // not present
} else {
throw err;
}
});
});
return webdriver.promise.all(list).then(function(presenceArr) {
return presenceArr.reduce(function(acc, isPresent) {
return acc + isPresent;
}, 0);
});
return arr.length;
}, function(err) {

@@ -670,2 +654,3 @@ if (err.code == webdriver.error.ErrorCode.NO_SUCH_ELEMENT) {

* @constructor
* @extends {webdriver.WebElement}
* @param {Protractor} ptor

@@ -926,4 +911,21 @@ * @param {ElementArrayFinder} elementArrayFinder The ElementArrayFinder

ElementFinder.prototype.isPresent = function() {
return this.parentElementArrayFinder.count().then(function(count) {
return !!count;
return this.parentElementArrayFinder.getWebElements().then(function(arr) {
if (arr.length === 0) {
return false;
}
return arr[0].isEnabled().then(function() {
return true; // is present, whether it is enabled or not
}, function(err) {
if (err.code == webdriver.error.ErrorCode.STALE_ELEMENT_REFERENCE) {
return false;
} else {
throw err;
}
});
}, function(err) {
if (err.code == webdriver.error.ErrorCode.NO_SUCH_ELEMENT) {
return false;
} else {
throw err;
}
});

@@ -933,4 +935,6 @@ };

/**
* Override for WebElement.prototype.isElementPresent so that protractor waits
* for Angular to settle before making the check.
* Same as ElementFinder.isPresent(), except this checks whether the element
* identified by the subLocator is present, rather than the current element
* finder. i.e. `element(by.css('#abc')).element(by.css('#def')).isPresent()` is
* identical to `element(by.css('#abc')).isElementPresent(by.css('#def'))`.
*

@@ -941,5 +945,10 @@ * @see ElementFinder.isPresent

* @return {ElementFinder} which resolves to whether
* the element is present on the page.
* the subelement is present on the page.
*/
ElementFinder.prototype.isElementPresent = function(subLocator) {
if (!subLocator) {
throw new Error('SubLocator is not supplied as a parameter to ' +
'`isElementPresent(subLocator)`. You are probably looking for the ' +
'function `isPresent()`.');
}
return this.element(subLocator).isPresent();

@@ -946,0 +955,0 @@ };

@@ -49,2 +49,8 @@ var webdriver = require('selenium-webdriver');

*
* @example
* var EC = protractor.ExpectedConditions;
* var titleIsNotFoo = EC.not(EC.titleIs('Foo'));
* // Waits for title to become something besides 'foo'.
* browser.wait(titleIsNotFoo, 5000);
*
* @param {!function} expectedCondition

@@ -94,2 +100,9 @@ *

*
* @example
* var EC = protractor.ExpectedConditions;
* var titleContainsFoo = EC.titleContains('Foo');
* var titleIsNotFooBar = EC.not(EC.titleIs('FooBar'));
* // Waits for title to contain 'Foo', but is not 'FooBar'
* browser.wait(EC.and(titleContainsFoo, titleIsNotFooBar), 5000);
*
* @param {Array.<Function>} fns An array of expected conditions to 'and' together.

@@ -109,2 +122,9 @@ *

*
* @example
* var EC = protractor.ExpectedConditions;
* var titleContainsFoo = EC.titleContains('Foo');
* var titleContainsBar = EC.titleContains('Bar');
* // Waits for title to contain either 'Foo' or 'Bar'
* browser.wait(EC.or(titleContainsFoo, titleContainsBar), 5000);
*
* @param {Array.<Function>} fns An array of expected conditions to 'or' together.

@@ -123,2 +143,7 @@ *

*
* @example
* var EC = protractor.ExpectedConditions;
* // Waits for an alert pops up.
* browser.wait(EC.alertIsPresent(), 5000);
*
* @return {!function} An expected condition that returns a promise

@@ -145,2 +170,7 @@ * representing whether an alert is present.

*
* @example
* var EC = protractor.ExpectedConditions;
* // Waits for the element with id 'abc' to be clickable.
* browser.wait(EC.elementToBeClickable($('#abc')), 5000);
*
* @param {!ElementFinder} elementFinder The element to check

@@ -161,2 +191,7 @@ *

*
* @example
* var EC = protractor.ExpectedConditions;
* // Waits for the element with id 'abc' to contain the text 'foo'.
* browser.wait(EC.textToBePresentInElement($('#abc'), 'foo'), 5000);
*
* @param {!ElementFinder} elementFinder The element to check

@@ -181,2 +216,7 @@ * @param {!string} text The text to verify against

*
* @example
* var EC = protractor.ExpectedConditions;
* // Waits for the element with id 'myInput' to contain the input 'foo'.
* browser.wait(EC.textToBePresentInElementValue($('#myInput'), 'foo'), 5000);
*
* @param {!ElementFinder} elementFinder The element to check

@@ -201,2 +241,7 @@ * @param {!string} text The text to verify against

*
* @example
* var EC = protractor.ExpectedConditions;
* // Waits for the title to contain 'foo'.
* browser.wait(EC.titleContains('foo'), 5000);
*
* @param {!string} title The fragment of title expected

@@ -218,2 +263,7 @@ *

*
* @example
* var EC = protractor.ExpectedConditions;
* // Waits for the title to be 'foo'.
* browser.wait(EC.titleIs('foo'), 5000);
*
* @param {!string} title The expected title, which must be an exact match.

@@ -235,3 +285,9 @@ *

* of a page. This does not necessarily mean that the element is visible.
* This is the opposite of 'stalenessOf'.
*
* @example
* var EC = protractor.ExpectedConditions;
* // Waits for the element with id 'abc' to be present on the dom.
* browser.wait(EC.presenceOf($('#abc')), 5000);
*
* @param {!ElementFinder} elementFinder The element to check

@@ -248,4 +304,9 @@ *

* An expectation for checking that an element is not attached to the DOM
* of a page.
* of a page. This is the opposite of 'presenceOf'.
*
* @example
* var EC = protractor.ExpectedConditions;
* // Waits for the element with id 'abc' to be no longer present on the dom.
* browser.wait(EC.stalenessOf($('#abc')), 5000);
*
* @param {!ElementFinder} elementFinder The element to check

@@ -263,4 +324,10 @@ *

* page and visible. Visibility means that the element is not only displayed
* but also has a height and width that is greater than 0.
* but also has a height and width that is greater than 0. This is the opposite
* of 'invisibilityOf'.
*
* @example
* var EC = protractor.ExpectedConditions;
* // Waits for the element with id 'abc' to be visible on the dom.
* browser.wait(EC.visibilityOf($('#abc')), 5000);
*
* @param {!ElementFinder} elementFinder The element to check

@@ -279,4 +346,9 @@ *

* An expectation for checking that an element is either invisible or not
* present on the DOM.
* present on the DOM. This is the opposite of 'visibilityOf'.
*
* @example
* var EC = protractor.ExpectedConditions;
* // Waits for the element with id 'abc' to be no longer visible on the dom.
* browser.wait(EC.invisibilityOf($('#abc')), 5000);
*
* @param {!ElementFinder} elementFinder The element to check

@@ -294,2 +366,7 @@ *

*
* @example
* var EC = protractor.ExpectedConditions;
* // Waits for the element with id 'myCheckbox' to be selected.
* browser.wait(EC.elementToBeSelected($('#myCheckbox')), 5000);
*
* @param {!ElementFinder} elementFinder The element to check

@@ -296,0 +373,0 @@ *

@@ -82,3 +82,3 @@ var ConfigParser = require('../configParser'),

feature = event.getPayloadItem('feature');
if (typeof originalHandleAfterScenarioEvent == 'function') {
if (typeof originalHandleBeforeFeatureEvent == 'function') {
originalHandleBeforeFeatureEvent.apply(formatter, arguments);

@@ -85,0 +85,0 @@ } else {

@@ -34,9 +34,11 @@ var q = require('q');

var specInfo = {
name: spec.suite.getFullName(),
category: spec.description
name: spec.description,
category: spec.suite.getFullName()
};
if (spec.results().passedCount) {
this.emitter.emit('testPass', specInfo);
} else if (spec.results().failedCount) {
this.emitter.emit('testFail', specInfo);
if (!spec.results().skipped) {
if (spec.results().passed()) {
this.emitter.emit('testPass', specInfo);
} else {
this.emitter.emit('testFail', specInfo);
}
}

@@ -43,0 +45,0 @@

@@ -74,4 +74,7 @@ /**

var shortName = (capabilities.browserName) ? capabilities.browserName : '';
shortName = (capabilities.logName) ? capabilities.logName
: (capabilities.browserName) ? capabilities.browserName : '';
shortName += (capabilities.version) ? capabilities.version : '';
shortName += (' #' + result.taskId);
shortName += (capabilities.logName && capabilities.count < 2) ?
'' : ' #' + result.taskId;
if (result.failedCount) {

@@ -78,0 +81,0 @@ log_(shortName + ' failed ' + result.failedCount + ' test(s)');

@@ -11,3 +11,3 @@ var util = require('util');

* @alias by
* @augments webdriver.Locator.Strategy
* @extends {webdriver.By}
*/

@@ -82,8 +82,8 @@ var ProtractorBy = function() {};

/**
* Find an element by binding. Does a partial match, so any elements bound to
* variables containing the input string will be returned.
* Find an element by text binding. Does a partial match, so any elements bound
* to variables containing the input string will be returned.
*
* Note: For AngularJS version 1.2, the interpolation brackets, usually {{}},
* are allowed in the binding description string. For Angular version 1.3, they
* are not allowed, and no elements will be found if they are used.
* Note: For AngularJS version 1.2, the interpolation brackets, (usually {{}}),
* are optionally allowed in the binding description string. For Angular version
* 1.3+, they are not allowed, and no elements will be found if they are used.
*

@@ -90,0 +90,0 @@ * @view

@@ -1,16 +0,9 @@

var q = require('q'),
helper = require('./util'),
var webdriver = require('selenium-webdriver'),
q = require('q'),
ConfigParser = require('./configParser'),
log = require('./logger');
var logPrefix = '[plugins]';
/**
* Custom console.log proxy
* @param {*} [stuff...] Any value to log
* @private
*/
var log_ = function() {
var args = [logPrefix].concat([].slice.call(arguments));
log.puts.apply(log, args);
var PROMISE_TYPE = {
Q: 0,
WEBDRIVER: 1
};

@@ -30,3 +23,5 @@

this.pluginObjs = [];
this.pluginConfs.forEach(function(pluginConf) {
this.assertions = {};
this.resultsReported = false;
this.pluginConfs.forEach(function(pluginConf, i) {
var path;

@@ -39,7 +34,8 @@ if (pluginConf.path) {

}
var pluginObj;
if (path) {
pluginConf.name = path;
self.pluginObjs.push(require(path));
} else if(pluginConf.inline) {
self.pluginObjs.push(pluginConf.inline);
pluginObj = require(path);
} else if (pluginConf.inline) {
pluginObj = pluginConf.inline;
} else {

@@ -49,8 +45,51 @@ throw new Error('Plugin configuration did not contain a valid path or ' +

}
annotatePluginObj(self, pluginObj, pluginConf, i);
log.debug('Plugin "' + pluginObj.name + '" loaded.');
self.pluginObjs.push(pluginObj);
});
};
var noop = function() {};
/**
* Adds properties to a plugin's object
*
* @see docs/plugins.md#provided-properties-and-functions
*/
function annotatePluginObj(self, obj, conf, i) {
function addAssertion(info, passed, message) {
if (self.resultsReported) {
throw new Error('Cannot add new tests results, since they were already ' +
'reported.');
}
info = info || {};
var specName = info.specName || (obj.name + ' Plugin Tests');
var assertion = {passed: passed};
if (!passed) {
assertion.errorMsg = message;
if (info.stackTrace) {
assertion.stackTrace = info.stackTrace;
}
}
self.assertions[specName] = self.assertions[specName] || [];
self.assertions[specName].push(assertion);
}
Plugins.printPluginResults = function(specResults) {
obj.name = obj.name || conf.name || conf.path || conf.package ||
('Plugin #' + i);
obj.config = conf;
obj.addFailure = function(message, options) {
addAssertion(options, false, message);
};
obj.addSuccess = function(options) {
addAssertion(options, true);
};
obj.addWarning = function(message, options) {
options = options || {};
log.puts('Warning ' + (options.specName ? 'in ' + options.specName :
'from "' + obj.name + '" plugin') +
': ' + message);
};
}
function printPluginResults(specResults) {
var green = '\x1b[32m';

@@ -76,4 +115,8 @@ var red = '\x1b[31m';

for (var k = 0; k < specResult.assertions.length; k++) {
if (!specResult.assertions[k].passed) {
log.puts('\t\t' + specResult.assertions[k].errorMsg);
var assertion = specResult.assertions[k];
if (!assertion.passed) {
log.puts('\t\t' + assertion.errorMsg);
if (assertion.stackTrace) {
log.puts('\t\t' + assertion.stackTrace.replace(/\n/g, '\n\t\t'));
}
}

@@ -83,79 +126,132 @@ }

}
};
function pluginFunFactory(funName) {
return function() {
var names = [];
var promises = [];
for (var i = 0; i < this.pluginConfs.length; ++i) {
var pluginConf = this.pluginConfs[i];
var pluginObj = this.pluginObjs[i];
names.push(pluginObj.name || pluginConf.name);
promises.push(
(pluginObj[funName] || noop).apply(
pluginObj[funName],
[pluginConf].concat([].slice.call(arguments))));
}
return q.all(promises).then(function(results) {
// Join the results into a single object and output any test results
var ret = {failedCount: 0};
for (var i = 0; i < results.length; i++) {
var pluginResult = results[i];
if (!!pluginResult && (typeof pluginResult == typeof {})) {
if (typeof pluginResult.failedCount != typeof 1) {
log_('Plugin "' + names[i] + '" returned a malformed object');
continue; // Just ignore this result
}
// Output test results
if (pluginResult.specResults) {
log.puts('Plugin: ' + names[i] + ' (' + funName + ')');
Plugins.printPluginResults(pluginResult.specResults);
}
// Join objects
ret = helper.joinTestLogs(ret, pluginResult);
}
}
return ret;
});
};
}
/**
* Sets up plugins before tests are run.
* Gets the tests results generated by any plugins
*
* @return {q.Promise} A promise which resolves when the plugins have all been
* set up.
* @see lib/frameworks/README.md#requirements for a complete description of what
* the results object must look like
*
* @return {Object} The results object
*/
Plugins.prototype.setup = pluginFunFactory('setup');
Plugins.prototype.getResults = function() {
var results = {
failedCount: 0,
specResults: []
};
for (var specName in this.assertions) {
results.specResults.push({
description: specName,
assertions: this.assertions[specName]
});
results.failedCount += this.assertions[specName].filter(
function(assertion) {return !assertion.passed;}).length;
}
printPluginResults(results.specResults);
this.resultsReported = true;
return results;
};
/**
* Tears down plugins after tests are run.
* Calls a function from a plugin safely. If the plugin's function throws an
* exception or returns a rejected promise, that failure will be logged as a
* failed test result instead of crashing protractor. If the tests results have
* already been reported, the failure will be logged to the console.
*
* @return {q.Promise} A promise which resolves when the plugins have all been
* torn down.
* @param {Object} pluginObj The plugin object containing the function to be run
* @param {string} funName The name of the function we want to run
* @param {*[]} args The arguments we want to invoke the function with
* @param {PROMISE_TYPE} promiseType The type of promise (WebDriver or Q) that
* should be used
* @param {boolean} resultsReported If the results have already been reported
* @param {*} failReturnVal The value to return if the function fails
*
* @return {webdriver.promise.Promise} A promise which resolves to the
* function's return value
*/
Plugins.prototype.teardown = pluginFunFactory('teardown');
function safeCallPluginFun(pluginObj, funName, args, promiseType,
resultsReported, failReturnVal) {
var deferred = promiseType == PROMISE_TYPE.Q ? q.defer() :
webdriver.promise.defer();
var logError = function(e) {
if (resultsReported) {
printPluginResults([{
description: pluginObj.name + ' Runtime',
assertions: [{
passed: false,
errorMsg: 'Failure during ' + funName + ': ' + (e.message || e),
stackTrace: e.stack
}]
}]);
} else {
pluginObj.addFailure('Failure during ' + funName + ': ' +
e.message || e, {stackTrace: e.stack});
}
deferred.fulfill(failReturnVal);
};
try {
var result = pluginObj[funName].apply(pluginObj, args);
if (webdriver.promise.isPromise(result)) {
result.then(function() {
deferred.fulfill.apply(deferred, arguments);
}, function(e) {
logError(e);
});
} else {
deferred.fulfill(result);
}
} catch(e) {
logError(e);
}
return deferred.promise;
}
/**
* Run after the test results have been processed (any values returned will
* be ignored), but before the process exits. Final chance for cleanup.
* Generates the handler for a plugin function (e.g. the setup() function)
*
* @return {q.Promise} A promise which resolves when the plugins have all been
* torn down.
* @param {string} funName The name of the function to make a handler for
* @param {PROMISE_TYPE} promiseType The type of promise (WebDriver or Q) that
* should be used
* @param {boolean=} failReturnVal The value that the function should return if
* the plugin crashes
*
* @return {Function} The handler
*/
Plugins.prototype.postResults = pluginFunFactory('postResults');
function pluginFunFactory(funName, promiseType, failReturnVal) {
return function() {
var promises = [];
var self = this;
var args = arguments;
this.pluginObjs.forEach(function(pluginObj) {
if (pluginObj[funName]) {
promises.push(safeCallPluginFun(pluginObj, funName, args, promiseType,
self.resultsReported, failReturnVal));
}
});
if (promiseType == PROMISE_TYPE.Q) {
return q.all(promises);
} else {
return webdriver.promise.all(promises);
}
};
}
/**
* Called after each test block completes.
*
* @return {q.Promise} A promise which resolves when the plugins have all been
* torn down.
* @see docs/plugins.md#writing-plugins for information on these functions
*/
Plugins.prototype.postTest = pluginFunFactory('postTest');
Plugins.prototype.setup = pluginFunFactory('setup', PROMISE_TYPE.Q);
Plugins.prototype.teardown = pluginFunFactory('teardown', PROMISE_TYPE.Q);
Plugins.prototype.postResults = pluginFunFactory('postResults', PROMISE_TYPE.Q);
Plugins.prototype.postTest = pluginFunFactory('postTest', PROMISE_TYPE.Q);
Plugins.prototype.onPageLoad = pluginFunFactory('onPageLoad',
PROMISE_TYPE.WEBDRIVER);
Plugins.prototype.onPageStable = pluginFunFactory('onPageStable',
PROMISE_TYPE.WEBDRIVER);
Plugins.prototype.waitForPromise = pluginFunFactory('waitForPromise',
PROMISE_TYPE.WEBDRIVER);
Plugins.prototype.waitForCondition = pluginFunFactory('waitForCondition',
PROMISE_TYPE.WEBDRIVER, true);
module.exports = Plugins;

@@ -15,2 +15,3 @@ var util = require('util');

// jshint browser: true
/* global angular */

@@ -186,2 +187,9 @@

/**
* Set by the runner.
*
* @type {q.Promise} Done when the new browser is ready for use
*/
this.ready = null;
/**
* The reset URL to use between page loads.

@@ -196,5 +204,8 @@ *

// Safari accepts data urls, but SafariDriver fails after one is used.
// PhantomJS produces a "Detected a page unload event" if we use data urls
var browserName = caps.get('browserName');
if (browserName === 'internet explorer' || browserName === 'safari') {
self.resetUrl = 'about:blank';
if (browserName === 'internet explorer' ||
browserName === 'safari' ||
browserName === 'phantomjs') {
self.resetUrl = 'about:blank';
}

@@ -221,3 +232,26 @@ });

/**
* Get the processed configuration object that is currently being run. This
* will contain the specs and capabilities properties of the current runner
* instance.
*
* Set by the runner.
*
* @return {q.Promise} A promise which resolves to the capabilities object.
*/
Protractor.prototype.getProcessedConfig = null;
/**
* Fork another instance of protractor for use in interactive tests.
*
* Set by the runner.
*
* @param {boolean} opt_useSameUrl Whether to navigate to current url on creation
* @param {boolean} opt_copyMockModules Whether to apply same mock modules on creation
* @return {Protractor} a protractor instance.
*/
Protractor.prototype.forkNewDriverInstance = null;
/**
* The same as {@code webdriver.WebDriver.prototype.executeScript},

@@ -283,5 +317,7 @@ * but with a customized description for debugging.

var description = opt_description ? ' - ' + opt_description : '';
var self = this;
if (this.ignoreSynchronization) {
return webdriver.promise.fulfilled();
}
return this.executeAsyncScript_(

@@ -296,3 +332,13 @@ clientSideScripts.waitForAngular,

}
}).then(null, function(err) {
}).then(function() {
return self.driver.controlFlow().execute(function() {
return self.plugins_.waitForPromise();
}, 'Plugins.waitForPromise()').then(function() {
return self.driver.wait(function() {
return self.plugins_.waitForCondition().then(function(results) {
return results.reduce(function(x, y) { return x && y; }, true);
});
}, self.allScriptsTimeout, 'Plugins.waitForCondition()');
});
}, function(err) {
var timeout;

@@ -310,5 +356,37 @@ if (/asynchronous script timeout/.test(err.message)) {

if (timeout) {
throw 'Timed out waiting for Protractor to synchronize with ' +
var errMsg = 'Timed out waiting for Protractor to synchronize with ' +
'the page after ' + timeout + '. Please see ' +
'https://github.com/angular/protractor/blob/master/docs/faq.md';
var pendingTimeoutsPromise = self.executeScript_(
'return window.NG_PENDING_TIMEOUTS',
'Protractor.waitForAngular() - getting pending timeouts' + description
);
var pendingHttpsPromise = self.executeScript_(
clientSideScripts.getPendingHttpRequests,
'Protractor.waitForAngular() - getting pending https' + description,
self.rootEl
);
return webdriver.promise.all([
pendingTimeoutsPromise, pendingHttpsPromise]).
then(function(arr) {
var pendingTimeouts = arr[0] || [];
var pendingHttps = arr[1] || [];
var key, pendingTasks = [];
for (key in pendingTimeouts) {
if (pendingTimeouts.hasOwnProperty(key)) {
pendingTasks.push('- $timeout: ' + pendingTimeouts[key]);
}
}
for (key in pendingHttps) {
pendingTasks.push('- $http: ' + pendingHttps[key].url);
}
if (pendingTasks.length) {
errMsg += '. The following tasks were pending:\n';
errMsg += pendingTasks.join('\n');
}
throw errMsg;
}, function() {
throw errMsg;
});
} else {

@@ -424,2 +502,44 @@ throw err;

}
}]).
config(['$provide', function($provide) {
$provide.decorator('$timeout', ['$delegate', function($delegate) {
var $timeout = $delegate;
var taskId = 0;
if (!window.NG_PENDING_TIMEOUTS) {
window.NG_PENDING_TIMEOUTS = {};
}
var extendedTimeout = function() {
var args = Array.prototype.slice.call(arguments);
if (typeof(args[0]) !== 'function') {
return $timeout.apply(null, args);
}
taskId++;
var fn = args[0];
window.NG_PENDING_TIMEOUTS[taskId] = fn.toString();
var wrappedFn = (function(taskId_) {
return function() {
delete window.NG_PENDING_TIMEOUTS[taskId_];
return fn.apply(null, arguments);
};
})(taskId);
args[0] = wrappedFn;
var promise = $timeout.apply(null, args);
promise.ptorTaskId_ = taskId;
return promise;
};
extendedTimeout.cancel = function() {
var taskId_ = arguments[0] && arguments[0].ptorTaskId_;
if (taskId_) {
delete window.NG_PENDING_TIMEOUTS[taskId_];
}
return $timeout.cancel.apply($timeout, arguments);
};
return extendedTimeout;
}]);
}]);

@@ -456,3 +576,6 @@ });

if (this.ignoreSynchronization) {
return this.driver.get(destination);
this.driver.get(destination);
return this.driver.controlFlow().execute(function() {
return self.plugins_.onPageLoad();
});
}

@@ -491,2 +614,6 @@

this.driver.controlFlow().execute(function() {
return self.plugins_.onPageLoad();
});
// Make sure the page is an Angular page.

@@ -529,5 +656,13 @@ self.executeAsyncScript_(clientSideScripts.testForAngular,

moduleNames)
.then(deferred.fulfill, deferred.reject);
.then(null, deferred.reject);
return deferred;
this.driver.controlFlow().execute(function() {
return self.plugins_.onPageStable().then(function() {
deferred.fulfill();
}, function(error) {
deferred.reject(error);
});
});
return deferred.promise;
};

@@ -543,6 +678,5 @@

*
* @param {number=} opt_timeout Number of seconds to wait for Angular to start.
* @param {number=} opt_timeout Number of milliseconds to wait for Angular to start.
*/
Protractor.prototype.refresh = function(opt_timeout) {
var timeout = opt_timeout || 10;
var self = this;

@@ -557,3 +691,3 @@

'Protractor.refresh() - getUrl').then(function(href) {
return self.get(href, timeout);
return self.get(href, opt_timeout);
});

@@ -560,0 +694,0 @@ };

@@ -178,3 +178,3 @@ var protractor = require('./protractor'),

*/
Runner.prototype.createBrowser = function() {
Runner.prototype.createBrowser = function(plugins) {
var config = this.config_;

@@ -187,5 +187,9 @@ var driver = this.driverprovider_.getNewDriver();

browser_.params = config.params;
browser_.plugins_ = plugins;
if (config.getPageTimeout) {
browser_.getPageTimeout = config.getPageTimeout;
}
if (config.allScriptsTimeout) {
browser_.allScriptsTimeout = config.allScriptsTimeout;
}
if (config.debuggerServerPort) {

@@ -197,15 +201,5 @@ browser_.debuggerServerPort_ = config.debuggerServerPort;

/**
* @type {q.Promise} Done when the new browser is ready for use
*/
browser_.ready =
driver.manage().timeouts().setScriptTimeout(config.allScriptsTimeout);
/**
* Get the processed configuration object that is currently being run. This
* will contain the specs and capabilities properties of the current runner
* instance.
*
* @return {q.Promise} A promise which resolves to the capabilities object.
*/
browser_.getProcessedConfig = function() {

@@ -215,11 +209,4 @@ return webdriver.promise.fulfilled(config);

/**
* Fork another instance of protractor for use in interactive tests.
*
* @param {boolean} opt_useSameUrl Whether to navigate to current url on creation
* @param {boolean} opt_copyMockModules Whether to apply same mock modules on creation
* @return {Protractor} a protractor instance.
*/
browser_.forkNewDriverInstance = function(opt_useSameUrl, opt_copyMockModules) {
var newBrowser = self.createBrowser();
var newBrowser = self.createBrowser(plugins);
if (opt_copyMockModules) {

@@ -260,4 +247,6 @@ newBrowser.mockModules_ = browser_.mockModules_;

testPassed,
plugins,
browser_;
plugins = new Plugins(self.config_),
pluginPostTestPromises,
browser_,
results;

@@ -272,4 +261,3 @@ if (this.config_.framework !== 'explorer' && !this.config_.specs.length) {

// 2) Create a browser and setup globals
}).then(function() {
browser_ = self.createBrowser();
browser_ = self.createBrowser(plugins);
self.setupGlobals_(browser_);

@@ -285,6 +273,5 @@ return browser_.ready.then(browser_.getSession).then(function(session) {

}).then(function() {
plugins = new Plugins(self.config_);
return plugins.setup();
// 4) Execute test cases
}).then(function(pluginSetupResults) {
}).then(function() {
// Do the framework setup here so that jasmine and mocha globals are

@@ -335,3 +322,3 @@ // available to the onPrepare function.

// an event emitter).
var pluginPostTestPromises = [];
pluginPostTestPromises = [];

@@ -345,25 +332,17 @@ self.on('testPass', function(testInfo) {

return require(frameworkPath).run(self, self.config_.specs).
then(function(testResults) {
return q.all(pluginPostTestPromises).then(function(postTestResultList) {
var results = helper.joinTestLogs(pluginSetupResults, testResults);
postTestResultList.forEach(function(postTestResult) {
results = helper.joinTestLogs(results, postTestResult);
});
return results;
});
});
// 5) Teardown plugins
log.debug('Running with spec files ' + self.config_.specs);
return require(frameworkPath).run(self, self.config_.specs);
// 5) Wait for postTest plugins to finish
}).then(function(testResults) {
var deferred = q.defer();
plugins.teardown().then(function(pluginTeardownResults) {
deferred.resolve(helper.joinTestLogs(testResults, pluginTeardownResults));
}, function(error) {
deferred.reject(error);
});
return deferred.promise;
// 6) Teardown
}).then(function(result) {
self.emit('testsDone', result);
testPassed = result.failedCount === 0;
results = testResults;
return q.all(pluginPostTestPromises);
// 6) Teardown plugins
}).then(function() {
return plugins.teardown();
// 7) Teardown
}).then(function() {
results = helper.joinTestLogs(results, plugins.getResults());
self.emit('testsDone', results);
testPassed = results.failedCount === 0;
if (self.driverprovider_.updateJob) {

@@ -378,6 +357,6 @@ return self.driverprovider_.updateJob({

}
// 7) Let plugins do final cleanup
// 8) Let plugins do final cleanup
}).then(function() {
return plugins.postResults();
// 8) Exit process
// 9) Exit process
}).then(function() {

@@ -384,0 +363,0 @@ var exitCode = testPassed ? 0 : 1;

@@ -58,9 +58,8 @@ var EOL = require('os').EOL;

var capabilities = this.task.capabilities;
tag += (capabilities.browserName) ?
capabilities.browserName : '';
tag += (capabilities.version) ?
(' ' + capabilities.version) : '';
tag += (capabilities.platform) ?
(' ' + capabilities.platform) : '';
tag += (' #' + this.task.taskId);
tag += (capabilities.logName) ? capabilities.logName
: (capabilities.browserName) ? capabilities.browserName : '';
tag += (capabilities.version) ? (' ' + capabilities.version) : '';
tag += (capabilities.platform) ? (' ' + capabilities.platform) : '';
tag += (capabilities.logName && capabilities.count < 2) ?
'' : ' #' + this.task.taskId;
tag += '] ';

@@ -67,0 +66,0 @@

@@ -15,3 +15,3 @@ {

"dependencies": {
"request": "~2.36.0",
"request": "~2.57.0",
"selenium-webdriver": "2.45.1",

@@ -58,3 +58,3 @@ "minijasminenode": "1.1.1",

"license": "MIT",
"version": "2.1.0"
"version": "2.2.0"
}

@@ -52,24 +52,21 @@ var q = require('q'),

*
* @param {Object} config The configuration file for the accessibility plugin
* @return {q.Promise} A promise which resolves to the results of any passed or
* failed tests
* @this {Object} The plugin context object
* @return {q.Promise} A promise which resolves when all audits are finished
* @public
*/
function teardown(config) {
function teardown() {
var audits = [];
if (config.chromeA11YDevTools) {
audits.push(runChromeDevTools(config));
if (this.config.chromeA11YDevTools) {
audits.push(runChromeDevTools(this));
}
// check for Tenon config and an actual API key, not the placeholder
if (config.tenonIO && /[A-Za-z][0-9]/.test(config.tenonIO.options.key)) {
audits.push(runTenonIO(config));
if (this.config.tenonIO && /[A-Za-z][0-9]/.test(
this.config.tenonIO.options.key)) {
audits.push(runTenonIO(this));
}
return q.all(audits).then(function() {
return outputResults();
});
return q.all(audits);
}
var testOut = {failedCount: 0, specResults: []};
var entities = new Entities();

@@ -82,12 +79,11 @@

*
* @param {Object} config The configuration file for the accessibility plugin
* @return {q.Promise} A promise which resolves to the results of any passed or
* failed tests
* @param {Object} context The plugin context object
* @return {q.Promise} A promise which resolves when the audit is finished
* @private
*/
function runTenonIO(config) {
function runTenonIO(context) {
return browser.driver.getPageSource().then(function(source) {
var options = _.assign(config.tenonIO.options, {src: source});
var options = _.assign(context.config.tenonIO.options, {src: source});

@@ -127,16 +123,8 @@ // setup response as a deferred promise

testOut.failedCount = numResults;
if (numResults === 0) {
return testOut.specResults.push({
description: testHeader + 'All tests passed!',
assertions: [{
passed: true,
errorMsg: ''
}],
duration: 1
});
context.addSuccess();
return;
}
if (config.tenonIO.printAll) {
if (context.config.tenonIO.printAll) {
console.log('\x1b[32m', testHeader + 'API response', '\x1b[39m');

@@ -148,15 +136,6 @@ console.log(response);

var ref = (result.ref === null) ? '' : result.ref;
var errorMsg = result.errorDescription + '\n\n' +
'\t\t' +entities.decode(result.errorSnippet) +
'\n\n\t\t' + ref + '\n';
testOut.specResults.push({
description: testHeader + result.errorTitle,
assertions: [{
passed: false,
errorMsg: errorMsg
}],
duration: 1
});
context.addFailure(result.errorDescription + '\n\n' +
'\t\t' +entities.decode(result.errorSnippet) +
'\n\n\t\t' + ref, {specName: testHeader + result.errorTitle});
});

@@ -169,8 +148,7 @@ }

*
* @param {Object} config The configuration file for the accessibility plugin
* @return {q.Promise} A promise which resolves to the results of any passed or
* failed tests
* @param {Object} context The plugin context object
* @return {q.Promise} A promise which resolves when the audit is finished
* @private
*/
function runChromeDevTools(config) {
function runChromeDevTools(context) {

@@ -208,2 +186,8 @@ var data = fs.readFileSync(AUDIT_FILE, 'utf-8');

};
},
function(reason){
return {
code: result.rule.code,
list: reason
};
})

@@ -224,7 +208,6 @@ );

if (result.rule.severity !== 'Warning'
|| config.chromeA11YDevTools.treatWarningsAsFailures) {
result.passed = false;
testOut.failedCount++;
|| context.config.chromeA11YDevTools.treatWarningsAsFailures) {
result.warning = false;
} else {
result.passed = true;
result.warning = true;
result.rule.heading = '\x1b[33m(WARNING) '

@@ -244,16 +227,8 @@ + result.rule.heading + ' (' + result.elementCount

result.output += '\n\n\t\t' + result.rule.url;
(result.warning ? context.addWarning : context.addFailure)(
result.output, {specName: testHeader + result.rule.heading});
}
else {
result.passed = true;
result.output = '';
context.addSuccess({specName: testHeader + result.rule.heading});
}
testOut.specResults.push({
description: testHeader + result.rule.heading,
assertions: [{
passed: result.passed,
errorMsg: result.output
}],
duration: 1
});
});

@@ -264,15 +239,3 @@ });

/**
* Output results from either plugin configuration.
*
* @return {object} testOut An object containing number of failures and spec results
* @private
*/
function outputResults() {
if ((testOut.failedCount > 0) || (testOut.specResults.length > 0)) {
return testOut;
}
}
// Export
exports.teardown = teardown;
var q = require('q');
var testOut = {
failedCount: 0, specResults: [{
description: 'Console output',
assertions: [],
duration: 0
}]
};
/**

@@ -44,15 +36,15 @@ * This plugin checks the browser log after each test for warnings and errors.

* @param {Object} errors The list of errors detected by the browser log.
* @param {boolean} failOnWarning Tests fail if a warning was detected
* @param {boolean} failOnError Tests fail if an error was detected
* @param {Object} context The plugin context object
*/
ConsolePlugin.logMessages = function(warnings, errors) {
var passed = (testOut.failedCount === 0);
ConsolePlugin.logMessages = function(warnings, errors,
failOnWarning, failOnError, context) {
warnings.map(function(warning) {
testOut.specResults[0].assertions.push(
{passed: passed, errorMsg: warning.level.name + ': ' + warning.message}
);
(failOnWarning ? context.addFailure : context.addWarning)(
warning.level.name + ': ' + warning.message);
});
errors.map(function(error) {
testOut.specResults[0].assertions.push(
{passed: passed, errorMsg: error.level.name + ': ' + error.message}
);
(failOnError ? context.addFailure : context.addWarning)(
error.level.name + ': ' + error.message);
});

@@ -79,12 +71,12 @@ };

*
* @param {Object} config The plugin's config block
* @param {Object} context The plugin context object
* @return {!webdriver.promise.Promise.<R>} A promise which resolves when the
* logs have been gathered
*/
ConsolePlugin.parseLog = function(config) {
var failOnWarning = (config.failOnWarning === undefined) ? false :
config.failOnWarning;
var failOnError = (config.failOnError === undefined) ? true :
config.failOnError;
ConsolePlugin.exclude = config.exclude || [];
ConsolePlugin.parseLog = function(context) {
var failOnWarning = (context.config.failOnWarning === undefined) ? false :
context.config.failOnWarning;
var failOnError = (context.config.failOnError === undefined) ? true :
context.config.failOnError;
ConsolePlugin.exclude = context.config.exclude || [];

@@ -103,6 +95,4 @@ return ConsolePlugin.getBrowserLog().then(function(log) {

testOut.failedCount += failOnWarning ? warnings.length : 0;
testOut.failedCount += failOnError ? errors.length : 0;
ConsolePlugin.logMessages(warnings, errors);
ConsolePlugin.logMessages(warnings, errors, failOnWarning, failOnError,
context);
});

@@ -116,10 +106,7 @@

*
* @param {Object} config The plugin's config block
* @return {!webdriver.promise.Promise.<Object>} A promise which resolves to the
* test results generated by the console logs
*/
ConsolePlugin.prototype.teardown = function(config) {
return ConsolePlugin.parseLog(config).then(function() {
return testOut;
});
ConsolePlugin.prototype.teardown = function() {
return ConsolePlugin.parseLog(this);
};

@@ -129,2 +116,2 @@

exports.teardown = consolePlugin.teardown.bind(consolePlugin);
module.exports = consolePlugin;

@@ -6,11 +6,11 @@ describe('console plugin', function() {

it('should not fail on log and debug messages', function() {
it('should fail on error message', function() {
browser.get('console/index.html');
logMessageButton.click();
deleteMessageButton.click();
});
it('should fail on error message', function() {
it('should not fail on log and debug messages', function() {
browser.get('console/index.html');
deleteMessageButton.click();
logMessageButton.click();
});
});

@@ -7,7 +7,2 @@ describe('console plugin', function() {

it('should not fail on log and debug messages', function() {
browser.get('console/index.html');
logMessageButton.click();
});
it('should fail on warning message', function() {

@@ -22,2 +17,7 @@ browser.get('console/index.html');

});
it('should not fail on log and debug messages', function() {
browser.get('console/index.html');
logMessageButton.click();
});
});

@@ -6,11 +6,11 @@ describe('console plugin', function() {

it('should not fail on log and debug messages', function() {
it('should fail on warning message', function() {
browser.get('console/index.html');
logMessageButton.click();
warningMessageButton.click();
});
it('should fail on warning message', function() {
it('should not fail on log and debug messages', function() {
browser.get('console/index.html');
warningMessageButton.click();
logMessageButton.click();
});
});

@@ -60,6 +60,6 @@ var q = require('q'),

*
* @param {Object} config The configuration file for the ngHint plugin
* @see docs/plugins.md
* @public
*/
function setup(config) {
function setup() {
// Intercept ngHint output

@@ -150,3 +150,3 @@ browser.addMockModule('protractorNgHintCaptureModule_', function() {

*
* @param {Object} config The configuration file for the ngHint plugin
* @see docs/plugins.md
* @return {q.Promise} A promise which resolves to the results of any passed or

@@ -156,3 +156,4 @@ * failed tests

*/
function teardown(config) {
function teardown() {
var self = this;
// Get logged data

@@ -171,12 +172,7 @@ return browser.executeScript_(function() {

for (url in ngHintLog) {
if (!isExcluded(url, config)) {
if (!isExcluded(url, self.config)) {
for (var i = 0; i < modulesUsed.length; i++) {
// Add new test to output
var assertions = [];
testOut.specResults.push({
description: 'Angular Hint Test: ' + modulesUsed[i] + ' (' + url +
')',
assertions: assertions,
duration: 1
});
var resultInfo = {specName: 'Angular Hint Test: ' + modulesUsed[i] +
' (' + url + ')'};
var passed = true;

@@ -188,7 +184,6 @@ // Fill in the test details

if (!isMessageToIgnore(message)) {
assertions.push({
passed: false,
errorMsg: messages[message] + ' -- ' + message,
stackTrace: ''
});
(self.config.asTests !== false ? self.addFailure :
self.addWarning)(messages[message] + ' -- ' + message,
resultInfo);
passed = false;
}

@@ -198,12 +193,4 @@ }

if (assertions.length == 0) {
// Pass
assertions.push({
passed: true,
errorMsg: '',
stackTrace: ''
});
} else {
// Fail
testOut.failedCount++;
if (passed) {
self.addSuccess(resultInfo);
}

@@ -213,16 +200,2 @@ }

}
// Return
if (config.asTests == false) {
for (var i = 0; i < testOut.specResults.length; i++) {
for (var j = 0; j < testOut.specResults[i].assertions.length; j++) {
var assertion = testOut.specResults[i].assertions[j];
if (!assertion.passed) {
console.log(assertion.errorMsg);
}
}
}
} else if ((testOut.failedCount > 0) || (testOut.specResults.length > 0)) {
return testOut;
}
});

@@ -229,0 +202,0 @@ }

@@ -206,8 +206,8 @@ var q = require('q'),

/**
* @param {Object} config The configuration file for the ngHint plugin.
* @see docs/plugins.md
*/
TimelinePlugin.prototype.setup = function(config) {
TimelinePlugin.prototype.setup = function() {
var self = this;
var deferred = q.defer();
self.outdir = path.resolve(process.cwd(), config.outdir);
self.outdir = path.resolve(process.cwd(), this.config.outdir);
var counter = 0;

@@ -260,5 +260,5 @@

/**
* @param {Object} config The configuration file for the ngHint plugin.
* @see docs/plugins.md
*/
TimelinePlugin.prototype.teardown = function(config) {
TimelinePlugin.prototype.teardown = function() {
var self = this;

@@ -286,5 +286,5 @@ var deferred = q.defer();

/**
* @param {Object} config The configuration file for the ngHint plugin.
* @see docs/plugins.md
*/
TimelinePlugin.prototype.postResults = function(config) {
TimelinePlugin.prototype.postResults = function() {
var self = this;

@@ -297,3 +297,3 @@ var deferred = q.defer();

// gets implemented, remove this hack.
if (config.sauceUser && config.sauceKey) {
if (this.config.sauceUser && this.config.sauceKey) {
// WARNING, HACK: we have a timeout to deal with the fact that there's a

@@ -303,4 +303,4 @@ // delay before Sauce Labs updates logs.

var sauceServer = new SauceLabs({
username: config.sauceUser,
password: config.sauceKey
username: this.config.sauceUser,
password: this.config.sauceKey
});

@@ -344,7 +344,3 @@

var timelinePlugin = new TimelinePlugin();
exports.setup = timelinePlugin.setup.bind(timelinePlugin);
exports.teardown = timelinePlugin.teardown.bind(timelinePlugin);
exports.postResults = timelinePlugin.postResults.bind(timelinePlugin);
exports.TimelinePlugin = TimelinePlugin;
module.exports = new TimelinePlugin();
module.exports.TimelinePlugin = TimelinePlugin;

@@ -28,2 +28,5 @@ #!/usr/bin/env node

'node lib/cli.js spec/plugins/cucumberPostTestConf.js',
'node lib/cli.js spec/plugins/browserGetSyncedConf.js',
'node lib/cli.js spec/plugins/browserGetUnsyncedConf.js',
'node lib/cli.js spec/plugins/waitForAngularConf.js',
'node lib/cli.js spec/interactionConf.js',

@@ -120,2 +123,12 @@ 'node lib/cli.js spec/directConnectConf.js',

executor.addCommandlineTest('node lib/cli.js spec/errorTest/slowHttpAndTimeoutConf.js')
.expectExitCode(1)
.expectErrors([
{message: 'The following tasks were pending[\\s\\S]*\\$http: \/slowcall'},
{message: 'The following tasks were pending[\\s\\S]*' +
'\\$timeout: function \\(\\) {[\\s\\S]*' +
'\\$scope\\.slowAngularTimeoutStatus = \'done\';[\\s\\S]' +
'*}'}
]);
// Check ngHint plugin

@@ -122,0 +135,0 @@

{
"spec_dir": "",
"spec_files": [
"spec/unit/*.js",
"website/docgen/spec/*.js"
"spec/unit/*.js"
]
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc