refactor app directory structure and add tests

This commit is contained in:
s2
2016-11-10 16:27:26 +01:00
parent 204834ce28
commit dd88218c0e
1844 changed files with 263520 additions and 0 deletions

21
tests/node_modules/nightwatch/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Andrei Rusu <andrei.rusu@beatfactor.net>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

51
tests/node_modules/nightwatch/README.md generated vendored Normal file
View File

@@ -0,0 +1,51 @@
# Nightwatch
UI automated testing framework powered by [Node.js](http://nodejs.org/). It uses the [Selenium WebDriver API](https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol).
[![Build Status](https://travis-ci.org/nightwatchjs/nightwatch.svg?branch=master)](https://travis-ci.org/nightwatchjs/nightwatch) [![NPM version](https://badge.fury.io/js/nightwatch.png)](http://badge.fury.io/js/nightwatch) [![Coverage Status](https://coveralls.io/repos/nightwatchjs/nightwatch/badge.svg?branch=master&service=github)](https://coveralls.io/github/nightwatchjs/nightwatch?branch=master)
***
#### [Homepage](http://nightwatchjs.org) | [Developer Guide](http://nightwatchjs.org/guide) | [API Reference](http://nightwatchjs.org/api) | [Changelog](https://github.com/nightwatchjs/nightwatch/releases)
### Selenium WebDriver standalone server
Nightwatch works with the Selenium standalone server so the first thing you need to do is download the selenium server jar file `selenium-server-standalone-2.x.x.jar` from the Selenium releases page:
**https://selenium-release.storage.googleapis.com/index.html**
### Install Nightwatch
Install Node.js and then:
```sh
$ git clone https://github.com/nightwatchjs/nightwatch.git
$ cd nightwatch
$ npm install
```
### Run tests
The tests for Nightwatch are written using [Mocha](http://mochajs.org/) exports interface so they can also be run with Nightwatch itself.
To run the unit tests using mocha, do:
```sh
$ npm test
```
To run the unit tests using Nightwatch, do:
```sh
$ npm run unit-tests
```
To check test coverage, run the command:
```sh
$ npm run mocha-coverage
```
and then open the generate file _coverage.html_ in your browser.
### Discuss
The [Mailing List/Google Group](https://groups.google.com/forum/#!forum/nightwatchjs) is the most appropriate tool for Nightwatch related discussions. In addition, there is a [StackOverflow Nightwatch.js tag](http://stackoverflow.com/questions/tagged/nightwatch.js) at your disposal and [Twitter](https://twitter.com/nightwatchjs).
### Setup Guides
Browser specific setup and usage guides along with debugging instructions can be found on the [**Wiki**](https://github.com/nightwatchjs/nightwatch/wiki).

5
tests/node_modules/nightwatch/bin/.npmignore generated vendored Normal file
View File

@@ -0,0 +1,5 @@
selenium-server-standalone-*
chromedriver
chromedriver-*
phantomjs
IEDriverServer_*

3
tests/node_modules/nightwatch/bin/nightwatch generated vendored Executable file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env node
require('./runner.js');

116
tests/node_modules/nightwatch/bin/nightwatch.json generated vendored Normal file
View File

@@ -0,0 +1,116 @@
{
"src_folders" : ["./examples/tests"],
"output_folder" : "./examples/reports",
"custom_commands_path" : "./examples/custom-commands",
"page_objects_path" : "./examples/pages",
"custom_assertions_path" : "",
"globals_path" : "",
"live_output" : false,
"parallel_process_delay" : 10,
"disable_colors": false,
"test_workers" : false,
"selenium" : {
"start_process" : false,
"server_path" : "",
"log_path" : "",
"host" : "127.0.0.1",
"port" : 4444,
"cli_args" : {
"webdriver.chrome.driver" : "",
"webdriver.ie.driver" : "",
"webdriver.firefox.profile" : ""
}
},
"test_settings" : {
"default" : {
"launch_url" : "http://localhost",
"selenium_host" : "127.0.0.1",
"selenium_port" : 4444,
"silent" : true,
"disable_colors": false,
"screenshots" : {
"enabled" : false,
"path" : ""
},
"desiredCapabilities" : {
"browserName" : "firefox",
"javascriptEnabled" : true,
"acceptSslCerts" : true
}
},
"saucelabs" : {
"selenium_host" : "ondemand.saucelabs.com",
"selenium_port" : 80,
"username" : "${SAUCE_USERNAME}",
"access_key" : "${SAUCE_ACCESS_KEY}",
"use_ssl" : false,
"silent" : true,
"output" : true,
"screenshots" : {
"enabled" : false,
"on_failure" : true,
"path" : ""
},
"desiredCapabilities": {
"name" : "test-example",
"browserName": "firefox"
},
"globals" : {
"myGlobal" : "some_sauce_global"
},
"selenium" : {
"start_process" : false
}
},
"phantomjs" : {
"desiredCapabilities" : {
"browserName" : "phantomjs",
"javascriptEnabled" : true,
"acceptSslCerts" : true,
"phantomjs.binary.path" : "/path/to/phantomjs"
}
},
"browserstack" : {
"selenium" : {
"start_process" : false
},
"selenium_host" : "hub.browserstack.com",
"selenium_port" : 80,
"silent" : true,
"desiredCapabilities": {
"name" : "test-example",
"browserName": "firefox",
"browserstack.user" : "...",
"browserstack.key" : "..."
}
},
"testingbot" : {
"selenium_host" : "hub.testingbot.com",
"selenium_port" : 80,
"apiKey" : "${TB_KEY}",
"apiSecret" : "${TB_SECRET}",
"silent" : true,
"output" : true,
"screenshots" : {
"enabled" : false,
"on_failure" : true,
"path" : ""
},
"desiredCapabilities": {
"name" : "test-example",
"browserName": "firefox"
},
"selenium" : {
"start_process" : false
}
}
}
}

16
tests/node_modules/nightwatch/bin/runner.js generated vendored Normal file
View File

@@ -0,0 +1,16 @@
/**
* Module dependencies
*/
var Logger = require('../lib/util/logger.js');
var Nightwatch = require('../lib/index.js');
var Utils = require('../lib/util/utils.js');
try {
Nightwatch.cli(function(argv) {
argv._source = argv['_'].slice(0);
Nightwatch.runner(argv);
});
} catch (ex) {
Utils.showStackTraceWithHeadline('There was an error while starting the test runner:\n', ex.stack + '\n', true);
process.exit(2);
}

2
tests/node_modules/nightwatch/examples/.npmignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
reports
screens

View File

@@ -0,0 +1,18 @@
/**
* Simple example of custom command. This command will
* check if there's a onbeforeunload handler in the target web page
* and return the result
*/
/* global window */
module.exports.command = function(callback) {
var self = this;
this.execute(function() {
return window && typeof window.onbeforeunload === 'function';
}, [], function(result) {
callback.call(self, result.value);
});
return this;
};

8
tests/node_modules/nightwatch/examples/globals.json generated vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"default" : {
"myGlobal" : 1
},
"test_env" : {
"myGlobal" : 2
}
}

View File

@@ -0,0 +1,55 @@
module.exports = {
// this controls whether to abort the test execution when an assertion failed and skip the rest
// it's being used in waitFor commands and expect assertions
abortOnAssertionFailure : true,
// this will overwrite the default polling interval (currently 500ms) for waitFor commands
// and expect assertions that use retry
waitForConditionPollInterval : 300,
// default timeout value in milliseconds for waitFor commands and implicit waitFor value for
// expect assertions
waitForConditionTimeout : 5000,
// this will cause waitFor commands on elements to throw an error if multiple
// elements are found using the given locate strategy and selector
throwOnMultipleElementsReturned : true,
// controls the timeout time for async hooks. Expects the done() callback to be invoked within this time
// or an error is thrown
asyncHookTimeout : 10000,
'default' : {
myGlobal : function() {
return 'I\'m a method';
}
},
'test_env' : {
myGlobal: 'test_global',
beforeEach : function() {
}
},
before : function(cb) {
cb();
},
beforeEach : function(browser, cb) {
cb();
},
after : function(cb) {
cb();
},
afterEach : function(browser, cb) {
cb();
},
reporter : function(results, cb) {
//console.log(results);
cb();
}
};

24
tests/node_modules/nightwatch/examples/mocha/github.js generated vendored Normal file
View File

@@ -0,0 +1,24 @@
describe('Github', function() {
it('Demo test GitHub', function (client) {
client
.url('https://github.com/nightwatchjs/nightwatch')
.waitForElementVisible('body', 1000)
.assert.title('nightwatchjs/nightwatch · GitHub')
.assert.visible('.container h1 strong a')
.assert.containsText('.container h1 strong a', 'nightwatch', 'Checking project title is set to nightwatch');
});
after(function(client, done) {
if (client.sessionId) {
client.end(function() {
done();
});
} else {
done();
}
});
});

View File

@@ -0,0 +1,38 @@
describe('Google demo test for Mocha', function() {
describe('for demo purposes', function() {
before(function(client, done) {
done();
});
after(function(client, done) {
if (client.sessionId) {
client.end(function() {
done();
});
} else {
done();
}
});
afterEach(function(client, done) {
done();
});
beforeEach(function(client, done) {
done();
});
it('uses BDD to run the Google simple test', function(client) {
client
.url('http://google.com')
.expect.element('body').to.be.present.before(1000);
client.setValue('input[type=text]', ['nightwatch', client.Keys.ENTER])
.pause(1000)
.assert.containsText('#main', 'Night Watch');
});
});
});

17
tests/node_modules/nightwatch/examples/pages/home.js generated vendored Normal file
View File

@@ -0,0 +1,17 @@
var searchCommands = {
submit: function() {
this.waitForElementVisible('@submitButton', 3000)
.click('@submitButton')
.api.pause(1000);
return this; // Return page object for chaining
}
};
module.exports = {
url: 'http://google.com',
commands: [searchCommands],
elements: {
searchBar: { selector: 'input[name=q]' },
submitButton: { selector: '[type=submit]' }
}
};

View File

@@ -0,0 +1,31 @@
var util = require('util');
var menuXpath = '//div[contains(@class, "hdtb-mitem")][contains(., %s)]';
var menuCommands = {
productIsSelected: function(product, callback) {
var self = this;
return this.getAttribute(product, 'class', function(result) {
var isSelected = result.value.indexOf('hdtb-msel') > -1;
callback.call(self, isSelected);
});
}
};
module.exports = {
elements: {
results: { selector: '#ires' }
},
sections: {
menu: {
selector: '#hdtb-msb',
commands: [menuCommands],
elements: {
web: { selector: util.format(menuXpath, 'Web'), locateStrategy: 'xpath' },
video: { selector: util.format(menuXpath, 'Video'), locateStrategy: 'xpath' },
images: { selector: util.format(menuXpath, 'Images'), locateStrategy: 'xpath' },
shopping: { selector: util.format(menuXpath, 'Shopping'), locateStrategy: 'xpath' }
}
}
}
};

View File

@@ -0,0 +1 @@
fbcredentials.json

53
tests/node_modules/nightwatch/examples/tests/digg.js generated vendored Normal file
View File

@@ -0,0 +1,53 @@
/**
* =============================================================================
* Demo of Facebook Connect automated login with Nightwatch.js.
* The test navigates to digg.com and tries to perform a Facebook connect login.
*
* This test requires a fbcredentials.json file to be placed in the same
* folder, containing the facebook username and password in the form below:
* -----
* {
* "username" : "",
* "password" : ""
* }
* -----
* The test only works if you have the permissions ALREADY enabled for digg.com
* in your facebook account.
* =============================================================================
*/
module.exports = {
disabled : true,
'digg facebook login' : function (client) {
var fbcredentials;
try {
fbcredentials = require('./fbcredentials.json');
} catch (err) {
console.error('Couldn\'t load the facebook credentials file. Please ensure that ' +
'you have the fbcredentials.json in the same folder as the test.');
process.exit();
}
client
.url('http://digg.com')
.waitForElementVisible('body', 1000)
.click('#nav-signin')
.click('#btn-facebook-auth-topnav')
.windowHandles(function(result) {
client.assert.equal(result.value.length, 2, 'There should be two windows open.');
var fbWindowHandle = result.value[1];
client.switchWindow(fbWindowHandle);
})
.waitForElementVisible('#facebook body', 1000)
.setValue('input#email', fbcredentials.username)
.setValue('input#pass', fbcredentials.password)
.click('#loginbutton input')
.windowHandles(function(result) {
client.assert.equal(result.value.length, 1, 'There should be only one window open.');
client.switchWindow(result.value[0]);
})
.waitForElementVisible('#digg-header.authenticated', 3000)
.end();
}
};

View File

@@ -0,0 +1,14 @@
module.exports = {
tags: ['git'],
'Demo test GitHub' : function (client) {
client
.url('https://github.com/nightwatchjs/nightwatch')
.waitForElementVisible('body', 1000)
.assert.visible('.container h1 strong a')
.assert.containsText('.container h1 strong a', 'nightwatch', 'Checking project title is set to nightwatch');
},
after : function(client) {
client.end();
}
};

29
tests/node_modules/nightwatch/examples/tests/google.js generated vendored Normal file
View File

@@ -0,0 +1,29 @@
/* jshint expr: true */
module.exports = {
tags: ['google'],
'Demo test Google' : function (client) {
client
.url('http://google.no')
.pause(1000);
client.expect.element('body').to.be.present;
client.expect.element('#lst-ib').to.have.css('display');
client.expect.element('body').to.have.attribute('class').which.contains('vasq');
client.expect.element('body').to.have.attribute('class').which.matches(/vasq$/);
client.expect.element('body').to.have.attribute('class').before(1000);
client.expect.element('#lst-ib').to.be.enabled;
client.expect.element('#hplogo').text.to.match(/Norge/).before(1000);
client.setValue('#lst-ib', 'Norway').pause(500);
client.expect.element('#lst-ib').to.have.value.equal('Norway');
client.expect.element('#lst-ib').to.be.an('input');
client.expect.element('#lst-ib').to.be.not.selected;
client.expect.element('#lst-ib').to.be.visible;
client.end();
}
};

View File

@@ -0,0 +1,22 @@
/**
* Sample automated test scenario for Nightwatch.js
*
* > it navigates to google.com and searches for nightwatch,
* verifying if the term 'The Night Watch' exists in the search results
*/
module.exports = {
'demo test google' : function (client) {
client
.url('http://google.com')
.waitForElementPresent('body', 1000);
},
'part two' : function(client) {
client
.setValue('input[type=text]', ['nightwatch', client.Keys.ENTER])
.pause(1000)
.assert.containsText('#main', 'Night Watch')
.end();
}
};

View File

@@ -0,0 +1,29 @@
/* jshint expr: true */
module.exports = {
'Demo Google search test using page objects' : function (client) {
var homePage = client.page.home();
homePage.navigate();
homePage.expect.element('@searchBar').to.be.enabled;
homePage
.setValue('@searchBar', 'Nightwatch.js')
.submit();
var resultsPage = client.page.searchResults();
resultsPage.expect.element('@results').to.be.present.after(2000);
resultsPage.expect.element('@results').to.contain.text('Nightwatch.js');
resultsPage.expect.section('@menu').to.be.visible;
var menuSection = resultsPage.section.menu;
menuSection.expect.element('@web').to.be.visible;
menuSection.expect.element('@video').to.be.visible;
menuSection.expect.element('@images').to.be.visible;
menuSection.expect.element('@shopping').to.be.visible;
menuSection.productIsSelected('@web', function(result) {
this.assert.ok(result, 'Web results are shown by default on search results page');
});
client.end();
}
};

View File

@@ -0,0 +1,18 @@
module.exports = {
disabled : true,
'Demo test NightwatchJS.org' : function (client) {
client
.url('http://nightwatchjs.org')
.waitForElementVisible('body', 1000)
.elements('css selector', '#index-container ul.features li', function (result) {
for (var i = 0; i < result.value.length; i++) {
var element = result.value[i];
var selector = '#index-container ul.features li:nth-child(' + (element.ELEMENT ) +') em';
client.verify.cssClassPresent(selector, 'glyphicon');
}
})
.end();
}
};

View File

@@ -0,0 +1,39 @@
/**
* Sample automated test scenario for Nightwatch.js
*
* > it navigates to page that has onbeforeunload handler
*/
module.exports = {
'go to page with unload handler': function(client) {
client
.url('http://www.4guysfromrolla.com/demos/OnBeforeUnloadDemo1.htm')
.waitForElementVisible('body', 1000);
},
'navigate away from page WITH unload handler': function(client) {
var hasDialog = false;
client
.hasOnBeforeUnload(function(result) {
this.verify.equal(result, true, 'The page should have an onbeforeunload handler');
hasDialog = result;
})
.url('http://google.com', function() {
if (hasDialog) {
this.acceptAlert();
}
})
.waitForElementVisible('body', 1000);
},
'go to nightwatch' : function(c) {
c.url('http://nightwatchjs.org')
.waitForElementVisible('body', 1000);
},
after : function(c) {
c.end();
}
};

View File

@@ -0,0 +1,14 @@
module.exports = {
tags: ['git'],
'Demo test GitHub' : function (client) {
client
.url('https://github.com/nightwatchjs/nightwatch')
.waitForElementVisible('body', 1000)
.assert.visible('.container h1 strong a')
.assert.containsText('.container h1 strong a', 'nightwatch', 'Checking project title is set to nightwatch');
},
after : function(client) {
client.end();
}
};

View File

@@ -0,0 +1,9 @@
module.exports = {
'demo UnitTest' : function (client, done) {
client.assert.ok('TEST');
setTimeout(function() {
done();
}, 500);
}
};

View File

@@ -0,0 +1,41 @@
var Utils = require('../../lib/util/utils.js');
module.exports = {
testFormatElapsedTime : function(client) {
var test = client.assert;
var resultMs = Utils.formatElapsedTime(999);
test.equal(resultMs, '999ms');
var resultSec = Utils.formatElapsedTime(1999);
test.equal(resultSec, '1.999s');
var resultMin = Utils.formatElapsedTime(122299, true);
test.equal(resultMin, '2m 2s / 122299ms');
},
testMakeFnAsync : function(client) {
function asynFn(done) {
done();
}
function syncFn() {}
var test = client.assert;
test.equal(Utils.makeFnAsync(1, asynFn), asynFn);
var convertedFn = Utils.makeFnAsync(1, syncFn);
convertedFn(function() {
test.ok('converted fn called');
});
},
testGetTestSuiteName : function(client) {
var test = client.assert;
test.equal(Utils.getTestSuiteName('test-case-one'), 'Test Case One');
test.equal(Utils.getTestSuiteName('test_case_two'), 'Test Case Two');
test.equal(Utils.getTestSuiteName('test.case.one'), 'Test Case One');
test.equal(Utils.getTestSuiteName('testCaseOne'), 'Test Case One');
}
};

View File

@@ -0,0 +1,29 @@
var Utils = require('../../lib/util/utils.js');
var expect = require('chai').expect;
module.exports = {
testFormatElapsedTime : function(client) {
var resultMs = Utils.formatElapsedTime(999);
var resultSec = Utils.formatElapsedTime(1999);
var resultMin = Utils.formatElapsedTime(122299, true);
expect(resultMs).to.equal('999ms');
expect(resultSec).to.equal('1.999s');
expect(resultMin).to.equal('2m 2s / 122299ms');
},
testFormatElapsedTimeMore : function(client) {
var resultMs = Utils.formatElapsedTime(999);
expect(resultMs).to.equal('999ms');
},
testMakeFnAsync : function(client) {
function asynFn(done) {
done();
}
function syncFn() {}
expect(Utils.makeFnAsync(1, asynFn)).to.equal(asynFn);
}
};

3
tests/node_modules/nightwatch/index.js generated vendored Normal file
View File

@@ -0,0 +1,3 @@
module.exports = process.env.NIGHTWATCH_COV ?
require('./lib-cov/index') :
require('./lib/index');

View File

@@ -0,0 +1,57 @@
/**
* Checks if the given attribute of an element contains the expected value.
*
* ```
* this.demoTest = function (client) {
* browser.assert.attributeContains('#someElement', 'href', 'google.com');
* };
* ```
*
* @method attributeContains
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {string} attribute The attribute name
* @param {string} expected The expected contained value of the attribute to check.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(selector, attribute, expected, msg) {
var DEFAULT_MSG = 'Testing if attribute %s of <%s> contains "%s".';
var MSG_ELEMENT_NOT_FOUND = DEFAULT_MSG + ' ' + 'Element could not be located.';
var MSG_ATTR_NOT_FOUND = DEFAULT_MSG + ' ' + 'Element does not have a ' + attribute + ' attribute.';
this.message = msg || util.format(DEFAULT_MSG, attribute, selector, expected);
this.expected = function() {
return expected;
};
this.pass = function(value) {
return value.indexOf(expected) > -1;
};
this.failure = function(result) {
var failed = (result === false) ||
// no such element
result && (result.status === -1 || result.value === null);
if (failed) {
var defaultMsg = MSG_ELEMENT_NOT_FOUND;
if (result && result.value === null) {
defaultMsg = MSG_ATTR_NOT_FOUND;
}
this.message = msg || util.format(defaultMsg, attribute, selector, expected);
}
return failed;
};
this.value = function(result) {
return result.value;
};
this.command = function(callback) {
return this.api.getAttribute(selector, attribute, callback);
};
};

View File

@@ -0,0 +1,57 @@
/**
* Checks if the given attribute of an element has the expected value.
*
* ```
* this.demoTest = function (client) {
* browser.assert.attributeEquals('body', 'data-attr', 'some value');
* };
* ```
*
* @method attributeEquals
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {string} attribute The attribute name
* @param {string} expected The expected value of the attribute to check.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(selector, attribute, expected, msg) {
var DEFAULT_MSG = 'Testing if attribute %s of <%s> equals "%s".';
var MSG_ELEMENT_NOT_FOUND = DEFAULT_MSG + ' ' + 'Element could not be located.';
var MSG_ATTR_NOT_FOUND = DEFAULT_MSG + ' ' + 'Element does not have a ' + attribute + ' attribute.';
this.message = msg || util.format(DEFAULT_MSG, attribute, selector, expected);
this.expected = function() {
return expected;
};
this.pass = function(value) {
return value === expected;
};
this.failure = function(result) {
var failed = (result === false) ||
// no such element
result && (result.status === -1 || result.value === null);
if (failed) {
var defaultMsg = MSG_ELEMENT_NOT_FOUND;
if (result && result.value === null) {
defaultMsg = MSG_ATTR_NOT_FOUND;
}
this.message = msg || util.format(defaultMsg, attribute, selector, expected);
}
return failed;
};
this.value = function(result) {
return result.value;
};
this.command = function(callback) {
return this.api.getAttribute(selector, attribute, callback);
};
};

View File

@@ -0,0 +1,49 @@
/**
* Checks if the given element contains the specified text.
*
* ```
* this.demoTest = function (client) {
* browser.assert.containsText('#main', 'The Night Watch');
* };
* ```
*
* @method containsText
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {string} expectedText The text to look for.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(selector, expectedText, msg) {
var MSG_ELEMENT_NOT_FOUND = 'Testing if element <%s> contains text: "%s". ' +
'Element could not be located.';
this.message = msg || util.format('Testing if element <%s> contains text: "%s".', selector, expectedText);
this.expected = function() {
return expectedText;
};
this.pass = function(value) {
return value.indexOf(expectedText) > -1;
};
this.failure = function(result) {
var failed = result === false || result && result.status === -1;
if (failed) {
this.message = msg || util.format(MSG_ELEMENT_NOT_FOUND, selector, expectedText);
}
return failed;
};
this.value = function(result) {
return result.value;
};
this.command = function(callback) {
return this.api.getText(selector, callback);
};
};

View File

@@ -0,0 +1,50 @@
/**
* Checks if the given element does not have the specified CSS class.
*
* ```
* this.demoTest = function (client) {
* browser.assert.cssClassNotPresent('#main', 'container');
* };
* ```
*
* @method cssClassNotPresent
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {string} className The CSS class to look for.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(selector, className, msg) {
var MSG_ELEMENT_NOT_FOUND = 'Testing if element <%s> does not have css class: "%s". ' +
'Element could not be located.';
this.message = msg || util.format('Testing if element <%s> does not have css class: "%s".', selector, className);
this.expected = function() {
return 'without ' + className;
};
this.pass = function(value) {
var classes = value.split(' ');
return classes.indexOf(className) === -1;
};
this.failure = function(result) {
var failed = result === false || result && result.status === -1;
if (failed) {
this.message = msg || util.format(MSG_ELEMENT_NOT_FOUND, selector, className);
}
return failed;
};
this.value = function(result) {
return result.value;
};
this.command = function(callback) {
return this.api.getAttribute(selector, 'class', callback);
};
};

View File

@@ -0,0 +1,50 @@
/**
* Checks if the given element has the specified CSS class.
*
* ```
* this.demoTest = function (client) {
* browser.assert.cssClassPresent('#main', 'container');
* };
* ```
*
* @method cssClassPresent
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {string} className The CSS class to look for.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(selector, className, msg) {
var MSG_ELEMENT_NOT_FOUND = 'Testing if element <%s> has css class: "%s". ' +
'Element could not be located.';
this.message = msg || util.format('Testing if element <%s> has css class: "%s".', selector, className);
this.expected = function() {
return 'has ' + className;
};
this.pass = function(value) {
var classes = value.split(' ');
return classes.indexOf(className) > -1;
};
this.failure = function(result) {
var failed = result === false || result && result.status === -1;
if (failed) {
this.message = msg || util.format(MSG_ELEMENT_NOT_FOUND, selector, className);
}
return failed;
};
this.value = function(result) {
return result.value;
};
this.command = function(callback) {
return this.api.getAttribute(selector, 'class', callback);
};
};

View File

@@ -0,0 +1,47 @@
/**
* Checks if the specified css property of a given element has the expected value.
*
* ```
* this.demoTest = function (client) {
* browser.assert.cssProperty('#main', 'display', 'block');
* };
* ```
*
* @method cssProperty
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {string} cssProperty The CSS property.
* @param {string} expected The expected value of the css property to check.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(selector, cssProperty, expected, msg) {
var MSG_ELEMENT_NOT_FOUND = 'Testing if element <%s> has css property %s. ' +
'Element or attribute could not be located.';
this.message = msg || util.format('Testing if element <%s> has css property "%s: %s".', selector, cssProperty, expected);
this.expected = expected;
this.pass = function(value) {
return value === expected;
};
this.failure = function(result) {
var failed = result === false || result && result.status === -1;
if (failed) {
this.message = msg || util.format(MSG_ELEMENT_NOT_FOUND, selector, cssProperty);
}
return failed;
};
this.value = function(result) {
return result.value;
};
this.command = function(callback) {
return this.api.getCssProperty(selector, cssProperty, callback);
};
};

View File

@@ -0,0 +1,34 @@
/**
* Checks if the given element exists in the DOM.
*
* ```
* this.demoTest = function (client) {
* browser.assert.elementNotPresent(".should_not_exist");
* };
* ```
*
* @method elementNotPresent
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(selector, msg) {
this.message = msg || util.format('Testing if element <%s> is not present.', selector);
this.expected = 'not present';
this.pass = function(value) {
return value === 'not present';
};
this.value = function(result) {
return (result.status !== 0 || result.value.length === 0) ? 'not present' : 'present';
};
this.command = function(callback) {
return this.api.elements(this.client.locateStrategy, selector, callback);
};
};

View File

@@ -0,0 +1,34 @@
/**
* Checks if the given element exists in the DOM.
*
* ```
* this.demoTest = function (client) {
* browser.assert.elementPresent("#main");
* };
* ```
*
* @method elementPresent
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(selector, msg) {
this.message = msg || util.format('Testing if element <%s> is present.', selector);
this.expected = 'present';
this.pass = function(value) {
return value == 'present';
};
this.value = function(result) {
return (result.status !== 0 || result.value.length === 0) ? 'not present' : 'present';
};
this.command = function(callback) {
return this.api.elements(this.client.locateStrategy, selector, callback);
};
};

View File

@@ -0,0 +1,45 @@
/**
* Checks if the given element is not visible on the page.
*
* ```
* this.demoTest = function (client) {
* browser.assert.hidden(".should_not_be_visible");
* };
* ```
*
* @method hidden
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(selector, msg) {
var MSG_ELEMENT_NOT_FOUND = 'Testing if element <%s> is hidden. ' +
'Element could not be located.';
this.message = msg || util.format('Testing if element <%s> is hidden.', selector);
this.expected = true;
this.pass = function(value) {
return value === this.expected;
};
this.failure = function(result) {
var failed = result === false || result && result.status === -1;
if (failed) {
this.message = msg || util.format(MSG_ELEMENT_NOT_FOUND, selector);
}
return failed;
};
this.value = function(result) {
return !result.value;
};
this.command = function(callback) {
return this.api.isVisible(selector, callback);
};
};

View File

@@ -0,0 +1,35 @@
/**
* Checks if the page title equals the given value.
*
* ```
* this.demoTest = function (client) {
* browser.assert.title("Nightwatch.js");
* };
* ```
*
* @method title
* @param {string} expected The expected page title.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(expected, msg) {
this.message = msg || util.format('Testing if the page title equals "%s".', expected);
this.expected = expected;
this.pass = function(value) {
return value === this.expected;
};
this.value = function(result) {
return result.value;
};
this.command = function(callback) {
this.api.title(callback);
return this;
};
};

View File

@@ -0,0 +1,35 @@
/**
* Checks if the current URL contains the given value.
*
* ```
* this.demoTest = function (client) {
* browser.assert.urlContains('google');
* };
* ```
*
* @method urlContains
* @param {string} expected The value expected to exist within the current URL.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(expected, msg) {
this.message = msg || util.format('Testing if the URL contains "%s".', expected);
this.expected = expected;
this.pass = function(value) {
return value.indexOf(this.expected) > -1;
};
this.value = function(result) {
return result.value;
};
this.command = function(callback) {
this.api.url(callback);
return this;
};
};

View File

@@ -0,0 +1,35 @@
/**
* Checks if the current url equals the given value.
*
* ```
* this.demoTest = function (client) {
* browser.assert.urlEquals('http://www.google.com');
* };
* ```
*
* @method urlEquals
* @param {string} expected The expected url.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(expected, msg) {
this.message = msg || util.format('Testing if the URL equals "%s".', expected);
this.expected = expected;
this.pass = function(value) {
return value === this.expected;
};
this.value = function(result) {
return result.value;
};
this.command = function(callback) {
this.api.url(callback);
return this;
};
};

View File

@@ -0,0 +1,50 @@
/**
* Checks if the given form element's value equals the expected value.
*
* ```
* this.demoTest = function (client) {
* browser.assert.value("form.login input[type=text]", "username");
* };
* ```
*
* @method value
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {string} expectedText The expected text.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(selector, expected, msg) {
var MSG_ELEMENT_NOT_FOUND = 'Testing if value of <%s> equals: "%s". ' +
'Element or attribute could not be located.';
this.message = msg || util.format('Testing if value of <%s> equals: "%s".', selector, expected);
this.expected = expected;
this.pass = function(value) {
return value === this.expected;
};
this.failure = function(result) {
var failed = (result === false) ||
// no such element
result && result.status === -1 ||
// element doesn't have a value attribute
result && result.value === null;
if (failed) {
this.message = msg || util.format(MSG_ELEMENT_NOT_FOUND, selector, expected);
}
return failed;
};
this.value = function(result) {
return result.value;
};
this.command = function(callback) {
return this.api.getValue(selector, callback);
};
};

View File

@@ -0,0 +1,53 @@
/**
* Checks if the given form element's value contains the expected value.
*
* ```
* this.demoTest = function (client) {
* browser.assert.valueContains("form.login input[type=text]", "username");
* };
* ```
*
* @method valueContains
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {string} expectedText The expected text.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(selector, expected, msg) {
var DEFAULT_MSG = 'Testing if value of <%s> contains: "%s".';
var MSG_ELEMENT_NOT_FOUND = DEFAULT_MSG + ' ' + 'Element could not be located.';
var VALUE_ATTR_NOT_FOUND = DEFAULT_MSG + ' ' + 'Element does not have a value attribute.';
this.message = msg || util.format(DEFAULT_MSG, selector, expected);
this.expected = true;
this.pass = function(value) {
return value.indexOf(expected) > -1;
};
this.failure = function(result) {
var failed = (result === false) ||
// no such element
result && (result.status === -1 || result.value === null);
if (failed) {
var defaultMsg = MSG_ELEMENT_NOT_FOUND;
if (result && result.value === null) {
defaultMsg = VALUE_ATTR_NOT_FOUND;
}
this.message = msg || util.format(defaultMsg, selector, expected);
}
return failed;
};
this.value = function(result) {
return result.value;
};
this.command = function(callback) {
return this.api.getValue(selector, callback);
};
};

View File

@@ -0,0 +1,45 @@
/**
* Checks if the given element is visible on the page.
*
* ```
* this.demoTest = function (client) {
* browser.assert.visible(".should_be_visible");
* };
* ```
*
* @method visible
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api assertions
*/
var util = require('util');
exports.assertion = function(selector, msg) {
var MSG_ELEMENT_NOT_FOUND = 'Testing if element <%s> is visible. ' +
'Element could not be located.';
this.message = msg || util.format('Testing if element <%s> is visible.', selector);
this.expected = true;
this.pass = function(value) {
return value === this.expected;
};
this.failure = function(result) {
var failed = result === false || result && result.status === -1;
if (failed) {
this.message = msg || util.format(MSG_ELEMENT_NOT_FOUND, selector);
}
return failed;
};
this.value = function(result) {
return result.value;
};
this.command = function(callback) {
return this.api.isVisible(selector, callback);
};
};

View File

@@ -0,0 +1,509 @@
var events = require('events');
module.exports = function(client) {
return {
/**
* Change focus to another window. The window to change focus to may be specified by its server assigned window handle, or by the value of its name attribute.
*
* To find out the window handle use `window_handles` protocol action
*
* ```
* this.demoTest = function (browser) {
* browser.window_handles(function(result) {
* var handle = result.value[0];
* browser.switchWindow(handle);
* });
* };
* ```
*
* @method switchWindow
* @param {string} handleOrName The server assigned window handle or the name attribute.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see window
* @since v0.3.0
* @api commands
*/
switchWindow : function(handleOrName, callback) {
var self = this;
this.window('POST', handleOrName, function(result) {
if (typeof callback === 'function') {
callback.call(self, result);
}
});
return this;
},
/**
* Resizes the current window.
*
* ```
* this.demoTest = function (browser) {
* browser.resizeWindow(1000, 800);
* };
* ```
*
* @method resizeWindow
* @param {number} width The new window width.
* @param {number} height The new window height.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see windowSize
* @since v0.3.0
* @api commands
*/
resizeWindow : function(width, height, callback) {
var self = this;
return this.windowSize('current', width, height, function(result) {
if (typeof callback === 'function') {
callback.call(self, result);
}
});
},
/**
* Sets the current window position.
*
* ```
* this.demoTest = function (browser) {
* browser.setWindowPosition(0, 0);
* };
* ```
*
* @method setWindowPosition
* @param {number} offsetX The new window offset x-position.
* @param {number} offsetY The new window offset y-position.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see windowPosition
* @since v0.8.18
* @api commands
*/
setWindowPosition : function(offsetX, offsetY, callback) {
var self = this;
return this.windowPosition('current', offsetX, offsetY, function(result) {
if (typeof callback === 'function') {
callback.call(self, result);
}
});
},
/**
* Maximizes the current window.
*
* ```
* this.demoTest = function (browser) {
* browser.maximizeWindow();
* };
* ```
*
* @method maximizeWindow
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see windowMaximize
* @since v0.5.13
* @api commands
*/
maximizeWindow: function(callback) {
var self = this;
return this.windowMaximize('current', function(result) {
if (typeof callback === 'function') {
callback.call(self, result);
}
});
},
/**
* Take a screenshot of the current page and saves it as the given filename.
*
* ```
* this.demoTest = function (browser) {
* browser.saveScreenshot('/path/to/fileName.png');
* };
* ```
*
* @method saveScreenshot
* @param {string} fileName The complete path to the file name where the screenshot should be saved.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see screenshot
* @api commands
*/
saveScreenshot : function(fileName, callback) {
var self = this;
return this.screenshot(client.api.options.log_screenshot_data, function(result) {
client.saveScreenshotToFile(fileName, result.value, function(err) {
if (typeof callback === 'function') {
callback.call(self, result, err);
}
});
});
},
/**
* Returns the title of the current page. Uses title protocol command.
*
* ```
* this.demoTest = function (browser) {
* browser.getTitle(function(title) {
* this.assert.equal(typeof title, 'string');
* this.assert.equal(title, 'Nightwatch.js');
* });
* };
* ```
*
* @method getTitle
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see title
* @returns {string} The page title.
* @api commands
*/
getTitle : function(callback) {
var self = this;
return this.title(function(result) {
callback.call(self, result.value);
});
},
/**
* Close the current window. This can be useful when you're working with multiple windows open (e.g. an OAuth login).
* Uses `window` protocol command.
*
* ```
* this.demoTest = function (client) {
* client.closeWindow();
* };
* ```
*
* @method closeWindow
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see window
* @since v0.3.0
* @api commands
*/
closeWindow : function(callback) {
var self = this;
return this.window('DELETE', function(result) {
if (typeof callback === 'function') {
callback.call(self, result);
}
});
},
/**
* This command is an alias to url and also a convenience method when called without any arguments in the sense that it performs a call to .url() with passing the value of `launch_url` field from the settings file.
* Uses `url` protocol command.
*
* ```
* this.demoTest = function (client) {
* client.init();
* };
* ```
*
* @method init
* @param {string} [url] Url to navigate to.
* @see url
* @since v0.4.0
* @api commands
*/
init : function(url) {
if (url) {
return this.url(url);
}
if (!this.launchUrl) {
return this;
}
return this.url(this.launchUrl);
},
/**
* Convenience method that adds the specified hash (i.e. url fragment) to the current value of the `launch_url` as set in `nightwatch.json`.
*
* ```
* this.demoTest = function (client) {
* client.urlHash('#hashvalue');
* // or
* client.urlHash('hashvalue');
* };
* ```
*
* @method urlHash
* @param {string} hash The hash to add/replace to the current url (i.e. the value set in the launch_url property in nightwatch.json).
* @see url
* @since v0.4.0
* @api commands
*/
urlHash : function(hash) {
if (hash.charAt(0) === '#') {
hash = hash.substring(1);
}
return this.url(this.launchUrl + '#' + hash);
},
/**
* Retrieve all cookies visible to the current page. The cookies are returned as an array of cookie JSON object, as defined [here](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
*
* Uses `cookie` protocol command.
*
* ```
* this.demoTest = function(browser) {
* browser.getCookies(function callback(result) {
* this.assert.equal(result.value.length, 1);
* this.assert.equals(result.value[0].name, 'test_cookie');
* });
* }
* ```
*
* @method getCookies
* @param {function} callback The callback function which will receive the response as an argument.
* @api commands
* @since v0.4.0
* @see cookie
* @returns {Array.<object>} A list of cookies.
*/
getCookies : function(callback) {
return this.cookie('GET', callback);
},
/**
* Retrieve a single cookie visible to the current page. The cookie is returned as a cookie JSON object, as defined [here](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
*
* Uses `cookie` protocol command.
*
* ```
* this.demoTest = function(browser) {
* browser.getCookie(name, function callback(result) {
* this.assert.equal(result.value, '123456');
* this.assert.equals(result.name, 'test_cookie');
* });
* }
* ```
*
* @method getCookie
* @param {string} name The cookie name.
* @param {function} callback The callback function which will receive the response as an argument.
* @api commands
* @since v0.4.0
* @see cookie
* @returns {object|null} The cookie object as a selenium cookie JSON object or null if the cookie wasn't found.
*/
getCookie : function(name, callback) {
var self = this;
return this.cookie('GET', function(result) {
if (!result.value || result.value.length === 0) {
callback.call(self, null);
return;
}
var cookie = null;
for (var i = 0; i < result.value.length; i++) {
if (result.value[i].name === name) {
cookie = result.value[i];
break;
}
}
callback.call(self, cookie);
});
},
/**
* Set a cookie, specified as a cookie JSON object, as defined [here](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
*
* Uses `cookie` protocol command.
*
* ```
* this.demoTest = function(browser) {
* browser.setCookie({
* name : "test_cookie",
* value : "test_value",
* path : "/", (Optional)
* domain : "example.org", (Optional)
* secure : false, (Optional)
* httpOnly : false, // (Optional)
* expiry : 1395002765 // (Optional) time in seconds since midnight, January 1, 1970 UTC
* });
* }
* ```
*
* @method setCookie
* @param {object} cookie The cookie object.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @api commands
* @since v0.4.0
* @see cookie
*/
setCookie : function(cookie, callback) {
return this.cookie('POST', cookie, callback);
},
/**
* Delete the cookie with the given name. This command is a no-op if there is no such cookie visible to the current page.
*
* ```
* this.demoTest = function(browser) {
* browser.deleteCookie("test_cookie", function() {
* // do something more in here
* });
* }
* ```
*
* @method deleteCookie
* @param cookieName The name of the cookie to delete.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @api commands
* @since v0.4.0
* @see cookie
*/
deleteCookie : function(cookieName, callback) {
return this.cookie('DELETE', cookieName, callback);
},
/**
* Delete all cookies visible to the current page.
*
* ```
* this.demoTest = function(browser) {
* browser.deleteCookies(function() {
* // do something more in here
* });
* }
* ```
*
* @method deleteCookies
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @api commands
* @since v0.4.0
* @see cookie
*/
deleteCookies : function(callback) {
return this.cookie('DELETE', callback);
},
/**
* Utility command to load an external script into the page specified by url.
*
* ```
* this.demoTest = function(client) {
* this.injectScript('http://example.org/js/utility.js', function() {
* // we're all done here.
* });
* };
* ```
*
* @method injectScript
* @param {string} scriptUrl The script file url
* @param {string} [id] Dom element id to be set on the script tag.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @api commands
* @since v0.4.0
* @returns {HTMLScriptElement} The newly created script tag.
*/
injectScript : function(scriptUrl, id, callback) {
var args = [scriptUrl];
if (arguments.length == 2 && typeof arguments[1] == 'function') {
callback = arguments[1];
} else if (typeof id == 'string') {
args.push(id);
}
return this.execute(function(u,i) {/* jshint browser:true */return (function(d){var e=d.createElement('script');var m=d.getElementsByTagName('head')[0];e.src=u;if(i){e.id=i;}m.appendChild(e);return e;})(document);}, args, callback);
},
/**
* Gets the available log types
*
* ```
* this.demoTest = function(client) {
* this.getLogTypes(function(typesArray) {
*
* });
* };
* ```
*
* @method getLogTypes
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @api commands
* @see logTypes
*/
getLogTypes: function(callback) {
var self = this;
return this.sessionLogTypes(function(response) {
if (callback) {
callback.call(self, response.value);
}
});
},
/**
* Gets a log from selenium
*
* ```
* this.demoTest = function(client) {
* this.getLog('browser', function(logEntriesArray) {
* console.log('Log length: ' + logEntriesArray.length);
* logEntriesArray.forEach(function(log) {
* console.log('[' + log.level + '] ' + log.timestamp + ' : ' + log.message);
* });
* });
* };
* ```
*
* @method getLog
* @param {string|function} typeString Log type to request
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @api commands
* @see log
*/
getLog: function(typeString, callback) {
var self = this;
if (typeof typeString !== 'string') {
if (typeof typeString === 'function') {
callback = typeString;
}
typeString = 'browser';
}
return this.sessionLog(typeString, function (response) {
if (callback) {
callback.call(self, response.value);
}
});
},
/**
* Utility command to test if the log type is available
*
* ```
* this.demoTest = function(browser) {
* browser.isLogAvailable('browser', function(isAvailable) {
* // do something more in here
* });
* }
* ```
*
* @method isLogAvailable
* @param {string|function} typeString Type of log to test
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @api commands
* @see getLogTypes
*/
isLogAvailable: function(typeString, callback) {
var self = this;
if (typeof typeString !== 'string') {
if (typeof typeString === 'function') {
callback = typeString;
}
typeString = 'browser';
}
return this.getLogTypes(function (types) {
var isAvailable;
try {
isAvailable = Array.isArray(types) && types.indexOf(typeString) >= 0;
} catch (err) {
isAvailable = false;
}
if (callback) {
callback.call(self, isAvailable);
}
});
}
};
};

View File

@@ -0,0 +1,24 @@
var util = require('util');
var events = require('events');
function Command() {
events.EventEmitter.call(this);
}
util.inherits(Command, events.EventEmitter);
Command.prototype.command = function(callback) {
var self = this;
this.client.locateStrategy = this.strategy;
process.nextTick(function() {
if (typeof callback == 'function') {
callback.call(self.client.api);
}
self.emit('complete');
});
return this;
};
module.exports = Command;

View File

@@ -0,0 +1,70 @@
var util = require('util');
var events = require('events');
var Utils = require('../../util/utils.js');
var Logger = require('../../util/logger.js');
/**
* Ends the session. Uses session protocol command.
*
* ```
* this.demoTest = function (browser) {
* browser.end();
* };
* ```
*
* @method end
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see session
* @api commands
*/
function End() {
events.EventEmitter.call(this);
}
util.inherits(End, events.EventEmitter);
End.prototype.command = function(callback) {
var self = this;
var client = this.client;
if (client.sessionId) {
if (this.testFailuresExist() && this.shouldTakeScreenshot()) {
var fileNamePath = Utils.getScreenshotFileName(client.api.currentTest, false, client.options.screenshots.path);
Logger.info('We have failures in "' + client.api.currentTest.name + '". Taking screenshot...');
client.api.saveScreenshot(fileNamePath, function(result, err) {
if (err || result.status !== 0) {
Logger.warn('Error saving screenshot...', err || result);
}
});
}
client.api.session('delete', function(result) {
client.sessionId = client.api.sessionId = null;
self.complete(callback, result);
});
} else {
setImmediate(function() {
self.complete(callback, null);
});
}
return this.client.api;
};
End.prototype.testFailuresExist = function() {
return this.client.results.errors > 0 || this.client.results.failed > 0;
};
End.prototype.shouldTakeScreenshot = function() {
return this.client.options.screenshots.enabled && this.client.options.screenshots.on_failure;
};
End.prototype.complete = function(callback, result) {
if (typeof callback === 'function') {
callback.call(this, result);
}
this.emit('complete');
};
module.exports = End;

View File

@@ -0,0 +1,46 @@
var util = require('util');
var events = require('events');
/**
* Suspends the test for the given time in milliseconds. If the milliseconds argument is missing it will suspend the test indefinitely
*
* ```
* this.demoTest = function (browser) {
* browser.pause(1000);
* // or suspend indefinitely
* browser.pause();
* };
* ```
*
* @method pause
* @param {number} ms The number of milliseconds to wait.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @api commands
*/
function Pause() {
events.EventEmitter.call(this);
}
util.inherits(Pause, events.EventEmitter);
Pause.prototype.command = function(ms, cb) {
var self = this;
// If we don't pass the milliseconds, the client will
// be suspended indefinitely
if (!ms) {
return this;
}
setTimeout(function() {
// if we have a callback, call it right before the complete event
if (cb) {
cb.call(self.client.api);
}
self.emit('complete');
}, ms);
return this;
};
module.exports = Pause;

View File

@@ -0,0 +1,81 @@
/**
* A simple perform command which allows access to the "api" in a callback.
* Can be useful if you want to read variables set by other commands.
*
* ```
* this.demoTest = function (browser) {
* var elementValue;
* browser
* .getValue('.some-element', function(result) {
* elementValue = result.value;
* })
* // other stuff going on ...
*
* // self-completing callback
* .perform(function() {
* console.log('elementValue', elementValue);
* // without any defined parameters, perform
* // completes immediately (synchronously)
* })
*
* // asynchronous completion
* .perform(function(done) {
* console.log('elementValue', elementValue);
* // potentially other async stuff going on
* // on finished, call the done callback
* done();
* })
*
* // asynchronous completion including api (client)
* .perform(function(client, done) {
* console.log('elementValue', elementValue);
* // similar to before, but now with client
* // potentially other async stuff going on
* // on finished, call the done callback
* done();
* });
* };
* ```
*
* @method perform
* @param {function} callback The function to run as part of the queue. Its signature can have up to two parameters. No parameters: callback runs and perform completes immediately at the end of the execution of the callback. One parameter: allows for asynchronous execution within the callback providing a done callback function for completion as the first argument. Two parameters: allows for asynchronous execution with the "api" object passed in as the first argument, followed by the done callback.
* @api commands
*/
var util = require('util');
var events = require('events');
function Perform() {
events.EventEmitter.call(this);
}
util.inherits(Perform, events.EventEmitter);
Perform.prototype.command = function(callback) {
var self = this;
var doneCallback;
if (callback.length === 0) {
callback.call(self, self.client.api);
doneCallback = function() {
self.emit('complete');
};
} else {
doneCallback = function() {
var args = [function() {
self.emit('complete');
}];
if (callback.length > 1) {
args.unshift(self.client.api);
}
callback.apply(self, args);
};
}
process.nextTick(doneCallback);
return this;
};
module.exports = Perform;

View File

@@ -0,0 +1,27 @@
var util = require('util');
var locateStrategy = require('./_locateStrategy.js');
/**
* Sets the locate strategy for selectors to `css selector`, therefore every following selector needs to be specified as css.
*
* ```
* this.demoTest = function (browser) {
* browser
* .useCss() // we're back to CSS now
* .setValue('input[type=text]', 'nightwatch');
* };
* ```
*
* @method useCss
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @api commands
*/
function Command() {
this.strategy = 'css selector';
locateStrategy.call(this);
}
util.inherits(Command, locateStrategy);
module.exports = Command;

View File

@@ -0,0 +1,20 @@
var util = require('util');
var locateStrategy = require('./_locateStrategy.js');
/**
* Sets the locate strategy for selectors to `recursion`, therefore every following selector needs to be an array of element objects
* This is used internally for sections of page objects which require element nesting
*
* @method useRecursion
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @api commands
*/
function Command() {
this.strategy = 'recursion';
locateStrategy.call(this);
}
util.inherits(Command, locateStrategy);
module.exports = Command;

View File

@@ -0,0 +1,27 @@
var util = require('util');
var locateStrategy = require('./_locateStrategy.js');
/**
* Sets the locate strategy for selectors to xpath, therefore every following selector needs to be specified as xpath.
*
* ```
* this.demoTest = function (browser) {
* browser
* .useXpath() // every selector now must be xpath
* .click("//tr[@data-recordid]/span[text()='Search Text']");
* };
* ```
*
* @method useXpath
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @api commands
*/
function Command() {
this.strategy = 'xpath';
locateStrategy.call(this);
}
util.inherits(Command, locateStrategy);
module.exports = Command;

View File

@@ -0,0 +1,388 @@
var util = require('util');
var events = require('events');
var Logger = require('../util/logger.js');
var Utils = require('../util/utils.js');
module.exports = function(client) {
var Protocol = require('./protocol.js')(client);
var returnValue = {};
var elementCommands = {};
/**
* Simulates a click event on the given DOM element. Uses `elementIdClick` protocol command.
*
* ```
* this.demoTest = function (client) {
* client.click("#main ul li a.first");
* };
* ```
*
* @method click
* @param {string} selector The CSS/Xpath selector used to locate the element.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see elementIdClick
* @api commands
*/
elementCommands.click = 'elementIdClick';
/**
* Clear a textarea or a text input element's value. Uses `elementIdValue` protocol command.
*
* ```
* this.demoTest = function (client) {
* client.clearValue('input[type=text]');
* };
* ```
*
* @method clearValue
* @param {string} selector The CSS/Xpath selector used to locate the element.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see elementIdClear
* @api commands
*/
elementCommands.clearValue = 'elementIdClear';
/**
* Retrieve the value of an attribute for a given DOM element. Uses `elementIdAttribute` protocol command.
*
* ```
* this.demoTest = function (client) {
* client.getAttribute("#main ul li a.first", "href", function(result) {
* this.assert.equal(typeof result, "object");
* this.assert.equal(result.status, 0);
* this.assert.equal(result.value, "#home");
* });
* };
* ```
*
* @method getAttribute
* @param {string} selector The CSS/Xpath selector used to locate the element.
* @param {string} attribute The attribute name to inspect.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see elementIdAttribute
* @returns {*} The value of the attribute
* @api commands
*/
elementCommands.getAttribute = ['elementIdAttribute', 1];
/**
* Retrieve the value of a css property for a given DOM element. Uses `elementIdCssProperty` protocol command.
*
* ```
* this.demoTest = function (client) {
* client.getCssProperty("#main ul li a.first", "display", function(result) {
* this.assert.equal(typeof result, "object");
* this.assert.equal(result.status, 0);
* this.assert.equal(result.value, 'inline');
* });
* };
* ```
*
* @method getCssProperty
* @param {string} selector The CSS/Xpath selector used to locate the element.
* @param {string} cssProperty The CSS property to inspect.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see elementIdCssProperty
* @returns {*} The value of the css property
* @api commands
*/
elementCommands.getCssProperty = ['elementIdCssProperty', 1];
/**
* Determine an element's size in pixels. Uses `elementIdSize` protocol command.
*
* ```
* this.demoTest = function (client) {
* client.getElementSize("#main ul li a.first", function(result) {
* this.assert.equal(typeof result, "object");
* this.assert.equal(result.status, 0);
* this.assert.equal(result.value.width, 500);
* this.assert.equal(result.value.height, 20);
* });
* };
* ```
*
* @method getElementSize
* @param {string} selector The CSS/Xpath selector used to locate the element.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see elementIdSize
* @returns {{width: number, height: number}} The width and height of the element in pixels
* @api commands
*/
elementCommands.getElementSize = 'elementIdSize';
/**
* Determine an element's location on the page. The point (0, 0) refers to the upper-left corner of the page.
*
* The element's coordinates are returned as a JSON object with x and y properties. Uses `elementIdLocation` protocol command.
*
* ```
* this.demoTest = function (client) {
* client.getLocation("#main ul li a.first", function(result) {
* this.assert.equal(typeof result, "object");
* this.assert.equal(result.status, 0);
* this.assert.equal(result.value.x, 200);
* this.assert.equal(result.value.y, 200);
* });
* };
* ```
*
* @method getLocation
* @param {string} selector The CSS/Xpath selector used to locate the element.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see elementIdLocation
* @returns {x:number, y:number} The X and Y coordinates for the element on the page.
* @api commands
*/
elementCommands.getLocation = 'elementIdLocation';
/**
* Determine an element's location on the screen once it has been scrolled into view. Uses `elementIdLocationInView` protocol command.
*
* ```
* this.demoTest = function (browser) {
* browser.getLocationInView("#main ul li a.first", function(result) {
* this.assert.equal(typeof result, "object");
* this.assert.equal(result.status, 0);
* this.assert.equal(result.value.x, 200);
* this.assert.equal(result.value.y, 200);
* });
* };
* ```
*
* @method getLocationInView
* @param {string} selector The CSS/Xpath selector used to locate the element.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see elementIdLocationInView
* @returns {x: number, y: number} The X and Y coordinates for the element on the page.
* @api commands
*/
elementCommands.getLocationInView = 'elementIdLocationInView';
/**
* Query for an element's tag name. Uses `elementIdName` protocol command.
*
* ```
* this.demoTest = function (client) {
* client.getTagName("#main ul li .first", function(result) {
* this.assert.equal(typeof result, "object");
* this.assert.equal(result.status, 0);
* this.assert.equal(result.value, "a");
* });
* };
* ```
*
* @method getTagName
* @param {string} selector The CSS/Xpath selector used to locate the element.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see elementIdName
* @returns {number} The element's tag name, as a lowercase string.
* @api commands
*/
elementCommands.getTagName = 'elementIdName';
/**
* Returns the visible text for the element. Uses `elementIdText` protocol command.
*
* ```
* this.demoTest = function (browser) {
* browser.getText("#main ul li a.first", function(result) {
* this.assert.equal(typeof result, "object");
* this.assert.equal(result.status, 0);
* this.assert.equal(result.value, "nightwatchjs.org");
* });
* };
* ```
*
* @method getText
* @param {string} selector The CSS/Xpath selector used to locate the element.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see elementIdText
* @returns {string} The element's visible text.
* @api commands
*/
elementCommands.getText = 'elementIdText';
/**
* Returns a form element current value. Uses `elementIdValue` protocol command.
*
* ```
* this.demoTest = function (browser) {
* browser.getValue("form.login input[type=text]", function(result) {
* this.assert.equal(typeof result, "object");
* this.assert.equal(result.status, 0);
* this.assert.equal(result.value, "enter username");
* });
* };
* ```
*
* @method getValue
* @param {string} selector The CSS/Xpath selector used to locate the element.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see elementIdValue
* @returns {string} The element's value.
* @api commands
*/
elementCommands.getValue = 'elementIdValue';
/**
* Determine if an element is currently displayed. Uses `elementIdDisplayed` protocol command.
*
* ```
* this.demoTest = function (browser) {
* browser.isVisible('#main', function(result) {
* this.assert.equal(typeof result, "object");
* this.assert.equal(result.status, 0);
* this.assert.equal(result.value, true);
* });
* };
* ```
*
* @method isVisible
* @param {string} selector The CSS/Xpath selector used to locate the element.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see elementIdDisplayed
* @api commands
*/
elementCommands.isVisible = 'elementIdDisplayed';
/**
* Move the mouse by an offset of the specified element. Uses `moveTo` protocol command.
*
* ```
* this.demoTest = function (browser) {
* browser.moveToElement('#main', 10, 10);
* };
* ```
*
* @method moveToElement
* @param {string} selector The CSS/Xpath selector used to locate the element.
* @param {number} xoffset X offset to move to, relative to the top-left corner of the element.
* @param {number} yoffset Y offset to move to, relative to the top-left corner of the element.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see moveTo
* @api commands
*/
elementCommands.moveToElement = ['moveTo', 2];
/**
* Sends some text to an element. Can be used to set the value of a form element or to send a sequence of key strokes to an element. Any UTF-8 character may be specified.
*
* An object map with available keys and their respective UTF-8 characters, as defined on [W3C WebDriver draft spec](http://www.w3.org/TR/webdriver/#character-types), is loaded onto the main Nightwatch instance as `client.Keys`.
*
* ```
* // send some simple text to an input
* this.demoTest = function (browser) {
* browser.setValue('input[type=text]', 'nightwatch');
* };
* //
* // send some text to an input and hit enter.
* this.demoTest = function (browser) {
* browser.setValue('input[type=text]', ['nightwatch', browser.Keys.ENTER]);
* };
* ```
*
* @link /session/:sessionId/element/:id/value
* @method setValue
* @param {string} selector The CSS/Xpath selector used to locate the element.
* @param {string|array} value The text to send to the element or key strokes.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see elementIdValue
* @api commands
*/
elementCommands.setValue = ['elementIdValue', 1];
/**
* Submit a FORM element. The submit command may also be applied to any element that is a descendant of a FORM element. Uses `submit` protocol command.
*
* ```
* this.demoTest = function (browser) {
* browser.submitForm('form.login');
* };
* ```
*
* @method submitForm
* @param {string} selector The CSS/Xpath selector used to locate the element.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @see submit
* @api commands
*/
elementCommands.submitForm = 'submit';
function addElementCommand(protocolAction, extraArgs) {
extraArgs = extraArgs || 0;
var expectedArgs = 3 + extraArgs;
return function commandActionFn() {
var originalStackTrace = commandActionFn.stackTrace;
var noopFn = function() {};
var args = Array.prototype.slice.call(arguments, 0);
if (typeof args[args.length-1] !== 'function') {
args.push(noopFn);
}
var defaultUsing = client.locateStrategy || 'css selector';
if (expectedArgs - args.length === 1) {
args.unshift(defaultUsing);
}
if (args.length < expectedArgs - 1 || args.length > expectedArgs) {
throw new Error(protocolAction + ' method expects ' + (expectedArgs - 1) + ' or ' + expectedArgs + ' arguments - ' + args.length + ' given.');
}
var using = args.shift();
var value = args.shift();
var callback = args.pop();
return new CommandAction(using, value, protocolAction, args, callback, originalStackTrace);
};
}
function CommandAction(using, value, protocolAction, args, callback, originalStackTrace) {
events.EventEmitter.call(this);
var $this = this;
var el = Protocol.element(using, value, function(result) {
if (result.status !== 0) {
callback.call(client.api, result);
var errorMessage = 'ERROR: Unable to locate element: "' + value + '" using: ' + using;
var stack = originalStackTrace.split('\n');
stack.shift();
Utils.showStackTraceWithHeadline(errorMessage, stack);
client.results.errors++;
client.errors.push(errorMessage + '\n' + stack.join('\n'));
$this.emit('complete', el, $this);
} else {
result = result.value.ELEMENT;
args.push(function(r) {
callback.call(client.api, r);
});
args.unshift(result);
var c = Protocol[protocolAction].apply(Protocol, args).once('complete', function() {
$this.emit('complete', c, $this);
});
}
});
}
util.inherits(CommandAction, events.EventEmitter);
Object.keys(elementCommands).forEach(function(commandName) {
var args = elementCommands[commandName];
if (!Array.isArray(args)) {
args = [args];
}
returnValue[commandName] = addElementCommand.apply(client.api, args);
});
// alias
returnValue.sendKeys = returnValue.setValue;
return returnValue;
};

View File

@@ -0,0 +1,43 @@
var util = require('util');
var events = require('events');
/**
* Search for an element on the page, starting with the first element of the array, and where each element in the passed array is nested under the previous one. The located element will be returned as a WebElement JSON object.
*
* @param {Array} elements An array of ancestor element objects containing selector and locateStrategy properties
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @api protocol
*/
function ElementByRecursion(client) {
events.EventEmitter.call(this);
this.protocol = require('../protocol.js')(client);
}
util.inherits(ElementByRecursion, events.EventEmitter);
ElementByRecursion.prototype.command = function(elements, callback) {
var self = this;
var allElements = elements.slice();
var topElement = allElements.shift();
var el = this.protocol.element(topElement.locateStrategy, topElement.selector, function checkResult(result) {
if (result.status !== 0) {
callback(result);
self.emit('complete', el, self);
} else {
var nextElement = allElements.shift();
var parentId = result.value.ELEMENT;
if (nextElement) {
self.protocol.elementIdElement(parentId, nextElement.locateStrategy, nextElement.selector, checkResult);
} else {
callback(result);
self.emit('complete', el, self);
}
}
});
return this;
};
module.exports = ElementByRecursion;

View File

@@ -0,0 +1,94 @@
var util = require('util');
var events = require('events');
var Q = require('q');
/**
* Search for multiple elements on the page, starting with the first element of the array, and where each element in the passed array is nested under the previous one. The located element will be returned as a WebElement JSON objects.
*
* @param {Array} elements An array of ancestor element objects containing selector and locateStrategy properties
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @api protocol
*/
function ElementsByRecursion(client) {
events.EventEmitter.call(this);
this.protocol = require('../protocol.js')(client);
}
util.inherits(ElementsByRecursion, events.EventEmitter);
ElementsByRecursion.prototype.command = function(elements, callback) {
var self = this;
function deferredElementIdElements(el, using, value) {
var deferred = Q.defer();
var req = self.protocol.elementIdElements(el, using, value, function(result) {
deferred.resolve(result);
});
req.on('error', function() {
deferred.reject();
});
return deferred.promise;
}
function createResult(result, foundElements) {
return {
status: 0,
sessionId: result.sessionId,
value: foundElements.map(function(e) {
return {
ELEMENT: e
};
}),
class: result.class,
hCode: result.hCode
};
}
function aggregateResults(results) {
var result;
var foundElements = [];
for (var i = 0; i < results.length; i++) {
result = results[i];
if (result.status === 0 && result.value && result.value.length) {
result.value.forEach(function(e) {
if (foundElements.indexOf(e.ELEMENT) <= -1) {
// In case we have multiple matches on the same element, only add once
foundElements.push(e.ELEMENT);
}
});
}
}
return createResult(result, foundElements);
}
var allElements = elements.slice();
var topElement = allElements.shift();
var el = this.protocol.elements(topElement.locateStrategy, topElement.selector, function checkResult() {
var result = aggregateResults(arguments);
if (result.value.length === 0) {
callback(result);
self.emit('complete', el, self);
return;
}
var nextElement = allElements.shift();
if (nextElement) {
var promises = [];
result.value.forEach(function(el) {
var p = deferredElementIdElements(el.ELEMENT, nextElement.locateStrategy, nextElement.selector, checkResult);
promises.push(p);
});
Q.all(promises).spread(checkResult);
} else {
callback(result);
self.emit('complete', el, self);
}
});
return this;
};
module.exports = ElementsByRecursion;

View File

@@ -0,0 +1,280 @@
var util = require('util');
var events = require('events');
var Logger = require('../../util/logger.js');
var Utils = require('../../util/utils.js');
/*!
* Base class for waitForElement commands. It provides a command
* method and element* methods to be overwritten by subclasses
*
* @constructor
*/
function WaitForElement() {
events.EventEmitter.call(this);
this.startTimer = null;
this.cb = null;
this.ms = null;
this.element = null;
this.abortOnFailure = typeof this.client.api.globals.abortOnAssertionFailure == 'undefined' || this.client.api.globals.abortOnAssertionFailure;
this.selector = null;
this.locateStrategy = this.client.locateStrategy || 'css selector';
this.rescheduleInterval = this.client.api.globals.waitForConditionPollInterval || this.client.options.waitForConditionPollInterval || 500; //ms
this.throwOnMultipleElementsReturned = this.client.api.globals.throwOnMultipleElementsReturned || this.client.options.throwOnMultipleElementsReturned || false;
this.protocol = require('../protocol.js')(this.client);
}
util.inherits(WaitForElement, events.EventEmitter);
/*!
* The public command function which will be called by the test runner. Arguments can be passed in a variety of ways.
*
* The custom message always is last and the callback is always before the message or last if a message is not passed.
*
* The second argument is always the time in milliseconds. The third argument can be either of:
* - abortOnFailure: this can overwrite the default behaviour of aborting the test if the condition is not met within the specified time
* - rescheduleInterval: this can overwrite the default polling interval (currently 500ms)
* The above can be supplied also together, in which case the rescheduleInterval is specified before the abortOnFailure.
*
* Some of the multiple usage possibilities:
* ---------------------------------------------------------------------------
* - with no arguments; in this case a global default timeout is expected
* waitForElement('body');
*
* - with a global default timeout and a callback
* waitForElement('body', function() {});
*
* - with a global default timeout, a callback and a custom message
* waitForElement('body', function() {}, 'test message');
*
* - with only the timeout
* waitForElement('body', 500);
*
* - with a timeout and a custom message
* waitForElement('body', 500, 'test message);
*
* - with a timeout and a callback
* waitForElement('body', 500, function() { .. });
*
* - with a timeout and a custom abortOnFailure
* waitForElement('body', 500, true);
*
* - with a timeout, a custom abortOnFailure and a custom message
* waitForElement('body', 500, true, 'test message');
*
* - with a timeout, a custom abortOnFailure and a callback
* waitForElement('body', 500, true, function() { .. });
*
* - with a timeout, a custom abortOnFailure, a callback and a custom message
* waitForElement('body', 500, true, function() { .. }, 'test message');
*
* - with a timeout, a custom reschedule interval and a callback
* waitForElement('body', 500, 100, function() { .. });
*
* - with a timeout, a custom rescheduleInterval and a custom abortOnFailure
* waitForElement('body', 500, 100, false);
*
*
* @param {string} selector
* @param {number|function|string} milliseconds
* @param {function|boolean|string|number} callbackOrAbort
* @returns {WaitForElement}
*/
WaitForElement.prototype.command = function commandFn(selector, milliseconds, callbackOrAbort) {
this.startTimer = new Date().getTime();
this.ms = this.setMilliseconds(milliseconds);
this._stackTrace = commandFn.stackTrace;
if (typeof arguments[1] === 'function') {
////////////////////////////////////////////////
// The command was called with an implied global timeout:
//
// waitForElement('body', function() {});
// waitForElement('body', function() {}, 'custom message');
////////////////////////////////////////////////
this.cb = arguments[1];
} else if (typeof arguments[2] === 'boolean') {
////////////////////////////////////////////////
// The command was called with a custom abortOnFailure:
//
// waitForElement('body', 500, false);
////////////////////////////////////////////////
this.abortOnFailure = arguments[2];
// The optional callback is the 4th argument now
this.cb = arguments[3] || function() {};
} else if (typeof arguments[2] === 'number') {
////////////////////////////////////////////////
// The command was called with a custom rescheduleInterval:
//
// waitForElement('body', 500, 100);
////////////////////////////////////////////////
this.rescheduleInterval = arguments[2];
if (typeof arguments[3] === 'boolean') {
////////////////////////////////////////////////
// The command was called with a custom rescheduleInterval and custom abortOnFailure:
//
// waitForElement('body', 500, 100, false);
////////////////////////////////////////////////
this.abortOnFailure = arguments[3];
// The optional callback is the 5th argument now
this.cb = arguments[4] || function() {};
} else {
// The optional callback is the 4th argument now
this.cb = arguments[3] || function() {};
}
} else {
// The optional callback is the 3th argument now
this.cb = (typeof callbackOrAbort === 'function' && callbackOrAbort) || function() {};
}
// support for a custom message
this.message = null;
if (arguments.length > 1) {
var lastArgument = arguments[arguments.length - 1];
if (typeof lastArgument === 'string') {
this.message = lastArgument;
}
}
this.selector = selector;
this.checkElement();
return this;
};
/*!
* @override
*/
WaitForElement.prototype.elementFound = function(result, now) {};
/*!
* @override
*/
WaitForElement.prototype.elementNotFound = function(result, now) {};
/*!
* @override
*/
WaitForElement.prototype.elementVisible = function(result, now) {};
/*!
* @override
*/
WaitForElement.prototype.elementNotVisible = function(result, now) {};
/*!
* Reschedule the checkElement
*/
WaitForElement.prototype.reschedule = function(method) {
var self = this;
method = method || 'checkElement';
setTimeout(function() {
self[method]();
}, this.rescheduleInterval);
};
WaitForElement.prototype.complete = function() {
var args = Array.prototype.slice.call(arguments, 0);
args.push(this);
this.cb.apply(this.client.api, args);
this.emit('complete');
return this;
};
WaitForElement.prototype.pass = function(result, defaultMsg, timeMs) {
this.message = this.formatMessage(defaultMsg, timeMs);
this.client.assertion(true, null, null, this.message, this.abortOnFailure);
return this.complete(result);
};
WaitForElement.prototype.fail = function(result, actual, expected, defaultMsg) {
this.message = this.formatMessage(defaultMsg);
this.client.assertion(false, actual, expected, this.message, this.abortOnFailure, this._stackTrace);
return this.complete(result);
};
/*!
* Will start checking if the element exists and if not re-schedule the check
* until the timeout expires or the condition has been met
*/
WaitForElement.prototype.checkElement = function() {
var self = this;
this.getProtocolCommand(function(result) {
var now = new Date().getTime();
if (result.value && result.value.length > 0) {
if (result.value.length > 1) {
var message = 'WaitForElement found ' + result.value.length + ' elements for selector "' + self.selector + '".';
if (self.throwOnMultipleElementsReturned) {
throw new Error(message);
} else if (self.client.options.output) {
console.log(Logger.colors.green(' Warn: ' + message + ' Only the first one will be checked.'));
}
}
self.element = result.value[0].ELEMENT;
return self.elementFound(result, now);
}
return self.elementNotFound(result, now);
});
};
WaitForElement.prototype.getProtocolCommand = function(callback) {
return this.protocol.elements(this.locateStrategy, this.selector, callback);
};
/*!
* Will start checking if the element is visible and if not re-schedule the check
* until the timeout expires or the condition has been met
*/
WaitForElement.prototype.isVisible = function() {
var self = this;
this.protocol.elementIdDisplayed(this.element, function(result) {
var now = new Date().getTime();
if (result.status === 0 && result.value === true) {
// element was visible
return self.elementVisible(result, now);
}
if (result.status === -1 && result.errorStatus === 10) {
return self.checkElement();
}
return self.elementNotVisible(result, now);
});
};
/**
* @param {string} defaultMsg
* @param {number} [timeMs]
* @returns {string}
*/
WaitForElement.prototype.formatMessage = function (defaultMsg, timeMs) {
return Utils.format(this.message || defaultMsg, this.selector, timeMs || this.ms);
};
/**
* Set the time in milliseconds to wait for the condition, accepting a given value or a globally defined default
*
* @param {number} [timeoutMs]
* @throws Will throw an error if the global default is undefined or a non-number
* @returns {number}
*/
WaitForElement.prototype.setMilliseconds = function (timeoutMs) {
if (timeoutMs && typeof timeoutMs === 'number') {
return timeoutMs;
}
var globalTimeout = this.client.api.globals.waitForConditionTimeout;
if (typeof globalTimeout !== 'number') {
throw new Error('waitForElement expects second parameter to have a global default ' +
'(waitForConditionTimeout) to be specified if not passed as the second parameter ');
}
return globalTimeout;
};
module.exports = WaitForElement;

View File

@@ -0,0 +1,53 @@
var util = require('util');
var WaitForElement = require('./_waitForElement.js');
/**
* Opposite of `waitForElementPresent`. Waits a given time in milliseconds for an element to be not present (i.e. removed) in the page before performing any other commands or assertions.
*
* If the element is still present after the specified amount of time, the test fails.
*
* You can change the polling interval by defining a `waitForConditionPollInterval` property (in milliseconds) in as a global property in your `nightwatch.json` or in your external globals file.
*
* Similarly, a default timeout can be specified as a global `waitForConditionTimeout` property (in milliseconds).
*
* ```
* this.demoTest = function (browser) {
* browser.waitForElementNotPresent('#dialog', 1000);
* };
* ```
*
* @method waitForElementNotPresent
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {number} time The number of milliseconds to wait. The runner performs repeated checks every 500 ms.
* @param {boolean} [abortOnFailure] By the default if the element is not found the test will fail. Set this to false if you wish for the test to continue even if the assertion fails. To set this globally you can define a property `abortOnAssertionFailure` in your globals.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @param {string} [message] Optional message to be shown in the output; the message supports two placeholders: %s for current selector and %d for the time (e.g. Element %s was not in the page for %d ms).
* @see waitForElementPresent
* @since v0.4.0
* @api commands
*/
function WaitForElementNotPresent() {
WaitForElement.call(this);
this.expectedValue = 'not found';
}
util.inherits(WaitForElementNotPresent, WaitForElement);
WaitForElementNotPresent.prototype.elementFound = function(result, now) {
if (now - this.startTimer < this.ms) {
// element is still there, schedule another check
this.reschedule();
return this;
}
var defaultMsg = 'Timed out while waiting for element <%s> to be removed for %d milliseconds.';
return this.fail(result, 'found', this.expectedValue, defaultMsg);
};
WaitForElementNotPresent.prototype.elementNotFound = function(result, now) {
var defaultMsg = 'Element <%s> was not present after %d milliseconds.';
return this.pass(result, defaultMsg, now - this.startTimer);
};
module.exports = WaitForElementNotPresent;

View File

@@ -0,0 +1,56 @@
var util = require('util');
var WaitForElementPresent = require('./waitForElementPresent.js');
/**
* Opposite of `waitForElementVisible`. Waits a given time in milliseconds for an element to be not visible (i.e. hidden but existing) in the page before performing any other commands or assertions.
*
* If the element fails to be hidden in the specified amount of time, the test fails.
*
* You can change the polling interval by defining a `waitForConditionPollInterval` property (in milliseconds) in as a global property in your `nightwatch.json` or in your external globals file.
*
* Similarly, a default timeout can be specified as a global `waitForConditionTimeout` property (in milliseconds).
*
* ```
* this.demoTest = function (browser) {
* browser.waitForElementNotVisible('#dialog', 1000);
* };
* ```
*
* @method waitForElementNotVisible
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {number} time The number of milliseconds to wait. The runner performs repeated checks every 500 ms.
* @param {boolean} [abortOnFailure] By the default if the element is not found the test will fail. Set this to false if you wish for the test to continue even if the assertion fails. To set this globally you can define a property `abortOnAssertionFailure` in your globals.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @param {string} [message] Optional message to be shown in the output; the message supports two placeholders: %s for current selector and %d for the time (e.g. Element %s was not in the page for %d ms).
* @since v0.4.0
* @see waitForElementVisible
* @api commands
*/
function WaitForElementNotVisible() {
WaitForElementPresent.call(this);
this.expectedValue = 'not visible';
}
util.inherits(WaitForElementNotVisible, WaitForElementPresent);
WaitForElementNotVisible.prototype.elementFound = function() {
return this.isVisible();
};
WaitForElementNotVisible.prototype.elementVisible = function(result, now) {
if (now - this.startTimer < this.ms) {
// element wasn't visible, schedule another check
this.reschedule('isVisible');
return this;
}
var defaultMsg = 'Timed out while waiting for element <%s> to not be visible for %d milliseconds.';
return this.fail(result, 'visible', this.expectedValue, defaultMsg);
};
WaitForElementNotVisible.prototype.elementNotVisible = function(result, now) {
var defaultMsg = 'Element <%s> was not visible after %d milliseconds.';
return this.pass(result, defaultMsg, now - this.startTimer);
};
module.exports = WaitForElementNotVisible;

View File

@@ -0,0 +1,60 @@
var util = require('util');
var WaitForElement = require('./_waitForElement.js');
/**
* Waits a given time in milliseconds for an element to be present in the page before performing any other commands or assertions.
*
* If the element fails to be present in the specified amount of time, the test fails. You can change this by setting `abortOnFailure` to `false`.
*
* You can change the polling interval by defining a `waitForConditionPollInterval` property (in milliseconds) in as a global property in your `nightwatch.json` or in your external globals file.
*
* Similarly, a default timeout can be specified as a global `waitForConditionTimeout` property (in milliseconds).
*
* ```
* this.demoTest = function (browser) {
* browser.waitForElementPresent('body', 1000);
* // continue if failed
* browser.waitForElementPresent('body', 1000, false);
* // with callback
* browser.waitForElementPresent('body', 1000, function() {
* // do something while we're here
* });
* // custom Spanish message
* browser.waitForElementPresent('body', 1000, 'elemento %s no era presente en %d ms');
* // many combinations possible - the message is always the last argument
* browser.waitForElementPresent('body', 1000, false, function() {}, 'elemento %s no era presente en %d ms');
* };
* ```
*
* @method waitForElementPresent
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {number} time The number of milliseconds to wait. The runner performs repeated checks every 500 ms.
* @param {boolean} [abortOnFailure] By the default if the element is not found the test will fail. Set this to false if you wish for the test to continue even if the assertion fails. To set this globally you can define a property `abortOnAssertionFailure` in your globals.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @param {string} [message] Optional message to be shown in the output; the message supports two placeholders: %s for current selector and %d for the time (e.g. Element %s was not in the page for %d ms).
* @api commands
*/
function WaitForElementPresent() {
WaitForElement.call(this);
this.expectedValue = 'found';
}
util.inherits(WaitForElementPresent, WaitForElement);
WaitForElementPresent.prototype.elementFound = function(result, now) {
var defaultMsg = 'Element <%s> was present after %d milliseconds.';
return this.pass(result, defaultMsg, now - this.startTimer);
};
WaitForElementPresent.prototype.elementNotFound = function(result, now) {
if (now - this.startTimer < this.ms) {
// element wasn't found, schedule another check
this.reschedule();
return this;
}
var defaultMsg = 'Timed out while waiting for element <%s> to be present for %d milliseconds.';
return this.fail({value:false}, 'not found', this.expectedValue, defaultMsg);
};
module.exports = WaitForElementPresent;

View File

@@ -0,0 +1,64 @@
var util = require('util');
var WaitForElementPresent = require('./waitForElementPresent.js');
/**
* Waits a given time in milliseconds for an element to be visible in the page before performing any other commands or assertions.
*
* If the element fails to be present and visible in the specified amount of time, the test fails. You can change this by setting `abortOnFailure` to `false`.
*
* You can change the polling interval by defining a `waitForConditionPollInterval` property (in milliseconds) in as a global property in your `nightwatch.json` or in your external globals file.
*
* Similarly, a default timeout can be specified as a global `waitForConditionTimeout` property (in milliseconds).
*
* ```
* this.demoTest = function (browser) {
* browser.waitForElementVisible('body', 1000);
* // continue if failed
* browser.waitForElementVisible('body', 1000, false);
* // with callback
* browser.waitForElementVisible('body', 1000, function() {
* // do something while we're here
* });
* // custom Spanish message
* browser.waitForElementVisible('body', 1000, 'elemento %s no era visible en %d ms');
* // many combinations possible - the message is always the last argument
* browser.waitForElementVisible('body', 1000, false, function() {}, 'elemento %s no era visible en %d ms');
* };
* ```
*
* @method waitForElementVisible
* @param {string} selector The selector (CSS / Xpath) used to locate the element.
* @param {number} time The number of milliseconds to wait. The runner performs repeated checks every 500 ms.
* @param {boolean} [abortOnFailure] By the default if the element is not found the test will fail. Set this to false if you wish for the test to continue even if the assertion fails. To set this globally you can define a property `abortOnAssertionFailure` in your globals.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @param {string} [message] Optional message to be shown in the output; the message supports two placeholders: %s for current selector and %d for the time (e.g. Element %s was not in the page for %d ms).
* @api commands
*/
function WaitForElementVisible() {
WaitForElementPresent.call(this);
this.expectedValue = 'visible';
}
util.inherits(WaitForElementVisible, WaitForElementPresent);
WaitForElementVisible.prototype.elementFound = function(result, now) {
return this.isVisible();
};
WaitForElementVisible.prototype.elementVisible = function(result, now) {
var defaultMsg = 'Element <%s> was visible after %d milliseconds.';
return this.pass(result, defaultMsg, now - this.startTimer);
};
WaitForElementVisible.prototype.elementNotVisible = function(result, now) {
if (now - this.startTimer < this.ms) {
// element wasn't visible, schedule another check
this.reschedule('isVisible');
return this;
}
var defaultMsg = 'Timed out while waiting for element <%s> to be visible for %d milliseconds.';
return this.fail(result, 'not visible', this.expectedValue, defaultMsg);
};
module.exports = WaitForElementVisible;

25
tests/node_modules/nightwatch/lib/api/errors.json generated vendored Normal file
View File

@@ -0,0 +1,25 @@
{
"0": {"id": "Success", "message": "The command executed successfully."},
"7": {"id": "NoSuchElement", "message": "An element could not be located on the page using the given search parameters."},
"8": {"id": "NoSuchFrame", "message": "A request to switch to a frame could not be satisfied because the frame could not be found."},
"9": {"id": "UnknownCommand", "message": "The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource."},
"10": {"id": "StaleElementReference", "message": "An element command failed because the referenced element is no longer attached to the DOM."},
"11": {"id": "ElementNotVisible", "message": "An element command could not be completed because the element is not visible on the page."},
"12": {"id": "InvalidElementState", "message": "An element command could not be completed because the element is in an invalid state (e.g. attempting to click a disabled element)."},
"13": {"id": "UnknownError", "message": "An unknown server-side error occurred while processing the command."},
"15": {"id": "ElementIsNotSelectable", "message": "An attempt was made to select an element that cannot be selected."},
"17": {"id": "JavaScriptError", "message": "An error occurred while executing user supplied JavaScript."},
"19": {"id": "XPathLookupError", "message": "An error occurred while searching for an element by XPath."},
"23": {"id": "NoSuchWindow", "message": "A request to switch to a different window could not be satisfied because the window could not be found."},
"24": {"id": "InvalidCookieDomain", "message": "An illegal attempt was made to set a cookie under a different domain than the current page."},
"25": {"id": "UnableToSetCookie", "message": "A request to set a cookie's value could not be satisfied."},
"26": {"id": "UnexpectedAlertOpen", "message": "A modal dialog was open, blocking this operation."},
"27": {"id": "NoAlertOpenError", "message": "An attempt was made to operate on a modal dialog when one was not open."},
"28": {"id": "Timeout", "message": ""},
"29": {"id": "InvalidElementCoordinates", "message": "The coordinates provided to an interactions operation are invalid."},
"30": {"id": "IMENotAvailable", "message": "IME was not available."},
"31": {"id": "IMEEngineActivationFailed", "message": "An IME engine could not be started."},
"32": {"id": "InvalidSelector", "message": "Argument was an invalid selector (e.g. XPath/CSS)."},
"33": {"id": "SessionNotCreatedException", "message": "A new session could not be created."},
"34": {"id": "MoveTargetOutOfBounds", "message": "Target provided for a move action is out of bounds."}
}

138
tests/node_modules/nightwatch/lib/api/expect.js generated vendored Executable file
View File

@@ -0,0 +1,138 @@
var util = require('util');
var events = require('events');
var chai = require('chai-nightwatch');
var expect = chai.expect;
var ChaiAssertion = chai.Assertion;
var Q = require('q');
var flag = chai.flag;
module.exports = function(client) {
var Protocol = require('./protocol.js')(client);
var PresentAssertion = require('./expect/present.js');
var AttributeAssertion = require('./expect/attribute.js');
var CssAssertion = require('./expect/css.js');
var TextAssertion = require('./expect/text.js');
var EnabledAssertion = require('./expect/enabled.js');
var VisibleAssertion = require('./expect/visible.js');
var SelectedAssertion = require('./expect/selected.js');
var TypeAssertion = require('./expect/type.js');
var ValueAssertion = require('./expect/value.js');
var Expect = {};
ChaiAssertion.addMethod('before', function(ms) {
flag(this, 'waitFor', ms);
flag(this, 'before', true);
});
ChaiAssertion.addMethod('after', function(ms) {
flag(this, 'after', true);
flag(this, 'waitFor', ms);
});
ChaiAssertion.addProperty('present', function() {
createAssertion(PresentAssertion, this);
});
ChaiAssertion.addProperty('enabled', function() {
createAssertion(EnabledAssertion, this);
});
ChaiAssertion.addProperty('text', function() {
createAssertion(TextAssertion, this);
});
ChaiAssertion.addProperty('value', function() {
createAssertion(ValueAssertion, this);
});
ChaiAssertion.addProperty('visible', function() {
createAssertion(VisibleAssertion, this);
});
ChaiAssertion.addProperty('selected', function() {
createAssertion(SelectedAssertion, this);
});
ChaiAssertion.addMethod('attribute', function(attribute, msg) {
createAssertion(AttributeAssertion, this, [attribute, msg]);
});
ChaiAssertion.addMethod('css', function(property, msg) {
createAssertion(CssAssertion, this, [property, msg]);
});
function typeAssertion(type, msg) {
createAssertion(TypeAssertion, this, [type, msg]);
}
ChaiAssertion.addMethod('a', typeAssertion);
ChaiAssertion.addMethod('an', typeAssertion);
function createAssertion(AssertionClass, chaiAssert, args) {
function F() {
this.setAssertion(chaiAssert)
.setClient(client)
.setProtocol(Protocol)
.init();
return AssertionClass.apply(this, args);
}
F.prototype = AssertionClass.prototype;
chaiAssert.assertion = new F();
}
function Element(selector, using) {
this.selector = selector;
this.using = using;
this.startTime = null;
this.emitter = null;
this.createPromise();
}
util.inherits(Element, events.EventEmitter);
Element.prototype.getElementsCommand = function(callback) {
this.locator = this.using || client.locateStrategy || 'css selector';
return Protocol.elements(this.locator, this.selector, callback);
};
Element.prototype.promise = function() {
return this.deferred.promise;
};
Element.prototype.createPromise = function() {
this.deferred = Q.defer();
return this.deferred.promise;
};
Element.prototype.locate = function(emitter) {
if (emitter) {
this.emitter = emitter;
this.startTime = new Date().getTime();
}
this.getElementsCommand(function(result) {
if (result.status !== 0 || !result.value || result.value.length === 0) {
this.deferred.reject(result);
} else {
this.deferred.resolve(result.value[0]);
}
}.bind(this));
};
Expect.element = function(selector, using) {
var element = new Element(selector, using);
var promise = element.promise();
var expect = chai.expect(promise);
flag(expect, 'selector', selector);
flag(expect, 'promise', promise);
flag(expect, 'element', element);
return {
element : element,
expect : expect
};
};
return Expect;
};

View File

@@ -0,0 +1,341 @@
/**
* Abstract assertion class that will subclass all defined Chai assertions
*
* All assertions must implement the following api:
*
* - @type {function}
* executeCommand
* - @type {string}
* elementFound
* - @type {function}
* elementNotFound
* - @type {function}
* retryCommand
* - @type {string}
* expected
* - @type {string}
* actual
* - @type {string}
* message
* - @type {boolean}
* passed
*
* @constructor
*/
var util = require('util');
var events = require('events');
var Assertion = require('../../core/assertion.js');
var chai = require('chai-nightwatch');
var flag = chai.flag;
var Utils = require('../../util/utils.js');
function BaseAssertion() {
}
BaseAssertion.ASSERT_FLAGS = [
'be',
'that',
'have',
'which',
'equal',
'contains',
'matches',
'before',
'after',
'waitFor'
];
/**
* @override
*/
BaseAssertion.prototype.executeCommand = function(callback) {};
BaseAssertion.prototype.setAssertion = function(assertion) {
this.assertion = assertion;
return this;
};
BaseAssertion.prototype.flag = function(key, value) {
if (typeof value == 'undefined') {
return flag(this.assertion, key);
}
flag(this.assertion, key, value);
return this;
};
BaseAssertion.prototype.setClient = function(client) {
this.client = client;
return this;
};
BaseAssertion.prototype.setProtocol = function(protocol) {
this.protocol = protocol;
return this;
};
BaseAssertion.prototype.init = function() {
this.promise = this.flag('promise');
this.element = this.flag('element');
this.selector = this.flag('selector');
this.setNegate();
this.waitForMs = this.client.api.globals.waitForConditionTimeout || null;
if (this.waitForMs) {
this.flag('waitFor', this.waitForMs);
}
this.retryInterval = this.client.api.globals.waitForConditionPollInterval || 500;
this.retries = 0;
this.messageParts = [];
this.abortOnFailure = typeof this.client.api.globals.abortOnAssertionFailure == 'undefined' || this.client.api.globals.abortOnAssertionFailure;
this.passed = false;
this.expected = null;
this.elementResult = null;
this.flags = [];
};
BaseAssertion.prototype.start = function() {
this.promise.then(this.onPromiseResolved.bind(this), this.onPromiseRejected.bind(this));
};
BaseAssertion.prototype.onPromiseResolved = function(elementResult) {
if (elementResult) {
this.elementResult = elementResult;
}
this.executeCommand(function(result) {
this.resultValue = result.value;
this.resultStatus = result.status;
this.resultErrorStatus = result.errorStatus;
this.processFlags();
this.elementFound();
if (!this.passed && this.shouldRetry()) {
this.scheduleRetry();
} else {
this.done();
}
}.bind(this));
};
BaseAssertion.prototype.onPromiseRejected = function(response) {
this.processFlags();
if (this.shouldRetry() && !this.negate) {
this.scheduleRetry();
return;
}
this.messageParts.push(' - element was not found');
this.actual = 'not present';
this.expected = 'present';
this.elementNotFound();
this.done();
};
BaseAssertion.prototype.processFlags = function() {
BaseAssertion.ASSERT_FLAGS.forEach(function(entry) {
var value = this.flag(entry);
if ((typeof value != 'undefined') && (typeof this['@' + entry + 'Flag'] == 'function')) {
this.flags.push([entry, value]);
this['@' + entry + 'Flag'](value);
}
}.bind(this));
};
BaseAssertion.prototype.hasFlag = function(type) {
return this.flags.some(function(flag) {
return flag[0] === type;
});
};
/**
* @override
*/
BaseAssertion.prototype.elementFound = function() {};
/**
* @override
*/
BaseAssertion.prototype.elementNotFound = function() {};
BaseAssertion.prototype.done = function() {
this.formatMessage();
var stackTrace = this.flag('element')._stackTrace;
Assertion.assert(
this.passed,
this.actual,
this.expected,
this.message,
this.abortOnFailure,
stackTrace
);
this.element.emitter.emit('complete');
};
BaseAssertion.prototype.getResult = function(value, fn) {
var result = fn.call(this);
this.setNegate();
return this.negate ? !result : result;
};
BaseAssertion.prototype.shouldRetryLocateElement = function() {
return (!this.elementResult || this.resultStatus === 10 || this.resultErrorStatus === 10);
};
BaseAssertion.prototype.shouldRetry = function() {
if (!this.waitForMs) {
return false;
}
this.elapsedTime = this.getElapsedTime();
return (this.elapsedTime < this.waitForMs);
};
BaseAssertion.prototype.getElapsedTime = function() {
var timeNow = new Date().getTime();
return timeNow - this.element.startTime;
};
BaseAssertion.prototype.scheduleRetry = function() {
this.retries++;
setTimeout(this.retryCommand.bind(this), this.retryInterval);
};
BaseAssertion.prototype.formatMessage = function() {
this.message = Utils.format(this.message || this.message, this.selector);
this.message += this.messageParts.join('');
};
BaseAssertion.prototype['@containsFlag'] = function(value) {
var verb = (this.hasFlag('that') || this.hasFlag('which')) ? 'contains' : 'contain';
this.conditionFlag(value, function() {
return this.resultValue ? this.resultValue.indexOf(value) > -1 : false;
}, [
'not ' + verb,
verb
]);
return this;
};
BaseAssertion.prototype['@equalFlag'] = function(value) {
var verb;
if (this.hasFlag('have')) {
verb = (this.hasFlag('that') || this.hasFlag('which')) ? 'equals' : 'equal to';
} else {
verb = 'equal';
}
this.conditionFlag(value, function() {
return this.resultValue == value;
}, [
'not ' + verb,
verb
]);
return this;
};
BaseAssertion.prototype['@matchesFlag'] = function(re) {
var adverb = this.hasFlag('that') || this.hasFlag('which');
var verb = adverb ? 'matches' : 'match';
this.conditionFlag(re, function() {
return re.test(this.resultValue);
}, [
(adverb ? 'does ' : '') + 'not match',
verb
]);
return this;
};
BaseAssertion.prototype.conditionFlag = function(value, conditionFn, arrverb) {
this.passed = this.getResult(value, conditionFn);
var verb = this.negate ? arrverb[0]: arrverb[1];
this.expected = verb + ' \'' + value + '\'';
this.actual = this.resultValue;
if (this.retries > 0) {
return;
}
var needsSpace = this.messageParts.length === 0 ? true : this.messageParts[this.messageParts.length-1].slice(-1) != ' ';
if (needsSpace) {
verb = ' ' + verb;
}
if (!this.customMessage) {
this.messageParts.push(
verb,
': "', value, '"'
);
}
};
BaseAssertion.prototype.setNegate = function() {
this.negate = this.flag('negate') || false;
return this;
};
BaseAssertion.prototype['@beforeFlag'] = function(value) {};
BaseAssertion.prototype['@afterFlag'] = function(value) {};
BaseAssertion.prototype['@haveFlag'] = function(value) {};
BaseAssertion.prototype['@waitForFlag'] = function(value) {
if (this.waitForMs !== value) {
this.waitForMs = value;
if (!this.customMessage) {
this.messageParts.push(this.checkWaitForMsg(this.waitForMs));
}
}
};
BaseAssertion.prototype['@thatFlag'] = function() {
if (this.retries > 0) {
return;
}
if (!this.customMessage) {
this.messageParts.push(' that ');
}
return this;
};
BaseAssertion.prototype['@whichFlag'] = function() {
if (this.retries > 0) {
return;
}
if (!this.customMessage) {
this.messageParts.push(' which ');
}
return this;
};
BaseAssertion.prototype['@beFlag'] = function() {};
BaseAssertion.prototype.hasCondition = function() {
return (this.hasFlag('contains') || this.hasFlag('equal') || this.hasFlag('matches'));
};
BaseAssertion.prototype.checkWaitForMsg = function(waitFor) {
var preposition = this.flag('before') && 'in' ||
this.flag('after') && 'after' ||
'in';
return ' ' + preposition +' ' + waitFor + 'ms';
};
BaseAssertion.prototype.retryCommand = function() {
if (this.shouldRetryLocateElement()) {
this.promise = this.element.createPromise();
this.promise.then(this.onPromiseResolved.bind(this), this.onPromiseRejected.bind(this));
this.element.locate();
} else {
this.onPromiseResolved();
}
};
module.exports = BaseAssertion;

View File

@@ -0,0 +1,98 @@
/**
* Checks if a given attribute of an element exists and optionally if it has the expected value.
*
* ```
* this.demoTest = function (browser) {
* browser.expect.element('body').to.have.attribute('data-attr');
* browser.expect.element('body').to.not.have.attribute('data-attr');
* browser.expect.element('body').to.not.have.attribute('data-attr', 'Testing if body does not have data-attr');
* browser.expect.element('body').to.have.attribute('data-attr').before(100);
* browser.expect.element('body').to.have.attribute('data-attr')
* .equals('some attribute');
* browser.expect.element('body').to.have.attribute('data-attr')
* .not.equals('other attribute');
* browser.expect.element('body').to.have.attribute('data-attr')
* .which.contains('something');
* browser.expect.element('body').to.have.attribute('data-attr')
* .which.matches(/^something\ else/);
* };
* ```
*
* @method attribute
* @param {string} attribute The attribute name
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @display .attribute(name)
* @since v0.7
* @api expect
*/
var util = require('util');
var events = require('events');
var BaseAssertion = require('./_baseAssertion.js');
function AttributeAssertion(attribute, msg) {
this.flag('attributeFlag', true);
this.attribute = attribute;
this.customMessage = msg;
this.message = msg || 'Expected element <%s> to ' + (this.negate ? 'not have' : 'have') + ' attribute "' + attribute + '"';
BaseAssertion.call(this);
this.start();
}
util.inherits(AttributeAssertion, BaseAssertion);
AttributeAssertion.prototype.executeCommand = function(callback) {
this.protocol.elementIdAttribute(this.elementResult.ELEMENT, this.attribute, function(result) {
if (result.value !== null && result.status === 0) {
callback(result);
} else {
this.attributeNotFound();
}
}.bind(this));
};
AttributeAssertion.prototype.elementFound = function() {
if (this.retries > 0 && this.negate) {
return;
}
if (!this.hasCondition()) {
this.passed = this.negate ? false : true;
this.expected = this.negate ? 'not found' : 'found';
this.actual = 'found';
}
if (this.waitForMs && this.passed) {
var message = 'attribute was present';
if (this.hasCondition()) {
message = 'condition was met';
}
this.elapsedTime = this.getElapsedTime();
this.messageParts.push(' - ' + message + ' in ' + this.elapsedTime + 'ms');
}
};
AttributeAssertion.prototype.attributeNotFound = function() {
this.processFlags();
this.passed = this.hasCondition() ? false : this.negate;
if (!this.passed && this.shouldRetry()) {
this.scheduleRetry();
} else {
if (!this.hasCondition()) {
this.expected = this.negate ? 'not found' : 'found';
this.actual = 'not found';
}
if (!this.negate) {
this.messageParts.push(' - attribute was not found');
}
this.done();
}
};
AttributeAssertion.prototype.elementNotFound = function() {
this.passed = false;
};
module.exports = AttributeAssertion;

69
tests/node_modules/nightwatch/lib/api/expect/css.js generated vendored Normal file
View File

@@ -0,0 +1,69 @@
/**
* Checks a given css property of an element exists and optionally if it has the expected value.
*
* ```
* this.demoTest = function (browser) {
* browser.expect.element('#main').to.have.css('display');
* browser.expect.element('#main').to.have.css('display', 'Testing for display');
* browser.expect.element('#main').to.not.have.css('display');
* browser.expect.element('#main').to.have.css('display').before(100);
* browser.expect.element('#main').to.have.css('display').which.equals('block');
* browser.expect.element('#main').to.have.css('display').which.contains('some value');
* browser.expect.element('#main').to.have.css('display').which.matches(/some\ value/);
* };
* ```
*
* @method css
* @param {string} property The css property name
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.*
* @display .css(property)
* @since v0.7
* @api expect
*/
var util = require('util');
var events = require('events');
var BaseAssertion = require('./_baseAssertion.js');
function CssAssertion(property, msg) {
this.cssProperty = property;
this.flag('cssFlag', true);
BaseAssertion.call(this);
this.customMessage = typeof msg != 'undefined';
this.message = msg || 'Expected element <%s> to ' + (this.negate ? 'not have' : 'have') + ' css property "' + property + '"';
this.start();
}
util.inherits(CssAssertion, BaseAssertion);
CssAssertion.prototype.executeCommand = function(callback) {
this.protocol.elementIdCssProperty(this.elementResult.ELEMENT, this.cssProperty, callback);
};
CssAssertion.prototype['@haveFlag'] = function() {
this.passed = this.negate ? (this.resultValue === '') : (this.resultValue !== '');
this.expected = this.negate ? 'not present' : 'present';
this.actual = this.resultValue === '' ? 'not present' : 'present';
};
CssAssertion.prototype.elementFound = function() {
if (this.retries > 0 && this.negate) {
return;
}
if (this.passed && this.waitForMs) {
var message = 'property was present';
if (this.hasCondition()) {
message = 'condition was met';
}
this.elapsedTime = this.getElapsedTime();
this.messageParts.push(' - ' + message + ' in ' + this.elapsedTime + 'ms');
}
};
CssAssertion.prototype.elementNotFound = function() {
this.passed = false;
};
module.exports = CssAssertion;

View File

@@ -0,0 +1,55 @@
/**
* Property that checks if an element is currently enabled.
*
* ```
* this.demoTest = function (browser) {
* browser.expect.element('#weblogin').to.be.enabled;
* browser.expect.element('#main').to.not.be.enabled;
* browser.expect.element('#main').to.be.enabled.before(100);
* };
* ```
*
* @method enabled
* @display .enabled
* @since v0.7
* @api expect
*/
var util = require('util');
var events = require('events');
var BaseAssertion = require('./_baseAssertion.js');
function EnabledAssertion() {
BaseAssertion.call(this);
this.message = 'Expected element <%s> to ' + (this.negate ? 'not be enabled' : 'be enabled');
this.start();
}
util.inherits(EnabledAssertion, BaseAssertion);
EnabledAssertion.prototype.executeCommand = function(callback) {
this.protocol.elementIdEnabled(this.elementResult.ELEMENT, callback);
};
EnabledAssertion.prototype.elementFound = function() {
if (this.retries > 0 && this.negate) {
return;
}
this.passed = this.negate ? !this.resultValue : this.resultValue;
this.expected = this.negate ? 'not enabled' : 'enabled';
this.actual = this.resultValue ? 'enabled' : 'not enabled';
if (this.passed && this.waitForMs) {
this.elapsedTime = this.getElapsedTime();
this.messageParts.push(' - condition was met in ' + this.elapsedTime + 'ms');
}
};
EnabledAssertion.prototype.elementNotFound = function() {
this.passed = false;
this.expected = this.negate ? 'not enabled' : 'enabled';
this.actual = 'not found';
};
module.exports = EnabledAssertion;

View File

@@ -0,0 +1,71 @@
/**
* Property that checks if an element is present in the DOM.
*
* ```
* this.demoTest = function (browser) {
* browser.expect.element('#main').to.be.present;
* browser.expect.element('#main').to.not.be.present;
* browser.expect.element('#main').to.be.present.before(100);
* };
* ```
*
* @method present
* @display .present
* @since v0.7
* @api expect
*/
var util = require('util');
var events = require('events');
var BaseAssertion = require('./_baseAssertion.js');
function PresentAssertion() {
this.flag('present', true);
BaseAssertion.call(this);
this.message = 'Expected element <%s> to ' + (this.negate ? 'not be present' : 'be present');
this.start();
}
util.inherits(PresentAssertion, BaseAssertion);
PresentAssertion.prototype.executeCommand = function(callback) {
return callback(this.elementResult);
};
PresentAssertion.prototype.elementFound = function() {
this.passed = !this.negate;
if (!this.passed && this.shouldRetry()) {
return;
}
if (this.waitForMs) {
this.elapsedTime = this.getElapsedTime();
this.messageParts.push(' - element was present in ' + this.elapsedTime + 'ms');
}
if (this.negate) {
this.actual = 'present';
this.expected = 'not present';
}
};
PresentAssertion.prototype.elementNotFound = function() {
this.passed = this.negate;
if (!this.passed && this.shouldRetry()) {
return;
}
if (this.waitForMs && this.negate) {
this.messageParts.push(this.checkWaitForMsg(this.elapsedTime) + '.');
}
};
PresentAssertion.prototype.retryCommand = function() {
this.promise = this.element.createPromise();
this.element.deferred.promise.then(this.onPromiseResolved.bind(this), this.onPromiseRejected.bind(this));
this.element.locate();
};
module.exports = PresentAssertion;

View File

@@ -0,0 +1,55 @@
/**
* Property that checks if an OPTION element, or an INPUT element of type checkbox or radio button is currently selected.
*
* ```
* this.demoTest = function (browser) {
* browser.expect.element('#main').to.be.selected;
* browser.expect.element('#main').to.not.be.selected;
* browser.expect.element('#main').to.be.selected.before(100);
* };
* ```
*
* @method selected
* @display .selected
* @since v0.7
* @api expect
*/
var util = require('util');
var events = require('events');
var BaseAssertion = require('./_baseAssertion.js');
function SelectedAssertion() {
BaseAssertion.call(this);
this.message = 'Expected element <%s> to ' + (this.negate ? 'not be selected' : 'be selected');
this.start();
}
util.inherits(SelectedAssertion, BaseAssertion);
SelectedAssertion.prototype.executeCommand = function(callback) {
this.protocol.elementIdSelected(this.elementResult.ELEMENT, callback);
};
SelectedAssertion.prototype.elementFound = function() {
if (this.retries > 0 && this.negate) {
return;
}
this.passed = this.negate ? !this.resultValue : this.resultValue;
this.expected = this.negate ? 'not selected' : 'selected';
this.actual = this.resultValue ? 'selected' : 'not selected';
if (this.passed && this.waitForMs) {
this.elapsedTime = this.getElapsedTime();
this.messageParts.push(' - condition was met in ' + this.elapsedTime + 'ms');
}
};
SelectedAssertion.prototype.elementNotFound = function() {
this.passed = false;
this.expected = this.negate ? 'not selected' : 'selected';
this.actual = 'not found';
};
module.exports = SelectedAssertion;

58
tests/node_modules/nightwatch/lib/api/expect/text.js generated vendored Normal file
View File

@@ -0,0 +1,58 @@
/**
* Property that retrieves the text contained by an element. Can be chained to check if contains/equals/matches the specified text or regex.
*
* ```
* this.demoTest = function (browser) {
* browser.expect.element('#main').text.to.equal('The Night Watch');
* browser.expect.element('#main').text.to.not.equal('The Night Watch');
* browser.expect.element('#main').text.to.equal('The Night Watch').before(100);
* browser.expect.element('#main').text.to.contain('The Night Watch');
* browser.expect.element('#main').text.to.match(/The\ Night\ Watch/);
* };
* ```
*
* @method text
* @since v0.7
* @display .text
* @api expect
*/
var util = require('util');
var events = require('events');
var BaseAssertion = require('./_baseAssertion.js');
function TextAssertion() {
this.flag('textFlag', true);
BaseAssertion.call(this);
this.message = 'Expected element <%s> text to' + (this.negate ? ' not' : '');
this.start();
}
util.inherits(TextAssertion, BaseAssertion);
TextAssertion.prototype.executeCommand = function(callback) {
this.protocol.elementIdText(this.elementResult.ELEMENT, callback);
};
TextAssertion.prototype.elementFound = function() {
if (this.retries > 0 && this.negate) {
return;
}
if (this.passed && this.waitForMs) {
var message = '';
if (this.hasCondition()) {
message = 'condition was met';
}
this.elapsedTime = this.getElapsedTime();
this.messageParts.push(' - ' + message + ' in ' + this.elapsedTime + 'ms');
}
};
TextAssertion.prototype.elementNotFound = function() {
this.passed = false;
};
module.exports = TextAssertion;

60
tests/node_modules/nightwatch/lib/api/expect/type.js generated vendored Normal file
View File

@@ -0,0 +1,60 @@
/**
* Checks if the type (i.e. tag name) of a specified element is of an expected value.
*
* ```
* this.demoTest = function (browser) {
* browser.expect.element('#q').to.be.an('input');
* browser.expect.element('#q').to.be.an('input', 'Testing if #q is an input');
* browser.expect.element('#w').to.be.a('span');
* };
* ```
*
* @method a
* @display .a(type)
* @alias an
* @since v0.7
* @param {string} type The expected type
* @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default.
* @api expect
*/
var util = require('util');
var events = require('events');
var BaseAssertion = require('./_baseAssertion.js');
function TypeAssertion(type, msg) {
this.type = type;
BaseAssertion.call(this);
this.article = ['a', 'e', 'i', 'o'].indexOf(type.substring(0, 1)) > -1 ? 'an' : 'a';
this.customMessage = msg;
this.message = msg || 'Expected element <%s> to ' + (this.negate ? 'not be' : 'be') + ' ' + this.article +' ' + type;
this.start();
}
util.inherits(TypeAssertion, BaseAssertion);
TypeAssertion.prototype.executeCommand = function(callback) {
this.protocol.elementIdName(this.elementResult.ELEMENT, callback);
};
TypeAssertion.prototype.elementFound = function() {
if (this.retries > 0 && this.negate) {
return;
}
this.passed = this.negate ? (this.resultValue != this.type) : (this.resultValue == this.type);
this.expected = this.negate ? 'not be ' + this.article + ' ' + this.type : 'be ' + this.article + ' ' + this.type;
this.actual = this.resultValue;
if (this.passed && this.waitForMs) {
this.elapsedTime = this.getElapsedTime();
this.messageParts.push(' - condition was met in ' + this.elapsedTime + 'ms');
}
};
TypeAssertion.prototype.elementNotFound = function() {
this.passed = false;
};
module.exports = TypeAssertion;

55
tests/node_modules/nightwatch/lib/api/expect/value.js generated vendored Normal file
View File

@@ -0,0 +1,55 @@
/**
* Property that retrieves the value (i.e. the value attributed) of an element. Can be chained to check if contains/equals/matches the specified text or regex.
*
* ```
* this.demoTest = function (browser) {
* browser.expect.element('#q').to.have.value.that.equals('search');
* browser.expect.element('#q').to.have.value.not.equals('search');
* browser.expect.element('#q').to.have.value.which.contains('search');
* browser.expect.element('#q').to.have.value.which.matches(/search/);
* };
* ```
*
* @display .value
* @method value
* @api expect
*/
var util = require('util');
var events = require('events');
var BaseAssertion = require('./_baseAssertion.js');
function ValueAssertion() {
this.flag('valueFlag', true);
BaseAssertion.call(this);
this.message = 'Expected element <%s> to have value' + (this.negate ? ' not' : '');
this.start();
}
util.inherits(ValueAssertion, BaseAssertion);
ValueAssertion.prototype.executeCommand = function(callback) {
this.protocol.elementIdValue(this.elementResult.ELEMENT, callback);
};
ValueAssertion.prototype.elementFound = function() {
if (this.retries > 0 && this.negate) {
return;
}
if (this.passed && this.waitForMs) {
var message = '';
if (this.hasCondition()) {
message = 'condition was met';
}
this.elapsedTime = this.getElapsedTime();
this.messageParts.push(' - ' + message + ' in ' + this.elapsedTime + 'ms');
}
};
ValueAssertion.prototype.elementNotFound = function() {
this.passed = false;
};
module.exports = ValueAssertion;

View File

@@ -0,0 +1,49 @@
/**
* Property that asserts the visibility of a specified element.
*
* ```
* this.demoTest = function (browser) {
* browser.expect.element('#main').to.be.visible;
* browser.expect.element('#main').to.not.be.visible;
* browser.expect.element('#main').to.be.visible.before(100);
* };
* ```
*
* @display .visible
* @method visible
* @api expect
*/
var util = require('util');
var BaseAssertion = require('./_baseAssertion.js');
function VisibleAssertion() {
BaseAssertion.call(this);
this.message = 'Expected element <%s> to ' + (this.negate ? 'not be visible' : 'be visible');
this.start();
}
util.inherits(VisibleAssertion, BaseAssertion);
VisibleAssertion.prototype.executeCommand = function(callback) {
this.protocol.elementIdDisplayed(this.elementResult.ELEMENT, callback);
};
VisibleAssertion.prototype.elementFound = function() {
this.passed = this.negate ? this.resultValue === false : this.resultValue;
this.expected = this.negate ? 'not visible' : 'visible';
this.actual = this.resultValue ? 'visible' : 'not visible';
if (this.passed && this.waitForMs) {
this.elapsedTime = this.getElapsedTime();
this.messageParts.push(' - condition was met in ' + this.elapsedTime + 'ms');
}
};
VisibleAssertion.prototype.elementNotFound = function() {
this.passed = false;
this.expected = this.negate ? 'not visible' : 'visible';
this.actual = 'not found';
};
module.exports = VisibleAssertion;

1289
tests/node_modules/nightwatch/lib/api/protocol.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

568
tests/node_modules/nightwatch/lib/core/api.js generated vendored Normal file
View File

@@ -0,0 +1,568 @@
/*!
* Module dependencies.
*/
var fs = require('fs');
var path = require('path');
var util = require('util');
var events = require('events');
var assertModule = require('assert');
var CommandQueue = require('./queue.js');
var Assertion = require('./assertion.js');
var Page = require('../page-object/page.js');
module.exports = new (function() {
var client;
var custom_commands_path;
var custom_assertions_path;
var page_objects_path;
var assertOperators = {
ok : ['ok', 'ko'],
equal : ['==', '!='],
notEqual : ['!=', '=='],
deepEqual : ['deepEqual', 'not deepEqual'],
notDeepEqual : ['not deepEqual', 'deepEqual'],
strictEqual : ['===', '!=='],
notStrictEqual : ['!==', '==='],
throws : ['throws', 'doesNotThrow'],
doesNotThrow : ['doesNotThrow', 'throws'],
fail : 'fail',
ifError : 'ifError'
};
/////////////////////////////////////////////////////////////////////
// Assertions
/////////////////////////////////////////////////////////////////////
/**
* Extends the node.js assert module
*
* @param prop
* @param abortOnFailure
* @returns {Function}
*/
function makeAssertion(prop, abortOnFailure) {
return function() {
var passed;
var expected = null;
var actual = null;
var lastArgument = arguments[arguments.length-1];
var isLastArgString = typeof lastArgument === 'string';
var message = isLastArgString &&
(arguments.length > 2 || typeof arguments[0] === 'boolean') &&
lastArgument || (typeof arguments[0] === 'function' && '[Function]');
var args = Array.prototype.slice.call(arguments, 0);
try {
assertModule[prop].apply(null, arguments);
passed = true;
message = 'Passed [' + prop + ']: ' + (message || getAssertMessage(prop, args, passed));
} catch (ex) {
passed = false;
message = 'Failed [' + prop + ']: ' + ('(' + ex.message + ')' || message || getAssertMessage(prop, args, passed));
actual = ex.actual;
expected = ex.expected;
}
return Assertion.assert(passed, actual, expected, message, abortOnFailure);
};
}
function getAssertMessage(prop, args, passed) {
if (!Array.isArray(assertOperators[prop])) {
return assertOperators[prop] || '';
}
var operator = passed ? assertOperators[prop][0] : assertOperators[prop][1];
if (args.length === 2) {
args.splice(1, 0, operator);
} else {
args.push(operator);
}
args = args.map(function(argument) {
if (argument && typeof argument == 'object') {
argument = util.inspect(argument);
}
return argument;
});
return args.join(' ');
}
/**
* Loads the available assertions
*/
function loadAssertions(parent) {
parent = parent || client.api;
parent.assert = {};
if (client.options.start_session) {
parent.verify = {};
}
for (var prop in assertModule) {
if (assertModule.hasOwnProperty(prop)) {
parent.assert[prop] = (function(prop) {
return makeAssertion(prop, true);
})(prop);
if (client.options.start_session) {
parent.verify[prop] = (function (prop) {
return makeAssertion(prop, false);
})(prop);
}
}
}
if (client.options.start_session) {
var dirPath = path.join(__dirname, './../api/assertions/');
loadAssertionFiles(dirPath, parent.assert, true);
loadAssertionFiles(dirPath, parent.verify, false);
}
}
/**
* Create an instance of an assertion
*
* @param {string} commandName
* @param {function} assertionFn
* @param {boolean} abortOnFailure
* @param {object} parent
* @returns {AssertionInstance}
*/
function createAssertion(commandName, assertionFn, abortOnFailure, parent) {
var assertion;
if (typeof assertionFn === 'object' && assertionFn.assertion) {
assertion = Assertion.factory(assertionFn.assertion, abortOnFailure, client);
addCommand(commandName, assertion._commandFn, assertion, parent);
return assertion;
}
// backwards compatibility
var module = loadCommandModule(assertionFn, client.api, {
abortOnFailure : abortOnFailure
});
addCommand(commandName, module.command, module.context, parent);
return assertion;
}
/**
* Loads the actual assertion files.
*
* @param {String} dirPath
* @param {Object} parent
* @param {Boolean} abortOnFailure
*/
function loadAssertionFiles(dirPath, parent, abortOnFailure) {
var commandFiles = fs.readdirSync(dirPath);
for (var i = 0, len = commandFiles.length; i < len; i++) {
if (path.extname(commandFiles[i]) === '.js') {
var commandName = path.basename(commandFiles[i], '.js');
var assertionFn = require(path.join(dirPath, commandFiles[i]));
createAssertion(commandName, assertionFn, abortOnFailure, parent);
}
}
}
/////////////////////////////////////////////////////////////////////
// Commands
/////////////////////////////////////////////////////////////////////
/**
* Loads selenium protocol actions
*/
function loadProtocolActions(parent) {
parent = parent || client.api;
var protocol = require('./../api/protocol.js')(client);
var actions = Object.keys(protocol);
actions.forEach(function(command) {
addCommand(command, protocol[command], client.api, parent);
});
}
/**
* Loads all the composite commands defined by nightwatch
*/
function loadAllCommands(parent) {
loadElementCommands(parent);
loadClientCommands(parent);
loadCommandFiles(client.api, parent, true);
}
/**
* Loads the element composite commands defined by nightwatch
*/
function loadElementCommands(parent) {
parent = parent || client.api;
var elementCommands = require('./../api/element-commands.js')(client);
var entries = Object.keys(elementCommands);
entries.forEach(function(command) {
addCommand(command, elementCommands[command], client.api, parent);
});
}
/**
* Loads all the client commands defined by nightwatch
*/
function loadClientCommands(parent) {
parent = parent || client.api;
var clientCommands = require('./../api/client-commands.js')(client);
var entries = Object.keys(clientCommands);
entries.forEach(function(command) {
addCommand(command, clientCommands[command], client.api, parent, true);
});
}
/**
* Loads the external commands
*/
function loadCommandFiles(context, parent, shouldLoadClientCommands) {
var relativePaths = ['./../api/element-commands/'];
if (shouldLoadClientCommands) {
relativePaths.push('./../api/client-commands/');
}
relativePaths.forEach(function(relativePath) {
var commandFiles = fs.readdirSync(path.join(__dirname, relativePath));
var commandName;
var commandModule;
for (var i = 0, len = commandFiles.length; i < len; i++) {
var ext = path.extname(commandFiles[i]);
commandName = path.basename(commandFiles[i], ext);
if (ext === '.js' && commandName.substr(0, 1) !== '_') {
commandModule = require(__dirname + relativePath + commandFiles[i]);
var m = loadCommandModule(commandModule, context);
addCommand(commandName, m.command, m.context, parent);
}
}
});
}
/**
* Loads a command module either specified as an object with a `command` method
* or specified as a function which will be instantiated (new function() {..})
*
* @param {object|function} module
* @param {object} context
* @param {object} [addt_props]
* @returns {{command: function, context: *}}
*/
function loadCommandModule(module, context, addt_props, return_val) {
var m = {command: null, context: context};
function F() {
if (typeof module === 'object') {
events.EventEmitter.call(this);
}
if (addt_props) {
for (var prop in addt_props) {
if (addt_props.hasOwnProperty(prop)) {
this[prop] = addt_props[prop];
}
}
}
this.client = client;
this.api = client.api;
if (typeof module === 'function') {
module.call(this);
}
}
if (typeof module === 'object' && module.command) {
util.inherits(F, events.EventEmitter);
F.prototype.command = function() {
return module.command.apply(this, arguments);
};
} else if (typeof module === 'function') {
F.prototype = Object.create(module.prototype);
F.prototype.constructor = F;
}
m.command = function commandFn() {
var instance = new F();
if (typeof module === 'function') {
context = m.context = instance;
}
instance.command.prototype.constructor.stackTrace = commandFn.stackTrace;
instance.command.apply(context, arguments);
return context;
};
return m;
}
/**
* Loads custom commands defined by the user
* @param {string} [dirPath]
* @param {object} [parent]
*/
function loadCustomCommands(dirPath, parent) {
if (!custom_commands_path && !dirPath) {
return;
}
dirPath = dirPath || custom_commands_path;
parent = parent || client.api;
if (Array.isArray(dirPath)) {
dirPath.forEach(function(folder) {
loadCustomCommands(folder, parent);
});
return;
}
var absPath = path.resolve(dirPath);
var commandFiles = fs.readdirSync(absPath);
commandFiles.forEach(function(file) {
var fullPath = path.join(absPath, file);
if (fs.lstatSync(fullPath).isDirectory()) {
parent[file] = parent[file] || {};
var pathFolder = path.join(dirPath, file);
loadCustomCommands(pathFolder, parent[file]);
} else if (path.extname(file) === '.js') {
var commandModule = require(fullPath);
var name = path.basename(file, '.js');
if (!commandModule) {
throw new Error('Module ' + file + 'should have a public method or function.');
}
var m = loadCommandModule(commandModule, client.api);
addCommand(name, m.command, m.context, parent, true);
}
});
}
/**
* Loads custom assertions, similarly to custom commands
* @param [folder]
*/
function loadCustomAssertions(folder, parent) {
folder = folder || custom_assertions_path;
parent = parent || client.api;
if (!custom_assertions_path) {
return;
}
if (Array.isArray(folder)) {
folder.forEach(function(folderName) {
loadCustomAssertions(folderName, parent);
});
return;
}
loadCustomAssertionFolder(folder, parent);
}
function loadCustomAssertionFolder(folderName, parent) {
var absPath = path.resolve(folderName);
loadAssertionFiles(absPath, parent.assert, true);
loadAssertionFiles(absPath, parent.verify, false);
}
function loadExpectAssertions(parent) {
parent = parent || client.api;
var Expect = require('../api/expect.js')(client);
var assertions = Object.keys(Expect);
try {
var chaiExpect = module.require('chai').expect;
parent.expect = function() {
return chaiExpect.apply(chaiExpect, arguments);
};
} catch (err) {
parent.expect = {};
}
assertions.forEach(function(assertion) {
parent.expect[assertion] = function() {
var args = Array.prototype.slice.call(arguments);
var command = Expect[assertion].apply(parent, args);
function F(element) {
events.EventEmitter.call(this);
this.client = client;
this.element = element;
}
util.inherits(F, events.EventEmitter);
F.prototype.command = function commandFn() {
this.element._stackTrace = commandFn.stackTrace;
this.element.locate(this);
return this;
};
var instance = new F(command.element);
var err = new Error;
Error.captureStackTrace(err, arguments.callee);
CommandQueue.add(assertion, instance.command, instance, [], err.stack);
return command.expect;
};
});
}
/**
* Loads page object files
* @param {string} [dirPath]
*/
function loadPageObjects(dirPath, parent) {
if (!page_objects_path && !dirPath) {
return;
}
dirPath = dirPath || page_objects_path;
client.api.page = client.api.page || {};
parent = parent || client.api.page;
if (Array.isArray(dirPath)) {
dirPath.forEach(function(folder) {
loadPageObjects(folder);
});
return;
}
var absPath = path.resolve(dirPath);
var pageFiles = fs.readdirSync(absPath);
pageFiles.forEach(function(file) {
var fullPath = path.join(absPath, file);
if (fs.lstatSync(fullPath).isDirectory()) {
parent[file] = parent[file] || {};
var pathFolder = path.join(dirPath, file);
loadPageObjects(pathFolder, parent[file]);
} else if (path.extname(file) === '.js') {
var pageName = path.basename(file, '.js');
var pageFnOrObject = require(path.join(absPath, file));
addPageObject(pageName, pageFnOrObject, client.api, parent);
}
});
}
/**
* Instantiates the page object class
* @param {String} name
* @param {Object} pageFnOrObject
* @param {Object} context
* @param {Object} parent
*/
function addPageObject(name, pageFnOrObject, context, parent) {
parent[name] = function() {
var args = Array.prototype.slice.call(arguments);
args.unshift(context);
if (useEnhancedModel(pageFnOrObject)) {
var loadOntoPageObject = function(parent) {
if (client.options.start_session) {
loadElementCommands(parent);
loadCommandFiles(client.api, parent, false);
loadExpectAssertions(parent);
// Alias
parent.expect.section = parent.expect.element;
}
loadAssertions(parent);
loadCustomCommands(null, parent);
loadCustomAssertions(null, parent);
return parent;
};
pageFnOrObject.name = name;
return new Page(pageFnOrObject, loadOntoPageObject, context, client);
}
return new (function() {
if (typeof pageFnOrObject == 'function') {
return createPageObject(pageFnOrObject, args);
}
return pageFnOrObject;
})();
};
}
function useEnhancedModel(pageFnOrObject) {
return typeof pageFnOrObject == 'object' && (pageFnOrObject.elements || pageFnOrObject.sections);
}
/**
*
* @param pageFnOrObject
* @param args
* @returns {Object}
*/
function createPageObject(pageFnOrObject, args) {
function PageObject() {
return pageFnOrObject.apply(this, args);
}
PageObject.prototype = pageFnOrObject.prototype;
return new PageObject();
}
/**
* Adds a command/assertion to the queue.
*
* @param {String} name
* @param {Object} command
* @param {Object} context
* @param {Object} [parent]
*/
function addCommand(name, command, context, parent) {
parent = parent || client.api;
if (parent[name]) {
client.results.errors++;
var error = new Error('The command "' + name + '" is already defined!');
client.errors.push(error.stack);
throw error;
}
parent[name] = function commandFn() {
var args = Array.prototype.slice.call(arguments);
var originalStackTrace;
if (commandFn.stackTrace) {
originalStackTrace = commandFn.stackTrace;
} else {
var err = new Error;
Error.captureStackTrace(err, arguments.callee);
originalStackTrace = err.stack;
}
CommandQueue.add(name, command, context, args, originalStackTrace);
return client.api; // for chaining
};
}
/**
* Initialize the api
*
* @param {Object} c The nightwatch client instance
* @api public
*/
this.init = function(c) {
client = c;
custom_commands_path = c.options.custom_commands_path;
custom_assertions_path = c.options.custom_assertions_path;
page_objects_path = c.options.page_objects_path;
return this;
};
/**
* Loads everything
*/
this.load = function() {
if (client.options.start_session) {
loadProtocolActions();
loadAllCommands();
loadPageObjects();
loadExpectAssertions();
}
loadAssertions();
loadCustomCommands();
loadCustomAssertions();
return this;
};
this.addCommand = addCommand;
this.loadCustomCommands = loadCustomCommands;
this.loadCustomAssertions = loadCustomAssertions;
this.loadPageObjects = loadPageObjects;
this.createAssertion = createAssertion;
})();

275
tests/node_modules/nightwatch/lib/core/assertion.js generated vendored Normal file
View File

@@ -0,0 +1,275 @@
var util = require('util');
var events = require('events');
var Logger = require('./../util/logger.js');
var Utils = require('./../util/utils.js');
module.exports = new (function() {
var doneSymbol = Utils.symbols.ok;
var failSymbol = Utils.symbols.fail;
var initialized = false;
var client;
/**
* Abstract assertion class that will subclass all defined assertions
*
* All assertions must implement the following api:
*
* - @type {boolean|function}
* expected
* - @type {string}
* message
* - @type {function}
* pass
* - @type {function}
* value
* - @type {function}
* command
* - @type {function} - Optional
* failure
*
* @param {boolean} abortOnFailure
* @param {Nightwatch} client
* @constructor
*/
function BaseAssertion(abortOnFailure, client) {
events.EventEmitter.call(this);
this.abortOnFailure = abortOnFailure;
this.client = client;
this.api = client.api;
this.startTime = new Date().getTime();
this.globals = this.api.globals || {};
this.timeout = this.globals.retryAssertionTimeout || 0; //ms
this.rescheduleInterval = this.globals.waitForConditionPollInterval || 500; //ms
this.shouldRetry = this.timeout > 0;
}
util.inherits(BaseAssertion, events.EventEmitter);
BaseAssertion.prototype.complete = function() {
var self = this, args = Array.prototype.slice.call(arguments, 0);
args.unshift('complete');
setImmediate(function() {
self.emit.apply(self, args);
});
};
/**
* Performs the command method
* @returns {*}
* @private
*/
BaseAssertion.prototype._executeCommand = function() {
var self = this;
var methods = [
'expected',
'message',
['pass'],
['value'],
['command']
];
methods.forEach(function(method) {
if (Array.isArray(method)) {
var name = method[0];
if (typeof self[name] !== 'function') {
throw new Error('Assertion must implement method ' + name);
}
} else if (typeof self[method] == 'undefined') {
throw new Error('Assertion must implement method/property ' + method);
}
});
return this._scheduleAssertion();
};
BaseAssertion.prototype._scheduleAssertion = function() {
var self = this;
return this.command(function(result) {
var passed, value;
if (typeof self.failure == 'function' && self.failure(result)) {
passed = false;
value = null;
} else {
value = self.value(result);
passed = self.pass(value);
}
var timeSpent = new Date().getTime() - self.startTime;
if (!passed && timeSpent < self.timeout) {
return self._reschedule();
}
var expected = typeof self.expected == 'function' ? self.expected() : self.expected;
var message = self._getFullMessage(passed, timeSpent);
self.client.assertion(passed, value, expected, message, self.abortOnFailure, self._stackTrace);
self.emit('complete');
});
};
BaseAssertion.prototype._getFullMessage = function(passed, timeSpent) {
if ( !this.shouldRetry) {
return this.message;
}
var timeLogged = passed ? timeSpent : this.timeout;
return this.message + ' after ' + timeLogged + ' milliseconds.';
};
BaseAssertion.prototype._reschedule = function() {
setTimeout(function(){}, this.rescheduleInterval);
return this._scheduleAssertion();
};
/**
*
* @param {string} stackTrace
* @param {string|null} message
*/
function buildStackTrace(stackTrace, message) {
var stackParts = stackTrace.split('\n');
stackParts.shift();
if (message) {
stackParts.unshift(message);
}
return Utils.stackTraceFilter(stackParts);
}
/**
* Assertion factory that creates the assertion instances with the supplied assertion definition
* and options
*
* @param {function} assertionFn
* @param {boolean} abortOnFailure
* @param {Nightwatch} client
* @constructor
*/
function AssertionInstance(assertionFn, abortOnFailure, client) {
this.abortOnFailure = abortOnFailure;
this.client = client;
this.assertionFn = assertionFn;
}
/**
* This will call the supplied constructor of the assertion, after calling the Base constructor
* first with other arguments and then inherits the rest of the methods from BaseAssertion
*
* @param {function} constructor
* @param {Array} args
* @returns {*}
* @private
*/
AssertionInstance.prototype._constructFromSuper = function(constructor, args) {
var self = this;
function F() {
BaseAssertion.apply(this, [self.abortOnFailure, self.client]);
return constructor.apply(this, args);
}
util.inherits(constructor, BaseAssertion);
F.prototype = constructor.prototype;
return new F();
};
AssertionInstance.prototype._commandFn = function commandFn() {
var args = Array.prototype.slice.call(arguments, 0);
var instance = this._constructFromSuper(this.assertionFn, args);
instance._stackTrace = commandFn.stackTrace;
return instance._executeCommand();
};
/**
* @public
* @param {function} assertionFn
* @param {boolean} abortOnFailure
* @param {Nightwatch} client
* @returns {AssertionInstance}
*/
this.factory = function(assertionFn, abortOnFailure, client) {
return new AssertionInstance(assertionFn, abortOnFailure, client);
};
/**
* Performs an assertion
*
* @param {Boolean} passed
* @param {Object} receivedValue
* @param {Object} expectedValue
* @param {String} message
* @param {Boolean} abortOnFailure
* @param {String} originalStackTrace
*/
this.assert = function assert(passed, receivedValue, expectedValue, message, abortOnFailure, originalStackTrace) {
if (!initialized) {
throw new Error('init must be called first.');
}
var failure = '';
var stacktrace = '';
var fullMsg = '';
if (passed) {
if (client.options.output && client.options.detailed_output) {
console.log(' ' + Logger.colors.green(doneSymbol) + ' ' + message);
}
client.results.passed++;
} else {
failure = 'Expected "' + expectedValue + '" but got: "' + receivedValue + '"';
var err = new Error();
err.name = message;
err.message = message + ' - ' + failure;
if (!originalStackTrace) {
Error.captureStackTrace(err, arguments.callee);
originalStackTrace = err.stack;
}
err.stack = buildStackTrace(originalStackTrace, client.options.start_session ? null : 'AssertionError: ' + message);
fullMsg = message;
if (client.options.output && client.options.detailed_output) {
var logged = ' ' + Logger.colors.red(failSymbol);
if (typeof expectedValue != 'undefined' && typeof receivedValue != 'undefined') {
fullMsg += ' ' + Logger.colors.white(' - expected ' + Logger.colors.green('"' +
expectedValue + '"')) + ' but got: ' + Logger.colors.red('"' + receivedValue + '"');
}
logged += ' ' + fullMsg;
console.log(logged);
}
stacktrace = err.stack;
if (client.options.output && client.options.detailed_output) {
var parts = stacktrace.split('\n');
console.log(Logger.colors.stack_trace(parts.join('\n')) + '\n');
}
client.results.lastError = err;
client.results.failed++;
}
client.results.tests.push({
message : message,
stackTrace : stacktrace,
fullMsg : fullMsg,
failure : failure !== '' ? failure : false
});
if (!passed && abortOnFailure) {
client.terminate(true);
}
};
/**
* Initializer
*
* @param {Object} c Nightwatch client instance
*/
this.init = function(c) {
client = c;
initialized = true;
};
})();

232
tests/node_modules/nightwatch/lib/core/queue.js generated vendored Normal file
View File

@@ -0,0 +1,232 @@
var util = require('util');
var events = require('events');
var Logger = require('./../util/logger.js');
function AsyncTree() {
events.EventEmitter.call(this);
this.rootNode = {
name : '__root__',
children : [],
started : false,
done : false,
parent : this.rootNode
};
this.currentNode = this.rootNode;
}
util.inherits(AsyncTree, events.EventEmitter);
AsyncTree.prototype.append = function(nodeName, command, context, args, stackTrace) {
var node = {
startTime : null,
name : nodeName,
command : command,
context : context,
args : args || [],
started : false,
done : false,
children : [],
parent : this.currentNode,
stackTrace : stackTrace
};
this.currentNode.children.push(node);
if (this.currentNode.started && !this.currentNode.done) {
this.scheduleTraverse(node);
}
};
AsyncTree.prototype.print = function(node, level) {
process.stdout.write(node.name + '\n');
level = level || 1;
for (var i = 0; i < node.children.length; i++) {
var childNode = node.children[i];
for (var k = 0; k < level; k++) {
process.stdout.write(' |- ');
}
if (childNode.children.length) {
var levelUp = level + 1;
arguments.callee(childNode, levelUp);
} else {
process.stdout.write(childNode.name + '\n');
}
}
process.stdout.write('');
};
AsyncTree.prototype.scheduleTraverse = function(node) {
if (this.scheduled) {
return this;
}
this.scheduled = true;
var self = this;
process.nextTick(function() {
self.scheduled = false;
self.traverse();
});
};
AsyncTree.prototype.traverse = function() {
this.emit('queue:started');
this.currentNode.started = true;
this.walkDown(this.currentNode);
return this;
};
AsyncTree.prototype.walkDown = function walkDown(context) {
var node = this.getNextChild(context);
if (node) {
this.runChildNode(node);
} else {
if (context.instance && !context.done) {
return;
}
this.currentNode.done = true;
if (this.currentNode.name === '__root__') {
this.done();
} else {
this.walkUp(context);
}
}
};
AsyncTree.prototype.walkUp = function(context) {
this.currentNode = context.parent;
this.walkDown(context.parent);
};
AsyncTree.prototype.getNextChild = function(context) {
for (var i = 0; i < context.children.length; i++) {
if (!context.children[i].started) {
var child = context.children[i];
context.children.splice(i, 1);
return child;
}
}
return false;
};
AsyncTree.prototype.runChildNode = function runChildNode(node) {
var self = this;
this.runCommand(node, function onCommandComplete() {
var timems = new Date().getTime() - node.startTime;
// checking if new children have been added while running this command which haven't finished yet
var childrenInProgress = false;
var currentChildNode = null;
for (var i = 0; i < node.children.length; i++) {
currentChildNode = node.children[i];
if (!currentChildNode.done) {
childrenInProgress = true;
break;
}
}
Logger.log(' ' + Logger.colors.green('→') +
' Completed command ' + Logger.colors.light_green(node.name), '(' + timems, 'ms)');
node.done = true;
if (!childrenInProgress) {
self.traverse();
}
});
};
AsyncTree.prototype.runCommand = function(node, callback) {
this.currentNode = node;
node.started = true;
try {
var commandFn = node.command;
if (typeof node.command !== 'function') {
// backwards compatibility
commandFn = node.command.command;
}
if (typeof commandFn !== 'function') {
throw new Error('Command must be a function');
}
node.startTime = new Date().getTime();
commandFn.prototype.constructor.stackTrace = node.stackTrace;
var instance = commandFn.apply(node.context, node.args);
if (instance instanceof events.EventEmitter) {
node.instance = instance;
instance.once('complete', callback);
}
return node.context;
} catch (err) {
err.stack = node.stackTrace;
err.name = 'Error while running ' + node.name + ' command';
this.emit('error', err);
return this;
}
};
AsyncTree.prototype.done = function() {
this.rootNode.started = false;
this.emit('queue:finished');
return this;
};
AsyncTree.prototype.empty = function() {
this.rootNode.children = [];
return this;
};
AsyncTree.prototype.reset = function() {
this.rootNode.started = false;
this.rootNode.done = false;
this.removeAllListeners();
this.currentNode = this.rootNode;
return this;
};
module.exports = new (function() {
var queue = new AsyncTree();
this.reset = function() {
queue.reset();
};
this.empty = function() {
queue.empty();
};
this.add = function() {
queue.append.apply(queue, arguments);
};
this.run = function queueRunner(callback) {
if (queue.rootNode.started) {
return queue;
}
if (callback) {
queue.once('queue:finished', function() {
callback(null);
})
.on('error', function(err) {
callback(err);
});
}
return queue.traverse();
};
this.done = function() {
queue.done();
};
this.instance = function instance() {
return queue;
};
this.list = function list() {
return queue.rootNode.children.map(function(item) {
return item.name;
});
};
})();

299
tests/node_modules/nightwatch/lib/http/request.js generated vendored Normal file
View File

@@ -0,0 +1,299 @@
var util = require('util');
var events = require('events');
var http = require('http');
var https = require('https');
var Logger = require('./../util/logger');
var format = util.format;
module.exports = (function() {
var Settings = {
selenium_host : 'localhost',
selenium_port : 4444,
default_path : '/wd/hub',
credentials : null,
use_ssl : false,
proxy : null
};
var DO_NOT_LOG_ERRORS = [
'Unable to locate element',
'{"errorMessage":"Unable to find element',
'no such element'
];
function HttpRequest(options) {
events.EventEmitter.call(this);
this.setOptions(options);
}
util.inherits(HttpRequest, events.EventEmitter);
HttpRequest.prototype.setOptions = function(options) {
this.data = options.data && jsonStringify(options.data) || '';
this.contentLength = this.data.length;
this.reqOptions = this.createOptions(options);
this.hostname = formatHostname(this.reqOptions.host, this.reqOptions.port);
this.request = null;
return this;
};
HttpRequest.prototype.setPathPrefix = function(options) {
this.defaultPathPrefix = options.path && options.path.indexOf(Settings.default_path) === -1 ?
Settings.default_path : '';
return this;
};
HttpRequest.prototype.createOptions = function(options) {
this.setPathPrefix(options);
var reqOptions = {
path : this.defaultPathPrefix + (options.path || ''),
host : options.host || Settings.selenium_host,
port : options.selenium_port || Settings.selenium_port,
method : options.method || 'POST',
headers : {}
};
var requestMethod = reqOptions.method.toUpperCase();
if (options.sessionId) {
reqOptions.path = reqOptions.path.replace(':sessionId', options.sessionId);
}
if (requestMethod === 'GET') {
reqOptions.headers['Accept'] = 'application/json';
}
if (this.contentLength > 0) {
reqOptions.headers['Content-Type'] = 'application/json; charset=utf-8';
}
if (needsContentLengthHeader(requestMethod)) {
reqOptions.headers['Content-Length'] = this.contentLength;
}
if (Settings.credentials &&
Settings.credentials.username && Settings.credentials.key
) {
var authHeader = new Buffer(Settings.credentials.username + ':' + Settings.credentials.key).toString('base64');
reqOptions.headers['Authorization'] = 'Basic ' + authHeader;
}
if (Settings.proxy) {
var ProxyAgent = require('proxy-agent');
var proxyUri = Settings.proxy;
reqOptions.agent = new ProxyAgent(proxyUri);
}
return reqOptions;
};
HttpRequest.prototype.send = function() {
var self = this;
var startTime = new Date();
this.request = (Settings.use_ssl ? https: http).request(this.reqOptions, function (response) {
response.setEncoding('utf8');
var redirected = false;
if (isRedirect(response.statusCode)) {
redirected = true;
}
var flushed = '';
response.on('data', function (chunk) {
if (self.reqOptions.method !== 'HEAD') {
flushed += chunk;
}
});
response.on('end', function () {
var elapsedTime = new Date() - startTime;
var screenshotContent;
var result, errorMessage = '';
if (flushed) {
result = parseResult(flushed);
if (result.value) {
if (result.value.screen) {
screenshotContent = result.value.screen;
delete result.value.screen;
}
if (result.value.stackTrace) {
// Selenium stack traces won't help us here and they will pollute the output
delete result.value.stackTrace;
}
if (needsFormattedErrorMessage(result)) {
errorMessage = formatErrorMessage(result.value);
delete result.value.localizedMessage;
delete result.value.message;
}
}
} else {
result = {};
}
if (errorMessage !== '') {
console.log(Logger.colors.yellow('There was an error while executing the Selenium command') +
(!Logger.isEnabled() ? ' - enabling the --verbose option might offer more details.' : '')
);
console.log(errorMessage);
}
self.emit('beforeResult', result);
var base64Data;
if (result.suppressBase64Data) {
base64Data = result.value;
result.value = '';
}
var logMethod = response.statusCode.toString().indexOf('5') === 0 ? 'error' : 'info';
Logger[logMethod](util.format('Response %s %s %s (%sms) ', response.statusCode, self.reqOptions.method, self.hostname + self.reqOptions.path, elapsedTime), result);
if (result.suppressBase64Data) {
result.value = base64Data;
}
if (response.statusCode.toString().indexOf('2') === 0 || redirected) {
self.emit('success', result, response, redirected);
} else {
self.emit('error', result, response, screenshotContent);
}
});
});
this.request.on('error', function(response) {
self.emit('error', {}, response);
});
Logger.info('Request: ' + this.reqOptions.method + ' ' + this.hostname + this.reqOptions.path,
'\n - data: ', this.data, '\n - headers: ', JSON.stringify(this.reqOptions.headers));
this.request.write(this.data);
this.request.end();
return this;
};
/**
*
* @param s
* @param emit_unicode
* @returns {string}
*/
HttpRequest.JSON_stringify = function(s, emit_unicode) {
var json = JSON.stringify(s);
if (json) {
return emit_unicode ? json : json.replace(jsonRegex, jsonRegexReplace);
}
};
HttpRequest.setSeleniumPort = function(port) {
Settings.selenium_port = port;
};
HttpRequest.useSSL = function(value) {
Settings.use_ssl = value;
};
HttpRequest.setSeleniumHost = function(host) {
Settings.selenium_host = host;
};
HttpRequest.setCredentials = function(credentials) {
Settings.credentials = credentials;
};
HttpRequest.setProxy = function(proxy) {
Settings.proxy = proxy;
};
HttpRequest.setDefaultPathPrefix = function(path) {
Settings.default_path = path;
};
///////////////////////////////////////////////////////////
// Helpers
///////////////////////////////////////////////////////////
var jsonRegex = new RegExp('[\\u007f-\\uffff]', 'g');
var jsonRegexReplace = function(c) {
return '\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4);
};
/**
* Built in JSON.stringify() will return unicode characters that require UTF-8 encoding on the wire.
* This function will replace unicode characters with their escaped (ASCII-safe) equivalents to support
* the keys sending command.
*
* @param {object} s
* @returns {string}
*/
function jsonStringify(s) {
var json = JSON.stringify(s);
if (json) {
return json.replace(jsonRegex, jsonRegexReplace);
}
return json;
}
function formatHostname(hostname, port) {
var isLocalHost = ['127.0.0.1', 'localhost'].indexOf(hostname) > -1;
var protocol = Settings.use_ssl ? 'https://' : 'http://';
var isPortDefault = [80, 443].indexOf(port) > -1;
if (isLocalHost) {
return '';
}
return protocol + hostname + (!isPortDefault && (':' + port) || '');
}
function isRedirect(statusCode) {
return [302, 303, 304].indexOf(statusCode) > -1;
}
function needsContentLengthHeader(requestMethod) {
return ['POST', 'DELETE'].indexOf(requestMethod) > -1;
}
function needsFormattedErrorMessage(result) {
return !!(result.localizedMessage || result.message);
}
function hasLocalizedMessage(result) {
return !!result.localizedMessage;
}
function formatErrorMessage(info) {
var msg = hasLocalizedMessage(info) ? info.localizedMessage : info.message;
if (shouldLogErrorMessage(msg)) {
msg = msg.replace(/\n/g, '\n\t');
}
return msg;
}
function parseResult(data) {
var result;
data = stripUnknownChars(data);
try {
result = JSON.parse(data);
} catch (err) {
console.log(Logger.colors.red('Error processing the server response:'), '\n', data);
result = {value: -1, error: err.message};
}
return result;
}
function shouldLogErrorMessage(msg) {
return !DO_NOT_LOG_ERRORS.some(function(item) {
return msg.indexOf(item) === 0;
});
}
function stripUnknownChars(str) {
var x = [], i = 0, length = str.length;
for (i; i < length; i++) {
if (str.charCodeAt(i)) {
x.push(str.charAt(i));
}
}
return x.join('');
}
return HttpRequest;
})();

583
tests/node_modules/nightwatch/lib/index.js generated vendored Normal file
View File

@@ -0,0 +1,583 @@
/*!
* Module dependencies.
*/
var path = require('path');
var util = require('util');
var events = require('events');
var HttpRequest = require('./http/request.js');
var CommandQueue = require('./core/queue.js');
var Assertion = require('./core/assertion.js');
var Logger = require('./util/logger.js');
var Api = require('./core/api.js');
var Utils = require('./util/utils.js');
function Nightwatch(options) {
events.EventEmitter.call(this);
this.locateStrategy = 'css selector';
this.api = {
capabilities : {},
globals : options && options.persist_globals && options.globals || {},
sessionId : null
};
this.setMaxListeners(0);
this.sessionId = null;
this.context = null;
this.terminated = false;
this.setOptions(options)
.setCapabilities()
.loadKeyCodes();
this.errors = [];
this.results = {
passed:0,
failed:0,
errors:0,
skipped:0,
tests:[]
};
Assertion.init(this);
Api.init(this).load();
this.queue = CommandQueue;
this.queue.empty();
this.queue.reset();
}
Nightwatch.DEFAULT_CAPABILITIES = {
browserName: 'firefox',
javascriptEnabled: true,
acceptSslCerts: true,
platform: 'ANY'
};
util.inherits(Nightwatch, events.EventEmitter);
Nightwatch.prototype.assertion = Assertion.assert;
Nightwatch.prototype.setOptions = function(options) {
this.options = {};
this.api.options = {};
if (options && typeof options == 'object') {
for (var propName in options) {
this.options[propName] = options[propName];
}
}
this.api.launchUrl = this.options.launchUrl || this.options.launch_url || null;
// backwords compatibility
this.api.launch_url = this.api.launchUrl;
if (this.options.globals && typeof this.options.globals == 'object' && !this.options.persist_globals) {
for (var globalKey in this.options.globals) {
this.api.globals[globalKey] = this.options.globals[globalKey];
}
}
var screenshots = this.options.screenshots;
var screenshotsEnabled = screenshots && screenshots.enabled || false;
this.api.options.screenshots = screenshotsEnabled;
if (screenshotsEnabled) {
if (typeof screenshots.path == 'undefined') {
throw new Error('Please specify the screenshots.path in nightwatch.json.');
}
this.options.screenshots.on_error = this.options.screenshots.on_error ||
(typeof this.options.screenshots.on_error == 'undefined');
this.api.screenshotsPath = this.api.options.screenshotsPath = screenshots.path;
} else {
this.options.screenshots = {
enabled : false,
path : ''
};
}
if (this.options.use_xpath) {
this.locateStrategy = 'xpath';
}
if (this.options.silent) {
Logger.disable();
} else {
Logger.enable();
}
this.options.start_session = this.options.start_session || (typeof this.options.start_session == 'undefined');
this.api.options.skip_testcases_on_fail = this.options.skip_testcases_on_fail ||
(typeof this.options.skip_testcases_on_fail == 'undefined' && this.options.start_session); // off by default for unit tests
this.api.options.log_screenshot_data = this.options.log_screenshot_data ||
(typeof this.options.log_screenshot_data == 'undefined');
var seleniumPort = this.options.seleniumPort || this.options.selenium_port;
var seleniumHost = this.options.seleniumHost || this.options.selenium_host;
var useSSL = this.options.useSsl || this.options.use_ssl;
var proxy = this.options.proxy;
if (seleniumPort) {
HttpRequest.setSeleniumPort(seleniumPort);
}
if (seleniumHost) {
HttpRequest.setSeleniumHost(seleniumHost);
}
if (useSSL) {
HttpRequest.useSSL(true);
}
if (proxy) {
HttpRequest.setProxy(proxy);
}
if (typeof this.options.default_path_prefix == 'string') {
HttpRequest.setDefaultPathPrefix(this.options.default_path_prefix);
}
var username = this.options.username;
var key = this.options.accesKey || this.options.access_key || this.options.password;
if (username && key) {
this.api.options.username = username;
this.api.options.accessKey = key;
HttpRequest.setCredentials({
username : username,
key : key
});
}
this.endSessionOnFail(typeof this.options.end_session_on_fail == 'undefined' || this.options.end_session_on_fail);
return this;
};
Nightwatch.prototype.endSessionOnFail = function(value) {
if (typeof value == 'undefined') {
return this.options.end_session_on_fail;
}
this.options.end_session_on_fail = value;
return this;
};
Nightwatch.prototype.setCapabilities = function() {
this.desiredCapabilities = {};
for (var capability in Nightwatch.DEFAULT_CAPABILITIES) {
this.desiredCapabilities[capability] = Nightwatch.DEFAULT_CAPABILITIES[capability];
}
if (this.options.desiredCapabilities) {
for (var prop in this.options.desiredCapabilities) {
if (this.options.desiredCapabilities.hasOwnProperty(prop)) {
this.desiredCapabilities[prop] = this.options.desiredCapabilities[prop];
}
}
}
this.api.options.desiredCapabilities = this.desiredCapabilities;
return this;
};
Nightwatch.prototype.loadKeyCodes = function() {
this.api.Keys = require('./util/keys.json');
return this;
};
Nightwatch.prototype.start = function() {
if (!this.sessionId && this.options.start_session) {
this
.once('selenium:session_create', this.start)
.startSession();
return this;
}
var self = this;
this.queue.reset();
this.queue.run(function(error) {
if (error) {
var stackTrace = '';
if (error.stack) {
stackTrace = error.stack.split('\n').slice(1).join('\n');
}
self.results.errors++;
self.errors.push(error.name + ': ' + error.message + '\n' + stackTrace);
if (self.options.output) {
Utils.showStackTraceWithHeadline(error.name + ': ' + error.message, stackTrace, true);
}
if (self.options.start_session) {
self.terminate();
}
return;
}
self.finished();
});
return this;
};
Nightwatch.prototype.terminate = function(deferred) {
// in case this was a synchronous command (e.g. assert.ok()) we need to wait for other possible
// commands which might have been added afterwards while client is terminated
if (deferred) {
this.queue.instance().once('queue:started', this.terminateSession.bind(this));
} else {
this.terminateSession();
}
this.terminated = true;
return this;
};
Nightwatch.prototype.resetTerminated = function() {
this.terminated = false;
return this;
};
Nightwatch.prototype.terminateSession = function() {
this.queue.reset();
this.queue.empty();
if (this.options.end_session_on_fail && this.options.start_session) {
this.api.end(function() {
this.finished();
}.bind(this));
// FIXME: sometimes the queue is incorrectly restarted when another .end() is
// scheduled from globalBeforeEach and results into a session command being sent with
// null as the sessionId
this.queue.run();
} else {
this.finished();
}
return this;
};
Nightwatch.prototype.complete = function() {
return this.emit('complete');
};
Nightwatch.prototype.finished = function() {
Logger.info('FINISHED');
this.emit('nightwatch:finished', this.results, this.errors);
return this;
};
Nightwatch.prototype.getFailureMessage = function() {
var errors = '';
var failure_msg = [];
if (this.results.failed > 0) {
failure_msg.push(Logger.colors.red(this.results.failed) +
' assertions failed');
}
if (this.results.errors > 0) {
failure_msg.push(Logger.colors.red(this.results.errors) + ' errors');
}
if (this.results.passed > 0) {
failure_msg.push(Logger.colors.green(this.results.passed) + ' passed');
}
if (this.results.skipped > 0) {
failure_msg.push(Logger.colors.blue(this.results.skipped) + ' skipped');
}
return failure_msg.join(', ').replace(/,([^,]*)$/g, function($0, $1) {
return ' and' + $1;
});
};
Nightwatch.prototype.printResult = function(elapsedTime) {
if (this.options.output && this.options.start_session) {
var ok = false;
if (this.results.failed === 0 && this.results.errors === 0) {
ok = true;
}
if (ok && this.results.passed > 0) {
console.log('\n' + Logger.colors.green('OK.'),
Logger.colors.green(this.results.passed) + ' assertions passed. (' + Utils.formatElapsedTime(elapsedTime, true) + ')');
} else if (ok && this.results.passed === 0) {
if (this.options.start_session) {
console.log(Logger.colors.green('No assertions ran.'));
}
} else {
var failure_msg = this.getFailureMessage();
console.log('\n' + Logger.colors.red('FAILED: '), failure_msg, '(' + Utils.formatElapsedTime(elapsedTime, true) + ')');
}
}
};
Nightwatch.prototype.clearResult = function() {
this.errors.length = 0;
this.results.passed = 0;
this.results.failed = 0;
this.results.errors = 0;
this.results.skipped = 0;
this.results.tests.length = 0;
};
Nightwatch.prototype.handleException = function(err) {
var stack = err.stack.split('\n');
var failMessage = stack.shift();
var firstLine = ' ' + String.fromCharCode(10006) + ' ' + failMessage;
if (typeof err.actual != 'undefined' && typeof err.expected != 'undefined') {
firstLine += '\033[0;90m - expected ' + Logger.colors.green('"' + err.expected + '"') + ' \033[0;90mbut got: ' + Logger.colors.red('"' + err.actual + '"');
}
if (this.options.output) {
Utils.showStackTraceWithHeadline(firstLine, stack);
}
if (err.name == 'AssertionError') {
this.results.failed++;
stack.unshift(failMessage + ' - expected "' + err.expected + '" but got: "' + err.actual + '"');
this.results.stackTrace = stack.join('\n');
} else {
this.addError('\n ' + err.stack, firstLine);
this.terminate();
}
};
Nightwatch.prototype.runProtocolAction = function(requestOptions, callback) {
var self = this;
var request = new HttpRequest(requestOptions)
.on('result', function(result) {
if (typeof callback != 'function') {
var error = new Error('Callback parameter is not a function - ' + typeof(callback) + ' passed: "' + callback + '"');
self.errors.push(error.stack);
self.results.errors++;
} else {
callback.call(self.api, result);
}
if (result.lastScreenshotFile && self.results.tests.length > 0) {
var lastTest = self.results.tests[self.results.tests.length-1];
lastTest.screenshots = lastTest.screenshots || [];
lastTest.screenshots.push(result.lastScreenshotFile);
delete result.lastScreenshotFile;
}
request.emit('complete');
})
.on('success', function(result, response) {
if (result.status && result.status !== 0) {
result = self.handleTestError(result);
}
request.emit('result', result, response);
})
.on('error', function(result, response, screenshotContent) {
result = self.handleTestError(result);
if (screenshotContent && self.options.screenshots.on_error) {
var fileNamePath = Utils.getScreenshotFileName(self.api.currentTest, true, self.options.screenshots.path);
self.saveScreenshotToFile(fileNamePath, screenshotContent);
result.lastScreenshotFile = fileNamePath;
}
request.emit('result', result, response);
});
return request;
};
Nightwatch.prototype.addError = function(message, logMessage) {
var currentTest;
if (this.api.currentTest) {
currentTest = '[' + Utils.getTestSuiteName(this.api.currentTest.module) + ' / ' + this.api.currentTest.name + ']';
} else {
currentTest = 'tests';
}
this.errors.push(' Error while running '+ currentTest + ':\n' + message);
this.results.errors++;
if (this.options.output) {
Logger.warn(' ' + (logMessage || message));
}
};
Nightwatch.prototype.saveScreenshotToFile = function(fileName, content, cb) {
var mkpath = require('mkpath');
var fs = require('fs');
var self = this;
cb = cb || function() {};
var dir = path.resolve(fileName, '..');
var fail = function(err) {
if (self.options.output) {
console.log(Logger.colors.yellow('Couldn\'t save screenshot to '), fileName);
}
Logger.warn(err);
cb(err);
};
mkpath(dir, function(err) {
if (err) {
fail(err);
} else {
fs.writeFile(fileName, content, 'base64', function(err) {
if (err) {
fail(err);
} else {
cb(null, fileName);
}
});
}
});
};
Nightwatch.prototype.handleTestError = function(result) {
var errorMessage = '';
if (result && result.status) {
var errorCodes = require('./api/errors.json');
errorMessage = errorCodes[result.status] && errorCodes[result.status].message || '';
}
return {
status: -1,
value : result && result.value || null,
errorStatus: result && result.status || '',
error : errorMessage
};
};
Nightwatch.prototype.startSession = function () {
if (this.terminated) {
return this;
}
var self = this;
var options = {
path : '/session',
data : {
desiredCapabilities : this.desiredCapabilities
}
};
var request = new HttpRequest(options);
request.on('success', function(data, response, isRedirect) {
if (data && data.sessionId) {
self.sessionId = self.api.sessionId = data.sessionId;
if (data.value) {
self.api.capabilities = data.value;
}
Logger.info('Got sessionId from selenium', self.sessionId);
self.emit('selenium:session_create', self.sessionId, request, response);
} else if (isRedirect) {
self.followRedirect(request, response);
} else {
request.emit('error', data, null);
}
})
.on('error', function(data, err) {
if (self.options.output) {
console.error('\n' + Logger.colors.light_red('Error retrieving a new session from the selenium server'));
}
if (typeof data == 'object' && Object.keys(data).length === 0) {
data = '';
}
if (!data && err) {
data = err;
}
self.emit('error', data);
})
.send();
return this;
};
Nightwatch.prototype.followRedirect = function (request, response) {
if (!response.headers || !response.headers.location) {
this.emit('error', null, null);
return this;
}
var url = require('url');
var urlParts = url.parse(response.headers.location);
request.setOptions({
path : urlParts.pathname,
host : urlParts.hostname,
port : urlParts.port,
method : 'GET'
}).send();
return this;
};
exports = module.exports = {};
exports.client = function(options) {
return new Nightwatch(options);
};
exports.cli = function(runTests) {
var cli = require('./runner/cli/cli.js');
cli.setup();
var argv = cli.init();
if (argv.help) {
cli.showHelp();
} else if (argv.version) {
var packageConfig = require(__dirname + '/../package.json');
console.log(packageConfig.name + ' v' + packageConfig.version);
} else {
if (typeof runTests != 'function') {
throw new Error('Supplied argument needs to be a function!');
}
runTests(argv);
}
};
exports.runner = function(argv, done, settings) {
var runner = exports.CliRunner(argv);
return runner.setup(settings, done).runTests(done);
};
exports.initGrunt = function(grunt) {
grunt.registerMultiTask('nightwatch', 'run nightwatch.', function() {
var done = this.async();
var options = this.options();
var settings = this.data && this.data.settings;
var argv = this.data && this.data.argv;
exports.cli(function(a) {
Object.keys(argv).forEach(function(key) {
if (key === 'env' && a['parallel-mode'] === true) {
return;
}
a[key] = argv[key];
});
if (a.test) {
a.test = path.resolve(a.test);
}
if (options.cwd) {
process.chdir(options.cwd);
}
exports.runner(a, done, settings);
});
});
};
exports.CliRunner = function(argv) {
var CliRunner = require('./runner/cli/clirunner.js');
return new CliRunner(argv);
};
exports.initClient = function(opts) {
var Manager = require('./runner/clientmanager.js');
var instance = new Manager();
return instance.init(opts);
};

View File

@@ -0,0 +1,238 @@
module.exports = new (function() {
/**
* Given an element name, returns that element object
*
* @param {Object} parent The parent page or section
* @param {string} elementName Name of element
* @returns {Object} The element object
*/
function getElement(parent, elementName) {
elementName = elementName.substring(1);
if (!(elementName in parent.elements)) {
throw new Error(elementName + ' was not found in "' + parent.name +
'". Available elements: ' + Object.keys(parent.elements));
}
return parent.elements[elementName];
}
/**
* Given a section name, returns that section object
*
* @param {Object} parent The parent page or section
* @param {string} sectionName Name of section
* @returns {Object} The section object
*/
function getSection(parent, sectionName) {
sectionName = sectionName.substring(1);
if (!(sectionName in parent.section)) {
throw new Error(sectionName + ' was not found in "' + parent.name +
'". Available sections: ' + Object.keys(parent.sections));
}
return parent.section[sectionName];
}
/**
* Calls use(Css|Xpath|Recursion) command
*
* Uses `useXpath`, `useCss`, and `useRecursion` commands.
*
* @param {Object} client The Nightwatch instance
* @param {string} desiredStrategy (css selector|xpath|recursion)
* @returns {null}
*/
function setLocateStrategy(client, desiredStrategy) {
var methodMap = {
xpath : 'useXpath',
'css selector' : 'useCss',
recursion : 'useRecursion'
};
if (desiredStrategy in methodMap) {
client.api[methodMap[desiredStrategy]]();
}
}
/**
* Creates a closure that enables calling commands and assertions on the page or section.
* For all element commands and assertions, it fetches element's selector and locate strategy
* For elements nested under sections, it sets 'recursion' as the locate strategy and passes as its first argument to the command an array of its ancestors + self
* If the command or assertion is not on an element, it calls it with the untouched passed arguments
*
* @param {Object} parent The parent page or section
* @param {function} commandFn The actual command function
* @param {string} commandName The name of the command ("click", "containsText", etc)
* @param {Boolean} [isChaiAssertion]
* @returns {function}
*/
function makeWrappedCommand(parent, commandFn, commandName, isChaiAssertion) {
return function() {
var args = Array.prototype.slice.call(arguments);
var prevLocateStrategy = parent.client.locateStrategy;
var elementCommand = isElementCommand(args);
if (elementCommand) {
var firstArg;
var desiredStrategy;
var callbackIndex;
var originalCallback;
var elementOrSectionName = args.shift();
var getter = (isChaiAssertion && commandName === 'section') ? getSection : getElement;
var elementOrSection = getter(parent, elementOrSectionName);
var ancestors = getAncestorsWithElement(elementOrSection);
if (ancestors.length === 1) {
firstArg = elementOrSection.selector;
desiredStrategy = elementOrSection.locateStrategy;
} else {
firstArg = ancestors;
desiredStrategy = 'recursion';
}
setLocateStrategy(parent.client, desiredStrategy);
args.unshift(firstArg);
// if a callback is being used with this command, wrap it in
// a function that allows us to restore the locate strategy
// to its original value before the callback is called
callbackIndex = findCallbackIndex(args);
if (callbackIndex !== -1) {
originalCallback = args[callbackIndex];
args[callbackIndex] = function callbackWrapper() {
// restore the locate strategy directly through client.locateStrategy.
// setLocateStrategy() can't be used since it uses the api commands
// which get added to the command queue and will not update the
// strategy in time for the callback which is getting immediately
// called after
parent.client.locateStrategy = prevLocateStrategy;
return originalCallback.apply(parent.client.api, arguments);
};
}
}
var c = commandFn.apply(parent.client, args);
if (elementCommand) {
setLocateStrategy(parent.client, prevLocateStrategy);
}
return (isChaiAssertion ? c : parent);
};
}
/**
*
* @param {Array} args
* @return {boolean}
*/
function isElementCommand(args) {
return (args.length > 0) && (args[0].toString().indexOf('@') === 0);
}
/**
* Identifies the location of a callback function within an arguments array.
*
* @param {Array} args Arguments array in which to find the location of a callback.
* @returns {number} Index location of the callback in the args array. If not found, -1 is returned.
*/
function findCallbackIndex(args) {
if (args.length === 0) {
return -1;
}
// callbacks will usually be the last argument. waitFor methods allow an additional
// message argument to follow the callback which will also need to be checked for.
// last argument
var index = args.length - 1;
if (typeof args[index] === 'function') {
return index;
}
// second to last argument (waitfor calls)
index--;
if (typeof args[index] === 'function') {
return index;
}
return -1;
}
/**
* Retrieves an array of ancestors of the supplied element. The last element in the array is the element object itself
*
* @param {Object} element The element
* @returns {Array}
*/
function getAncestorsWithElement(element) {
var elements = [];
function addElement(e) {
elements.unshift(e);
if (e.parent && e.parent.selector) {
addElement(e.parent);
}
}
addElement(element);
return elements;
}
/**
* Adds commands (elements commands, assertions, etc) to the page or section
*
* @param {Object} parent The parent page or section
* @param {Object} target What the command is added to (parent|section or assertion object on parent|section)
* @param {Object} commands
* @returns {null}
*/
function applyCommandsToTarget(parent, target, commands) {
Object.keys(commands).forEach(function(commandName) {
if (isValidAssertion(commandName)) {
target[commandName] = target[commandName] || {};
var isChaiAssertion = commandName === 'expect';
var assertions = commands[commandName];
Object.keys(assertions).forEach(function(assertionName) {
target[commandName][assertionName] = addCommand(target[commandName], assertions[assertionName], assertionName, parent, isChaiAssertion);
});
} else {
target[commandName] = addCommand(target, commands[commandName], commandName, parent, false);
}
});
}
function addCommand(target, commandFn, commandName, parent, isChaiAssertion) {
if (target[commandName]) {
parent.client.results.errors++;
var error = new Error('The command "' + commandName + '" is already defined!');
parent.client.errors.push(error.stack);
throw error;
}
return makeWrappedCommand(parent, commandFn, commandName, isChaiAssertion);
}
function isValidAssertion(commandName) {
return ['assert', 'verify', 'expect'].indexOf(commandName) > -1;
}
/**
* Entrypoint to add commands (elements commands, assertions, etc) to the page or section
*
* @param {Object} parent The parent page or section
* @param {function} commandLoader function that retrieves commands
* @returns {null}
*/
this.addWrappedCommands = function (parent, commandLoader) {
var commands = {};
commands = commandLoader(commands);
applyCommandsToTarget(parent, parent, commands);
};
})();

View File

@@ -0,0 +1,24 @@
/**
* Class that all elements subclass from
*
* @param {Object} options Element options defined in page object
* @constructor
*/
function Element(options) {
this.parent = options.parent;
if (!options.selector) {
throw new Error('No selector property for element "' + options.name +
'" Instead found properties: ' + Object.keys(options));
}
this.name = options.name;
this.selector = options.selector;
this.locateStrategy = options.locateStrategy || 'css selector';
}
Element.prototype.toString = function() {
return 'Element[name=@' + this.name + ']';
};
module.exports = Element;

View File

@@ -0,0 +1,89 @@
module.exports = new (function() {
/**
* Returns the properties object passed as an argument (or null if no arguments are passed).
* If the supplied properties argument is a function, it invokes that function with the page as its context.
*
* @method createProps
* @param {Page|Section} parent The page object or section instance
* @param {Object|Function} props Object or Function that returns an object
* @returns {null}
*/
this.createProps = function(parent, props) {
parent.props = typeof props === 'function' ? props.call(parent) : props;
return this;
};
/**
* Assigns the `elements` property for a page or section object.
* For each object in the passed array, it creates a new element object by instantiating Element with its options
*
* @param {Page|Section} parent The page object or section instance
* @param {Object|Array} elements Object or array of objects to become element objects
* @returns {null}
*/
this.createElements = function(parent, elements) {
var Element = require('./element.js');
var elementObjects = {};
var el;
if (!Array.isArray(elements)) {
elements = [elements];
}
elements.forEach(function(els) {
Object.keys(els).forEach(function(e) {
el = typeof els[e] === 'string' ? { selector: els[e] } : els[e];
el.parent = parent;
el.name = e;
elementObjects[el.name] = new Element(el);
});
});
parent.elements = elementObjects;
return this;
};
/**
* Assigns the `section` property for a page or section object.
* For each object in the passed array, it creates a new section object by instantiating Section with its options
*
* @param {Page|Section} parent The page object or section instance
* @param {Array} sections Array of objects to become section objects
* @returns {null}
*/
this.createSections = function(parent, sections) {
var Section = require('./section.js');
var sectionObjects = {};
var sec;
Object.keys(sections).forEach(function(s) {
sec = sections[s];
sec.parent = parent;
sec.name = s;
sectionObjects[sec.name] = new Section(sec);
});
parent.section = sectionObjects;
return this;
};
/**
* Mixes in the passed functions to the page or section object.
*
* @param {Page|Section} parent The page object or section instance
* @param {Object} command Array of commands that will be added to the age or section
* @returns {null}
*/
this.addCommands = function(parent, commands) {
commands.forEach(function(m) {
Object.keys(m).forEach(function(k) {
parent[k] = m[k];
});
});
return this;
};
})();

66
tests/node_modules/nightwatch/lib/page-object/page.js generated vendored Normal file
View File

@@ -0,0 +1,66 @@
var PageUtils = require('./page-utils.js');
var CommandWrapper = require('./command-wrapper.js');
/**
* Class that all pages subclass from
*
* @param {Object} options Page options defined in page object
* @constructor
*/
function Page(options, commandLoader, api, client) {
this.commandLoader = commandLoader;
this.api = api;
this.client = client;
this.name = options.name;
this.url = options.url;
PageUtils
.createProps(this, options.props || {})
.createElements(this, options.elements || {})
.createSections(this, options.sections || {})
.addCommands(this, options.commands || []);
CommandWrapper.addWrappedCommands(this, this.commandLoader);
}
Page.prototype = {
/**
* Returns the url passed as an argument (or null if no arguments are passed).
* If the supplied url is a function, it invokes that function with the page as its context.
*
* @method getUrl
* @param {string} url
* @returns {string|null}
*/
getUrl: function(url) {
if (typeof url === 'function') {
return url.call(this);
} else if (typeof url === 'string') {
return url;
}
return null;
},
/**
* This command is an alias to url and also a convenience method because when called without any arguments
* it performs a call to .url() with passing the value of `url` property on the page object.
* Uses `url` protocol command.
*
* @method navigate
* @param {Object} [url=this.url] Url to navigate to.
* @param {function} [callback] Optional callback function to be called when the command finishes.
* @returns {*}
*/
navigate: function(url, callback) {
var goToUrl = this.getUrl(url || this.url);
if (goToUrl === null) {
throw new Error('Invalid URL: You must either add a url property to "' +
this.name + '" or provide a url as an argument');
}
this.api.url(goToUrl, callback);
return this;
}
};
module.exports = Page;

View File

@@ -0,0 +1,38 @@
var PageUtils = require('./page-utils.js');
var CommandWrapper = require('./command-wrapper.js');
/**
* Class that all sections subclass from
*
* @param {Object} options Section options defined in page object
* @constructor
*/
function Section(options) {
this.parent = options.parent;
this.client = this.parent.client;
if(!options.selector) {
throw new Error('No selector property for section "' + options.name +
'" Instead found properties: ' + Object.keys(options));
}
this.name = options.name;
this.selector = options.selector;
this.locateStrategy = options.locateStrategy || 'css selector';
this.api = this.parent.api;
this.commandLoader = this.parent.commandLoader;
PageUtils
.createProps(this, options.props || {})
.createElements(this, options.elements || {})
.createSections(this, options.sections || {})
.addCommands(this, options.commands || []);
CommandWrapper.addWrappedCommands(this, this.commandLoader);
}
Section.prototype.toString = function() {
return 'Section[name=' + this.name + ']';
};
module.exports = Section;

View File

@@ -0,0 +1,144 @@
var child_process = require('child_process');
var util = require('util');
var events = require('events');
var Logger = require('../../util/logger.js');
function ChildProcess(environment, index, env_output, settings, args) {
events.EventEmitter.call(this);
this.env_output = env_output || [];
this.mainModule = process.mainModule.filename;
this.index = index;
this.itemKey = this.getChildProcessEnvKey(environment);
this.startDelay = settings.parallel_process_delay || ChildProcess.defaultStartDelay;
this.environment = environment;
this.settings = settings;
this.child = null;
this.globalExitCode = 0;
this.processRunning = false;
this.env_label = '';
this.args = args || [];
}
util.inherits(ChildProcess, events.EventEmitter);
ChildProcess.prevIndex = 0;
ChildProcess.defaultStartDelay = 10;
ChildProcess.prototype.setLabel = function(label) {
this.env_itemKey = label;
this.env_label = this.settings.disable_colors ?
(' ' + label + ' ') : Logger.colors.yellow(' ' + label + ' ', Logger.colors.background.black);
return this;
};
ChildProcess.prototype.run = function(colors, done) {
this.availColors = colors;
var cliArgs = this.getArgs();
var env = {};
Object.keys(process.env).forEach(function(key) {
env[key] = process.env[key];
});
var self = this;
setTimeout(function() {
env.__NIGHTWATCH_PARALLEL_MODE = 1;
env.__NIGHTWATCH_ENV = self.environment;
env.__NIGHTWATCH_ENV_KEY = self.itemKey;
env.__NIGHTWATCH_ENV_LABEL = self.env_itemKey;
self.child = child_process.spawn(process.execPath, cliArgs, {
cwd: process.cwd(),
encoding: 'utf8',
env: env,
stdio: [null, null, null, 'ipc']
});
self.child.on('message', function(data) {
self.emit('result', JSON.parse(data));
});
self.processRunning = true;
if (self.settings.output) {
console.log('Started child process for:' + self.env_label);
}
self.child.stdout.on('data', function (data) {
self.writeToStdout(data);
});
self.child.stderr.on('data', function (data) {
self.writeToStdout(data);
});
self.child.on('exit', function (code) {
if (self.settings.output) {
console.log('\n >>' + self.env_label + 'finished. ', '\n');
}
if (code) {
self.globalExitCode = 2;
}
self.processRunning = false;
done(self.env_output, code);
});
}, this.index * this.startDelay);
};
ChildProcess.prototype.getChildProcessEnvKey = function(env) {
return env + '_' + (this.index+1);
};
/**
* Returns an array of cli arguments to be passed to the child process,
* based on the args passed to the main process
* @returns {Array}
*/
ChildProcess.prototype.getArgs = function() {
var args = [this.mainModule];
args.push.apply(args, this.args);
args.push('--parallel-mode');
return args;
};
ChildProcess.prototype.writeToStdout = function(data) {
data = data.toString().trim();
var color_pair = this.availColors[this.index%4];
var output = '';
if (ChildProcess.prevIndex !== this.index) {
ChildProcess.prevIndex = this.index;
if (this.settings.live_output) {
output += '\n';
}
}
if (this.settings.output) {
if (this.settings.disable_colors) {
output += ' ' + this.environment + ' ';
} else {
output += Logger.colors[color_pair[1]](' ' + this.environment + ' ',
Logger.colors.background[color_pair[0]]);
}
output += ' ';
}
output += data;
if (this.settings.live_output) {
process.stdout.write(output + '\n');
} else {
this.env_output.push(output);
}
};
module.exports = ChildProcess;

174
tests/node_modules/nightwatch/lib/runner/cli/cli.js generated vendored Normal file
View File

@@ -0,0 +1,174 @@
/**
* Module dependencies
*/
var opt = require('optimist');
module.exports = new (function() {
var _DEFAULTS_ = {};
var _COMMANDS_ = {};
function Command(name) {
this.name = name;
_DEFAULTS_[this.name] = {};
}
Command.prototype.demand = function(value) {
if (!value && _DEFAULTS_[this.name].demand) {
return _DEFAULTS_[this.name].demand;
}
_DEFAULTS_[this.name].demand = value;
return this;
};
Command.prototype.description = function(value) {
if (!value && _DEFAULTS_[this.name].description) {
return _DEFAULTS_[this.name].alias;
}
_DEFAULTS_[this.name].description = value;
return this;
};
Command.prototype.alias = function(value) {
if (!value && _DEFAULTS_[this.name].alias) {
return _DEFAULTS_[this.name].alias;
}
_DEFAULTS_[this.name].alias = value;
return this;
};
Command.prototype.defaults = function(value) {
if (!value && _DEFAULTS_[this.name]['default']) {
return _DEFAULTS_[this.name]['default'];
}
_DEFAULTS_[this.name]['default'] = value;
return this;
};
Command.prototype.isDefault = function(value) {
return _DEFAULTS_[this.name]['default'] === value;
};
this.showHelp = function() {
return opt.showHelp();
};
this.command = function(name) {
if (_COMMANDS_[name]) {
return _COMMANDS_[name];
}
_COMMANDS_[name] = new Command(name);
return _COMMANDS_[name];
};
this.init = function() {
var argv = opt.usage('Usage: $0 [source] [options]')
.option('source', {
string : true
})
.options(_DEFAULTS_).argv;
return argv;
};
this.setup = function() {
// CLI definitions
// $ nightwatch -c
// $ nightwatch --config
this.command('config')
.demand(true)
.description('Path to configuration file')
.alias('c')
.defaults('./nightwatch.json');
// $ nightwatch -o
// $ nightwatch --output
this.command('output')
.description('Where to save the (JUnit XML) test reports.')
.alias('o')
.defaults('tests_output');
// $ nightwatch -r
// $ nightwatch --reporter
this.command('reporter')
.description('Name of a predefined reporter (e.g. junit) or path to a custom reporter file to use.')
.alias('r')
.defaults('junit');
// $ nightwatch -e
// $ nightwatch --env saucelabs
this.command('env')
.description('Testing environment to use.')
.alias('e')
.defaults('default');
// $ nightwatch --verbose
this.command('verbose')
.description('Turns on selenium command logging during the session.');
// $ nightwatch -t
// $ nightwatch --test
this.command('test')
.description('Runs a single test.')
.alias('t');
// $ nightwatch --testcase
this.command('testcase')
.description('Used only together with --test. Runs the specified testcase from the current suite/module.');
// $ nightwatch -g
// $ nightwatch --group
this.command('group')
.description('Runs a group of tests (i.e. a folder)')
.alias('g');
// $ nightwatch -s
// $ nightwatch --skipgroup
this.command('skipgroup')
.description('Skips one or several (comma separated) group of tests.')
.alias('s');
// $ nightwatch -f
// $ nightwatch --filter
this.command('filter')
.description('Specify a filter (glob expression) as the file name format to use when loading the files.')
.defaults('')
.alias('f');
// $ nightwatch -a
// $ nightwatch --tag
this.command('tag')
.description('Only run tests with the given tag.')
.defaults('')
.alias('a');
// $ nightwatch --skiptags
this.command('skiptags')
.description('Skips tests that have the specified tag or tags (comma separated).');
// $ nightwatch --retries
this.command('retries')
.description('Retries failed or errored testcases up <n> times.');
// $ nightwatch --suiteRetries
this.command('suiteRetries')
.description('Retries failed or errored testsuites up <n> times.');
// $ nightwatch -h
// $ nightwatch --help
this.command('help')
.description('Shows this help.')
.alias('h');
// $ nightwatch -v
// $ nightwatch --version
this.command('version')
.alias('v')
.description('Shows version information.');
return this;
};
})();

View File

@@ -0,0 +1,977 @@
var fs = require('fs');
var path = require('path');
var Q = require('q');
var clone = require('lodash.clone');
var defaults = require('lodash.defaultsdeep');
var Runner = require('../run.js');
var Logger = require('../../util/logger.js');
var Utils = require('../../util/utils.js');
var Selenium = require('../selenium.js');
var ChildProcess = require('./child-process.js');
var ErrorHandler = require('./errorhandler.js');
var SETTINGS_DEPRECTED_VAL = './settings.json';
var SETTINGS_JS_FILE = './nightwatch.conf.js';
function CliRunner(argv) {
this.settings = null;
this.argv = argv;
this.test_settings = null;
this.output_folder = '';
this.parallelMode = false;
this.runningProcesses = {};
this.cli = require('./cli.js');
}
CliRunner.prototype = {
/**
* @param {function} [done]
* @deprecated - use .setup instead
* @returns {CliRunner}
*/
init : function(done) {
this
.readSettings()
.parseTestSettings({}, done);
return this;
},
/**
* @param {object} [settings]
* @param {function} [done]
* @returns {CliRunner}
*/
setup : function(settings, done) {
this
.readSettings()
.parseTestSettings(settings, done);
return this;
},
/**
* Read the provided config json file; defaults to settings.json if one isn't provided
*/
readSettings : function() {
// use default nightwatch.json file if we haven't received another value
if (this.cli.command('config').isDefault(this.argv.config)) {
var defaultValue = this.cli.command('config').defaults();
var localJsValue = path.resolve(SETTINGS_JS_FILE);
if (Utils.fileExistsSync(SETTINGS_JS_FILE)) {
this.argv.config = localJsValue;
} else if (Utils.fileExistsSync(defaultValue)) {
this.argv.config = path.join(path.resolve('./'), this.argv.config);
} else if (Utils.fileExistsSync(SETTINGS_DEPRECTED_VAL)) {
this.argv.config = path.join(path.resolve('./'), SETTINGS_DEPRECTED_VAL);
} else {
var binFolder = path.resolve(__dirname + '/../../../bin');
var defaultFile = path.join(binFolder, this.argv.config);
if (Utils.fileExistsSync(defaultFile)) {
this.argv.config = defaultFile;
} else {
this.argv.config = path.join(binFolder, SETTINGS_DEPRECTED_VAL);
}
}
} else {
this.argv.config = path.resolve(this.argv.config);
}
this.argv.env = typeof this.argv.env == 'string' ? this.argv.env : 'default';
// reading the settings file
try {
this.settings = require(this.argv.config);
this.replaceEnvVariables();
this.manageSelenium = !this.isParallelMode() && this.settings.selenium &&
this.settings.selenium.start_process || false;
if (typeof this.settings.src_folders == 'string') {
this.settings.src_folders = [this.settings.src_folders];
}
this.settings.output = this.settings.output || typeof this.settings.output == 'undefined';
this.settings.detailed_output = this.settings.detailed_output || typeof this.settings.detailed_output == 'undefined';
} catch (ex) {
Logger.error(ex.stack);
this.settings = {};
}
return this;
},
/**
* Looks for pattern ${VAR_NAME} in settings
* @param {Object} [target]
*/
replaceEnvVariables : function(target) {
target = target || this.settings;
for (var key in target) {
switch(typeof target[key]) {
case 'object':
this.replaceEnvVariables(target[key]);
break;
case 'string':
target[key] = target[key].replace(/\$\{(\w+)\}/g, function(match, varName) {
return process.env[varName] || '${' + varName + '}';
});
break;
}
}
return this;
},
/**
* Reads globals from an external js or json file set in the settings file
* @return {CliRunner}
*/
readExternalGlobals : function () {
if (!this.settings.globals_path) {
return this;
}
var fullPath = path.resolve(this.settings.globals_path);
try {
var externalGlobals = require(fullPath);
if (!externalGlobals) {
return this;
}
// if we already have globals, make a copy of them
var globals = this.test_settings.globals ? clone(this.test_settings.globals, true) : {};
if (externalGlobals.hasOwnProperty(this.argv.env)) {
for (var prop in externalGlobals[this.argv.env]) {
externalGlobals[prop] = externalGlobals[this.argv.env][prop];
}
}
defaults(globals, externalGlobals);
this.test_settings.globals = globals;
return this;
} catch (err) {
var originalMsg = Logger.colors.red(err.name + ': ' + err.message);
err.name = 'Error reading external global file failed';
err.message = 'using '+ fullPath + ':\n' + originalMsg;
throw err;
}
},
/**
* @returns {CliRunner}
*/
setOutputFolder : function() {
var isDisabled = this.settings.output_folder === false && typeof(this.test_settings.output_folder) == 'undefined' ||
this.test_settings.output_folder === false;
var isDefault = this.cli.command('output').isDefault(this.argv.output);
var folder = this.test_settings.output_folder || this.settings.output_folder;
this.output_folder = isDisabled ? false : (isDefault && folder || this.argv.output);
return this;
},
singleTestRun: function () {
return typeof this.argv.test == 'string';
},
singleSourceFile: function() {
if (this.singleTestRun()) {
return fs.statSync(this.argv.test).isFile();
}
return (Array.isArray(this.argv._source) && this.argv._source.length == 1) && fs.statSync(this.argv._source[0]).isFile();
},
getTestSourceForSingle: function(targetPath) {
var testsource;
if (targetPath && path.resolve(targetPath) === targetPath) {
testsource = targetPath;
} else {
testsource = path.join(process.cwd(), targetPath);
}
if (testsource.substr(-3) != '.js') {
testsource += '.js';
}
return testsource;
},
/**
* Returns the path where the tests are located
* @returns {*}
*/
getTestSource : function() {
var testsource;
if (this.isTestWorker() || this.singleSourceFile()) {
testsource = this.getTestSourceForSingle(this.argv.test || this.argv._source[0]);
} else {
if (this.argv.testcase) {
this.argv.testcase = null;
if (this.test_settings.output) {
ErrorHandler.logWarning('Option --testcase used without --test is ignored.');
}
}
if (Array.isArray(this.argv._source) && (this.argv._source.length > 0)) {
testsource = this.argv._source;
} else if (this.argv.group) {
// add support for multiple groups
if (typeof this.argv.group == 'string') {
this.argv.group = this.argv.group.split(',');
}
var groupTestsource = this.findGroupPathMultiple(this.argv.group);
// If a group does not exist in the multiple src folder case, it is removed
// from the test path list.
if (this.settings.src_folders.length === 1) {
// any incorrect path here will result in a run error
testsource = groupTestsource;
} else {
// only when all groups fail to match will there be a run error
testsource = groupTestsource.filter(Utils.dirExistsSync);
if (this.argv.verbose) {
var ignoredSource = groupTestsource.filter(function(path) {
return testsource.indexOf(path) === -1;
});
if (ignoredSource.length) {
Logger.warn('The following group paths were not found and will be excluded from the run:\n - ' + ignoredSource.join('\n - '));
}
}
}
} else {
testsource = this.settings.src_folders;
}
}
return testsource;
},
/**
* Gets test paths from each of the src folders for a single group.
*
* @param {string} groupName
* @return {Array}
*/
findGroupPath : function(groupName) {
var fullGroupPath = path.resolve(groupName);
// for each src folder, append the group to the path
// to resolve the full test path
return this.settings.src_folders.map(function(srcFolder) {
var fullSrcFolder = path.resolve(srcFolder);
if (fullGroupPath.indexOf(fullSrcFolder) === 0) {
return groupName;
}
return path.join(srcFolder, groupName);
});
},
/**
* Gets test paths for tests from any number of groups.
*
* @param {Array} groups
*/
findGroupPathMultiple : function(groups) {
var paths = [];
groups.forEach(function(groupName) {
// findGroupPath will return an array of paths
paths = paths.concat(this.findGroupPath(groupName));
}.bind(this));
return paths;
},
/**
* Starts the selenium server process
* @param {function} [callback]
* @returns {CliRunner}
*/
startSelenium : function(callback) {
callback = callback || function() {};
if (this.isTestWorker()) {
callback();
return this;
}
var globalsContext = this.test_settings && this.test_settings.globals || null;
// adding a link to the test_settings object on the globals context
if (globalsContext) {
globalsContext.test_settings = this.test_settings;
}
var beforeGlobal = Utils.checkFunction('before', globalsContext) || function(done) {done();};
var self = this;
if (!this.manageSelenium) {
beforeGlobal.call(globalsContext, function() {
callback();
});
return this;
}
this.settings.parallelMode = this.parallelMode;
beforeGlobal.call(globalsContext, function() {
Selenium.startServer(self.settings, function(error, child, error_out, exitcode) {
if (error) {
console.error('There was an error while starting the Selenium server:');
ErrorHandler.handle({
message : error_out
});
return;
}
callback();
});
});
return this;
},
/**
* Stops the selenium server if it is running
* @return {*|promise}
*/
stopSelenium : function() {
var deferred = Q.defer();
if (this.manageSelenium) {
Selenium.stopServer(function() {
deferred.resolve();
});
} else {
deferred.resolve();
}
return deferred.promise;
},
/**
* Starts the test runner
*
* @param {function} [finished] optional callback
* @returns {CliRunner}
*/
runTests : function(finished) {
if (this.parallelMode) {
return this;
}
var self = this;
var handleError = ErrorHandler.handle;
this.startSelenium(function() {
self.runner(function(err, results) {
self.stopSelenium().then(function() {
if (self.isTestWorker()) {
handleError(err, results, finished);
return;
}
self.runGlobalAfter().then(function (ex) {
handleError(ex || err, results, finished);
});
});
});
});
return this;
},
runner : function(fn) {
var testRunner = this.settings.test_runner || 'default';
var testRunnerType = testRunner.type ? testRunner.type : testRunner;
// getTestSource() will throw on an error, so we need
// to wrap and pass along any error that does occur
// to the callback fn
var source;
try {
source = this.getTestSource();
} catch (err) {
fn(err, {});
return;
}
switch (testRunnerType) {
case 'default':
var runner = new Runner(source, this.test_settings, {
output_folder : this.output_folder,
src_folders : this.settings.src_folders,
live_output : this.settings.live_output,
detailed_output : this.settings.detailed_output,
start_session: this.startSession,
reporter : this.argv.reporter,
testcase : this.argv.testcase,
end_session_on_fail : this.endSessionOnFail,
retries : this.argv.retries,
test_worker : this.isTestWorker(),
suite_retries : this.argv.suiteRetries
}, fn);
return runner.run();
case 'mocha':
var MochaNightwatch = require('mocha-nightwatch');
var mochaOpts = testRunner.options || {
ui : 'bdd'
};
var mocha = new MochaNightwatch(mochaOpts);
var nightwatch = require('../../index.js');
Runner.readPaths(source, this.test_settings, function(error, modulePaths) {
if (error) {
fn(error, {});
return;
}
modulePaths.forEach(function(file) {
mocha.addFile(file);
});
mocha.run(nightwatch, this.test_settings, function(failures) {
fn(null, {
failed : failures
});
if (failures) {
process.exit(10);
}
});
}.bind(this));
return mocha;
}
},
inheritFromDefaultEnv : function() {
if (this.argv.env == 'default') {
return;
}
var defaultEnv = this.settings.test_settings['default'] || {};
defaults(this.test_settings, defaultEnv);
return this;
},
runGlobalAfter : function() {
var deferred = Q.defer();
var globalsContext = this.test_settings && this.test_settings.globals || null;
var afterGlobal = Utils.checkFunction('after', globalsContext) || function(done) {done();};
try {
afterGlobal.call(globalsContext, function done() {
deferred.resolve(null);
});
} catch (ex) {
deferred.resolve(ex);
}
return deferred.promise;
},
/**
* Validates and parses the test settings
* @param {object} [settings]
* @param {function} [done]
* @returns {CliRunner}
*/
parseTestSettings : function(settings, done) {
// checking if the env passed is valid
if (!this.settings.test_settings) {
throw new Error('No testing environment specified.');
}
var envs = this.argv.env.split(',');
for (var i = 0; i < envs.length; i++) {
if (!(envs[i] in this.settings.test_settings)) {
throw new Error('Invalid testing environment specified: ' + envs[i]);
}
}
if (envs.length > 1) {
this.setupParallelMode(envs, done);
return this;
}
this.initTestSettings(this.argv.env, settings);
if (this.parallelModeWorkers()) {
this.setupParallelMode(null, done);
}
return this;
},
setGlobalOutputOptions: function () {
this.test_settings.output = this.test_settings.output || (this.settings.output && typeof this.test_settings.output == 'undefined');
this.test_settings.silent = this.test_settings.silent || typeof this.test_settings.silent == 'undefined';
if (this.argv.verbose) {
this.test_settings.silent = false;
}
return this;
},
/**
* Sets the specific test settings for the specified environment
* @param {string} [env]
* @param {object} [settings]
* @returns {CliRunner}
*/
initTestSettings : function(env, settings) {
// picking the environment specific test settings
this.test_settings = env && this.settings.test_settings[env] || {};
if (env) {
this.test_settings.custom_commands_path = this.settings.custom_commands_path || '';
this.test_settings.custom_assertions_path = this.settings.custom_assertions_path || '';
this.test_settings.page_objects_path = this.settings.page_objects_path || '';
this.inheritFromDefaultEnv();
this.updateTestSettings(settings || {});
this.readExternalGlobals();
}
this.setOutputFolder();
this.setGlobalOutputOptions();
if (typeof this.test_settings.test_workers != 'undefined') {
this.settings.test_workers = this.test_settings.test_workers;
} else if (typeof this.settings.test_workers != 'undefined') {
this.test_settings.test_workers = this.settings.test_workers;
}
if (typeof this.test_settings.test_runner != 'undefined') {
this.settings.test_runner = this.test_settings.test_runner;
}
if (typeof this.test_settings.detailed_output != 'undefined') {
this.settings.detailed_output = this.test_settings.detailed_output;
}
if (typeof this.argv.skipgroup == 'string') {
this.test_settings.skipgroup = this.argv.skipgroup.split(',');
}
if (this.argv.filter) {
this.test_settings.filename_filter = this.argv.filter;
}
if (this.argv.tag) {
this.test_settings.tag_filter = this.argv.tag;
}
if (typeof this.argv.skiptags == 'string') {
this.test_settings.skiptags = this.argv.skiptags.split(',');
}
return this;
},
/**
*
* @param {Object} [test_settings]
* @returns {CliRunner}
*/
updateTestSettings : function(test_settings) {
if (this.parallelMode && !this.parallelModeWorkers()) {
return this;
}
if (test_settings && typeof test_settings == 'object') {
for (var key in test_settings) {
this.test_settings[key] = test_settings[key];
}
}
this.settings.selenium = this.settings.selenium || {};
// overwrite selenium settings per environment
if (Utils.isObject(this.test_settings.selenium)) {
for (var prop in this.test_settings.selenium) {
this.settings.selenium[prop] = this.test_settings.selenium[prop];
}
}
this.manageSelenium = !this.isParallelMode() && this.settings.selenium.start_process || false;
this.startSession = this.settings.selenium.start_session || typeof this.settings.selenium.start_session == 'undefined';
this.mergeSeleniumOptions();
this.disableCliColorsIfNeeded();
this.endSessionOnFail = typeof this.settings.end_session_on_fail == 'undefined' || this.settings.end_session_on_fail;
if (typeof this.test_settings.end_session_on_fail != 'undefined') {
this.endSessionOnFail = this.test_settings.end_session_on_fail;
}
return this;
},
disableCliColorsIfNeeded : function() {
if (this.settings.disable_colors || this.test_settings.disable_colors) {
Logger.disableColors();
}
return this;
},
/**
* Backwards compatible method which attempts to merge deprecated driver specific options for selenium
* @returns {CliRunner}
*/
mergeSeleniumOptions : function() {
if (!this.manageSelenium) {
return this;
}
this.settings.selenium.cli_args = this.settings.selenium.cli_args || {};
this.mergeCliArgs();
var deprecationNotice = function(propertyName, newSettingName) {
ErrorHandler.logWarning('DEPRECATION NOTICE: Property ' + propertyName + ' is deprecated since v0.5. Please' +
' use the "cli_args" object on the "selenium" property to define "' + newSettingName + '". E.g.:');
var demoObj = '{\n' +
' "cli_args": {\n' +
' "'+ Logger.colors.yellow(newSettingName) +'": "<VALUE>"\n' +
' }\n' +
'}';
console.log(demoObj, '\n');
};
if (this.test_settings.firefox_profile) {
deprecationNotice('firefox_profile', 'webdriver.firefox.profile');
this.settings.selenium.cli_args['webdriver.firefox.profile'] = this.test_settings.firefox_profile;
}
if (this.test_settings.chrome_driver) {
deprecationNotice('chrome_driver', 'webdriver.chrome.driver');
this.settings.selenium.cli_args['webdriver.chrome.driver'] = this.test_settings.chrome_driver;
}
if (this.test_settings.ie_driver) {
deprecationNotice('ie_driver', 'webdriver.ie.driver');
this.settings.selenium.cli_args['webdriver.ie.driver'] = this.test_settings.ie_driver;
}
return this;
},
/**
* Merge current environment specific cli_args into the main cli_args object to be passed to selenium
*
* @returns {CliRunner}
*/
mergeCliArgs : function() {
if (Utils.isObject(this.test_settings.cli_args)) {
for (var prop in this.test_settings.cli_args) {
if (this.test_settings.cli_args.hasOwnProperty(prop)) {
this.settings.selenium.cli_args[prop] = this.test_settings.cli_args[prop];
}
}
}
return this;
},
////////////////////////////////////////////////////////////////////////////////////
// Parallelism related
////////////////////////////////////////////////////////////////////////////////////
isParallelMode : function() {
return process.env.__NIGHTWATCH_PARALLEL_MODE === '1';
},
isTestWorker : function() {
return this.isParallelMode() && this.argv['test-worker'];
},
parallelModeWorkers: function () {
return !this.isParallelMode() && !this.singleSourceFile() && (this.settings.test_workers === true ||
Utils.isObject(this.settings.test_workers) && this.settings.test_workers.enabled);
},
/**
* Enables parallel execution mode
* @param {Array} envs
* @param {function} [done]
* @returns {CliRunner}
*/
setupParallelMode : function(envs, done) {
this.parallelMode = true;
var self = this;
this.startSelenium(function() {
self.startChildProcesses(envs, function(code) {
self.stopSelenium().then(function() {
if (self.parallelModeWorkers()) {
return self.runGlobalAfter();
}
return true;
}).then(function() {
if (done) {
try {
done(self.childProcessOutput, code);
} catch (err) {
done(err);
}
}
if (code) {
process.exit(code);
}
});
});
});
return this;
},
getAvailableColors : function () {
var availColors = [
['red', 'light_gray'],
['green', 'black'],
['blue', 'light_gray'],
['magenta', 'light_gray']
];
var currentIndex = availColors.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = availColors[currentIndex];
availColors[currentIndex] = availColors[randomIndex];
availColors[randomIndex] = temporaryValue;
}
return availColors;
},
/**
* Start a new child process for each environment
* @param {Array} envs
* @param {function} doneCallback
*/
startChildProcesses : function(envs, doneCallback) {
doneCallback = doneCallback || function() {};
var availColors = this.getAvailableColors();
this.childProcessOutput = {};
ChildProcess.prevIndex = 0;
var args = this.getChildProcessArgs(envs);
if (envs === null) {
this.startTestWorkers(availColors, args, doneCallback);
return this;
}
this.startEnvChildren(envs, availColors, args, doneCallback);
},
startEnvChildren : function(envs, availColors, args, doneCallback) {
var self = this;
var globalExitCode = 0;
envs.forEach(function(environment, index) {
self.childProcessOutput[environment] = [];
var childArgs = args.slice();
childArgs.push('--env', environment);
var child = new ChildProcess(environment, index, self.childProcessOutput[environment], self.settings, childArgs);
child.setLabel(environment + ' environment');
self.runningProcesses[child.itemKey] = child;
self.runningProcesses[child.itemKey].run(availColors, function(output, exitCode) {
if (exitCode > 0) {
globalExitCode = exitCode;
}
if (self.processesRunning() === 0) {
if (!self.settings.live_output) {
self.printChildProcessOutput();
}
doneCallback(globalExitCode);
}
});
});
},
getChildProcessArgs : function(envs) {
var childProcessArgs = [];
var arg;
for (var i = 2; i < process.argv.length; i++) {
arg = process.argv[i];
if (envs && (arg == '-e' || arg == '--env')) {
i++;
} else {
childProcessArgs.push(arg);
}
}
return childProcessArgs;
},
startTestWorkers : function(availColors, args, doneCallback) {
var workerCount = this.getTestWorkersCount();
var source = this.getTestSource();
var self = this;
var globalExitCode = 0;
var globalStartTime = new Date().getTime();
var globalResults = {
errmessages : [],
modules : {},
passed : 0,
failed : 0,
errors : 0,
skipped : 0,
tests : 0
};
var Reporter = require('../../runner/reporter.js');
var globalReporter = new Reporter(globalResults, {}, globalStartTime, {start_session: this.startSession});
Runner.readPaths(source, this.test_settings, function(error, modulePaths) {
if (error) {
ErrorHandler.handle(error, null, doneCallback);
return;
}
var remaining = modulePaths.length;
Utils.processAsyncQueue(workerCount, modulePaths, function(modulePath, index, next) {
var outputLabel = Utils.getModuleKey(modulePath, self.settings.src_folders, modulePaths);
var childOutput = self.childProcessOutput[outputLabel] = [];
// arguments to the new child process, essentially running a single test suite
var childArgs = args.slice();
childArgs.push('--test', modulePath, '--test-worker');
var settings = clone(self.settings);
settings.output = settings.output && settings.detailed_output;
var child = new ChildProcess(outputLabel, index, childOutput, settings, childArgs);
child.setLabel(outputLabel);
child.on('result', function(childResult) {
switch (childResult.type) {
case 'testsuite_finished':
globalResults.modules[childResult.moduleKey] = childResult.results;
if (childResult.errmessages.length) {
globalResults.errmessages.concat(childResult.errmessages);
}
globalResults.passed += childResult.passed;
globalResults.failed += childResult.failed;
globalResults.errors += childResult.errors;
globalResults.skipped += childResult.skipped;
globalResults.tests += childResult.tests;
self.printChildProcessOutput(childResult.itemKey);
break;
case 'testsuite_started':
break;
}
});
self.runningProcesses[child.itemKey] = child;
self.runningProcesses[child.itemKey].run(availColors, function (output, exitCode) {
if (exitCode > 0) {
globalExitCode = exitCode;
}
remaining -=1;
if (remaining > 0) {
next();
} else {
if (!self.settings.live_output) {
globalReporter.printTotalResults();
}
doneCallback(globalExitCode);
}
});
});
});
},
printChildProcessOutput : function(label) {
var self = this;
if (label) {
self.childProcessOutput[label] = self.childProcessOutput[label].filter(function(item) {
return item !== '';
}).map(function(item) {
if (item == '\\n') {
item = '\n';
}
return item;
});
self.childProcessOutput[label].forEach(function(output) {
process.stdout.write(output + '\n');
});
self.childProcessOutput[label].length = 0;
return;
}
Object.keys(this.childProcessOutput).forEach(function(environment) {
self.printChildProcessOutput(environment);
});
},
getTestWorkersCount : function() {
var workers = 1;
if (this.settings.test_workers === true || this.settings.test_workers.workers === 'auto') {
workers = require('os').cpus().length;
} else if ('number' === typeof this.settings.test_workers.workers) {
workers = this.settings.test_workers.workers;
}
return workers;
},
processesRunning : function() {
var running = 0;
for (var item in this.runningProcesses) {
if (this.runningProcesses.hasOwnProperty(item) && this.runningProcesses[item].processRunning) {
running += 1;
}
}
return running;
}
};
module.exports = CliRunner;

View File

@@ -0,0 +1,70 @@
var Logger = require('../../util/logger.js');
var ErrorHandler = module.exports = {
handle : function(err, results, finished) {
finished = finished || function() {};
if (results && results.errors) {
console.log(results.errmessages.join('\n'));
}
if (err) {
Logger.enable();
if (!err.message) {
err.message = 'There was an error while running the test.';
}
ErrorHandler.logError(err);
finished(false);
process.exit(1);
} else {
var result = true;
if (results.failed || results.errors) {
result = false;
}
finished(result);
}
},
logWarning : function(message) {
console.warn(Logger.colors.brown(message));
},
logError : function(err) {
if (!err) {
return;
}
var util = require('util');
console.error('');
var stackTrace = err && err.stack;
if (!stackTrace) {
var data;
if (err.message) {
data = err.data;
err = err.message;
}
if (typeof err == 'string') {
process.stderr.write(Logger.colors.red(err));
if (data) {
if (typeof data == 'object' && Object.keys(data).length > 0) {
data = util.inspect(data);
}
process.stderr.write(Logger.colors.stack_trace(data) + '\n');
}
process.stderr.write('\n');
} else {
console.error(err);
}
return;
}
var parts = stackTrace.split('\n');
process.stderr.write(Logger.colors.red(parts.shift()) + '\n');
process.stderr.write(parts.join('\n') + '\n\n');
}
};

View File

@@ -0,0 +1,206 @@
var util = require('util');
var events = require('events');
var Q = require('q');
var Nightwatch = require('../index.js');
function ClientManager() {
events.EventEmitter.call(this);
this.setMaxListeners(0);
}
util.inherits(ClientManager, events.EventEmitter);
ClientManager.prototype.init = function(opts) {
try {
this['@client'] = Nightwatch.client(opts);
} catch (err) {
console.log(err.stack);
this.emit('error', err, false);
return;
}
var self = this;
this.options = opts;
this['@client'].once('selenium:session_create', function() {
var capabilities = this.api.capabilities || {};
var browserName = (capabilities.browserName && capabilities.browserName.toUpperCase()) || '';
self.options.report_prefix = browserName + '_' + this.api.capabilities.version + '_' + this.api.capabilities.platform + '_';
});
return this;
};
ClientManager.prototype.start = function(done) {
var self = this;
this.resetTerminated();
this['@client'].once('nightwatch:finished', function(results, errors) {
self.emit('complete', results, errors);
if (done) {
if (results.failed > 0 || results.errors > 0) {
done(results.lastError);
results.lastError = null;
} else {
done();
}
}
});
this['@client'].once('error', function(error, data) {
var result = {
message: 'Connection refused! Is selenium server started?\n',
data : error || data
};
self.emit('error', result, false);
});
this['@client'].start();
return this;
};
ClientManager.prototype.get = function() {
return this['@client'];
};
ClientManager.prototype.set = function(prop, value) {
this['@client'][prop] = value;
return this;
};
ClientManager.prototype.publishTestResults = function(testcase, results) {
if (!this['@client'].api.currentTest) {
return this;
}
results = results || {};
var currentTestSuite = this['@client'].api.currentTest.results;
currentTestSuite.passed += results.passed;
currentTestSuite.failed += results.failed;
currentTestSuite.errors += results.errors;
currentTestSuite.skipped += results.skipped;
currentTestSuite.tests += results.tests.length;
currentTestSuite.testcases = currentTestSuite.testcases || {};
currentTestSuite.testcases[testcase] = {
passed : results.passed,
failed : results.failed,
errors : results.errors,
skipped : results.skipped,
tests : results.tests.length,
assertions : results.tests,
stackTrace : results.stackTrace
};
return this;
};
ClientManager.prototype.results = function(type, value) {
if (typeof value == 'undefined' && typeof type == 'undefined') {
return this['@client'].results;
}
if (typeof value == 'undefined') {
return this['@client'].results[type] || 0;
}
this['@client'].results[type] = value;
return this;
};
ClientManager.prototype.errors = function() {
return this['@client'].errors;
};
ClientManager.prototype.handleException = function(err) {
return this['@client'].handleException(err);
};
ClientManager.prototype.clearGlobalResult = function() {
return this['@client'].clearResult();
};
ClientManager.prototype.terminated = function() {
return this['@client'].terminated;
};
ClientManager.prototype.terminate = function() {
this['@client'].terminate();
return this;
};
ClientManager.prototype.resetTerminated = function() {
this['@client'].resetTerminated();
return this;
};
ClientManager.prototype.print = function(startTime) {
return this['@client'].printResult(startTime);
};
ClientManager.prototype.api = function(key, value) {
if (key && (typeof value != 'undefined')) {
this['@client'].api[key] = value;
}
return this['@client'].api;
};
ClientManager.prototype.globals = function(key, value) {
if (key) {
if (typeof value != 'undefined') {
this['@client'].api.globals[key] = value;
return this;
}
return this['@client'].api.globals[key];
}
return this['@client'].api.globals;
};
ClientManager.prototype.resetQueue = function() {
this['@client'].queue.reset();
return this;
};
ClientManager.prototype.restartQueue = function(onComplete) {
this.resetQueue();
this['@client'].queue.run(onComplete);
};
ClientManager.prototype.shouldRestartQueue = function() {
return this['@client'] && this['@client'].queue.list().length > 0;
};
ClientManager.prototype.checkQueue = function() {
var deferred = Q.defer();
if (this.shouldRestartQueue()) {
this.restartQueue(function() {
deferred.resolve();
});
} else {
deferred.resolve();
}
return deferred.promise;
};
ClientManager.prototype.endSessionOnFail = function(value) {
if (typeof value == 'undefined') {
return this['@client'].endSessionOnFail();
}
this['@client'].endSessionOnFail(value);
return this;
};
ClientManager.prototype.skipTestcasesOnFail = function() {
return this.api().options.skip_testcases_on_fail;
};
module.exports = ClientManager;

108
tests/node_modules/nightwatch/lib/runner/filematcher.js generated vendored Normal file
View File

@@ -0,0 +1,108 @@
var path = require('path');
var minimatch = require('minimatch');
function adaptFilePath(srcPath, excludedPath) {
var resolved = path.resolve(excludedPath);
if (resolved.indexOf(srcPath) === 0) {
return resolved;
}
if (excludedPath.charAt(excludedPath.length-1) === path.sep) {
excludedPath = excludedPath.substring(0, excludedPath.length-1);
}
return path.join(srcPath, excludedPath);
}
function matchFilePath(filePath, pathArr) {
for (var i = 0; i < pathArr.length; i++) {
if (minimatch(filePath, pathArr[i])) {
return true;
}
}
return false;
}
module.exports = {
tags: {
/**
* @param {string} testFilePath - file path of a test
* @param {object} opts - test options
* @returns {boolean} true if specified test matches given tag
*/
match : function (testFilePath, opts) {
var test;
try {
test = require(testFilePath);
} catch (e) {
// could not load test module
return false;
}
return this.checkModuleTags(test, opts);
},
/**
* @param {object} test - test module
* @param {object} opts - test options
* @returns {boolean}
*/
checkModuleTags: function (test, opts) {
var moduleTags = test['@tags'] || test.tags;
var match = true;
if (!Array.isArray(moduleTags)) {
return typeof opts.tag_filter == 'undefined' && typeof opts.skiptags != 'undefined';
}
if (opts.tag_filter || opts.skiptags){
moduleTags = this.convertTagsToString(moduleTags);
if (opts.tag_filter) {
match = this.containsTag(moduleTags, this.convertTagsToString(opts.tag_filter));
}
if (opts.skiptags) {
match = this.excludesTag(moduleTags, this.convertTagsToString(opts.skiptags));
}
}
return match;
},
convertTagsToString : function(tags) {
return [].concat(tags).map(function (tag) {
return String(tag).toLowerCase();
});
},
containsTag : function(moduleTags, tags) {
return moduleTags.some(function (testTag) {
return (tags.indexOf(testTag) > -1);
});
},
excludesTag : function(moduleTags, tags) {
return moduleTags.every(function (testTag) {
return (tags.indexOf(testTag) == -1);
});
}
},
exclude : {
adaptFilePath : function(filePath, excludedPath) {
if (!Array.isArray(excludedPath)) {
excludedPath = [excludedPath];
}
return excludedPath.map(function(item) {
return adaptFilePath(filePath, item);
});
},
match : matchFilePath
},
filter : {
adaptFilePath : adaptFilePath,
match : matchFilePath
}
};

205
tests/node_modules/nightwatch/lib/runner/module.js generated vendored Normal file
View File

@@ -0,0 +1,205 @@
var path = require('path');
var Utils = require('../util/utils.js');
function Module(modulePath, opts, addtOpts) {
try {
this['@module'] = require(modulePath);
} catch (err) {
throw err;
}
if (!this['@module']) {
throw new Error('Invalid test suite provided.');
}
var currentTestcase = addtOpts && addtOpts.testcase;
this['@testSuiteName'] = null;
this['@attributes'] = {
'@endSessionOnFail' : true,
'@disabled' : false,
'@desiredCapabilities' : {},
'@tags' : []
};
this.groupName = '';
this.setTestSuiteName();
this.keys = this.getKeys(currentTestcase);
if (currentTestcase && this.keys.length === 0) {
throw new Error('Error: "' + currentTestcase + '" is not a valid testcase in the current test suite.');
}
this.allKeys = this.keys.slice();
this.filePath = modulePath;
this.modulePathParts = this.filePath.split(path.sep);
this.moduleName = this.modulePathParts.pop();
this.options = opts;
}
Module.prototype.get = function(key) {
if (!key) {
return this['@module'];
}
return this['@module'][key];
};
Module.prototype.set = function(key, value) {
if (!key || !value) {
return this;
}
this['@module'][key] = value;
return this;
};
Module.prototype.getName = function() {
return this.moduleName;
};
Module.prototype.call = function(fnName /* arg1, arg2 ...*/) {
var args = Array.prototype.slice.call(arguments, 1);
return this['@module'][fnName].apply(this['@module'], args);
};
Module.prototype.callAsync = function(fnName, api, done, expectedArgs) {
expectedArgs = expectedArgs || 2;
var fnAsync = Utils.makeFnAsync(expectedArgs, this['@module'][fnName], this['@module']);
fnAsync.name = fnName;
var args = [done];
if (expectedArgs == 2) {
args.unshift(api);
}
return fnAsync.apply(this['@module'], args);
};
Module.prototype.setReportKey = function(fullPaths, srcFolders) {
var diffInFolder = '', folder, parentFolder = '';
var filePath = this.modulePathParts.join(path.sep);
if (srcFolders) {
for (var i = 0; i < srcFolders.length; i++) {
folder = path.resolve(srcFolders[i]);
if (fullPaths.length > 1) {
parentFolder = folder.split(path.sep).pop();
}
if (filePath.indexOf(folder) === 0) {
diffInFolder = filePath.substring(folder.length + 1);
break;
}
}
}
if (diffInFolder.substr(-1) == path.sep) {
diffInFolder = diffInFolder.substring(0, diffInFolder.length-1);
}
this.groupName = diffInFolder.split(path.sep).pop(); // in case we're in a sub-folder
this.moduleKey = path.join(parentFolder, diffInFolder, this.moduleName);
return this;
};
Module.prototype.getKeys = function(currentTestcase) {
return Object.keys(this['@module']).filter(function(element) {
if (!this['@module'][element]) {
return false;
}
var isFunction = typeof this['@module'][element] == 'function';
if (currentTestcase) {
return isFunction && (element === currentTestcase);
}
return isFunction;
}, this);
};
Module.prototype.removeKey = function(key) {
var self = this;
if (Array.isArray(key)) {
key.forEach(function(item) {
self.removeKey(item);
});
return;
}
var index = this.keys.indexOf(key);
if (index > -1) {
this.keys.splice(index, 1);
}
};
Module.prototype.setTestSuiteName = function() {
var module = this['@module'];
var keys = Object.keys(module);
var suiteName;
var hasSubSuite = keys.some(function(item) {
var isAttribute = this.isAttribute(item);
if (isAttribute) {
var attrName = Module.geAttributeName(item);
this['@attributes'][attrName] = module[item];
} else if (typeof module[item] == 'object' && module[item]) {
suiteName = item;
return true;
}
return false;
}, this);
if (hasSubSuite && suiteName) {
var sections = [];
if (this['@testSuiteName']) {
sections.push(this['@testSuiteName']);
}
sections.push(suiteName);
this['@testSuiteName'] = sections.join(' :: ');
this['@module'] = module[suiteName];
return this.setTestSuiteName();
}
return this;
};
Module.geAttributeName = function(item) {
return (item.charAt(0) == '@' ? '' : '@') + item;
};
Module.prototype.isAttribute = function(item) {
var attrName = Module.geAttributeName(item);
return (attrName in this['@attributes']) && (typeof module[item] != 'function');
};
Module.prototype.getTestSuiteName = function() {
return this['@testSuiteName'];
};
Module.prototype.getNextKey = function() {
if (this.keys.length) {
return this.keys.shift();
}
return null;
};
Module.prototype.resetKeys = function() {
this.keys = this.allKeys.slice();
};
Module.prototype.endSessionOnFail = function() {
return this['@attributes']['@endSessionOnFail'];
};
Module.prototype.isDisabled = function() {
return this['@attributes']['@disabled'];
};
Module.prototype.desiredCapabilities = function(capability) {
if (capability && (capability in this['@attributes']['@desiredCapabilities'])) {
return this['@attributes']['@desiredCapabilities'][capability];
}
return this['@attributes']['@desiredCapabilities'];
};
module.exports = Module;

283
tests/node_modules/nightwatch/lib/runner/reporter.js generated vendored Normal file
View File

@@ -0,0 +1,283 @@
var fs = require('fs');
var path = require('path');
var util = require('util');
var mkpath = require('mkpath');
var Logger = require('../util/logger.js');
var colors = Logger.colors;
var format = util.format;
var Utils = require('../util/utils.js');
var Q = require('q');
var Reporter = module.exports = function(globalResults, testResults, globalStartTime, options) {
this.globalResults = globalResults;
this.testResults = testResults;
this.globalStartTime = globalStartTime;
this.options = options;
this.reporter = options.reporter || 'junit';
};
var printf = function() {
console.log(format.apply(null, arguments));
};
/**
* @static
* @param err
* @param testname
* @param time
* @return {string}
*/
Reporter.getTestOutput = function (err, testname, time) {
var symbol;
if (Utils.isErrorObject(err)) {
symbol = Logger.colors.red(Utils.symbols.fail);
testname = Logger.colors.red(testname);
} else {
symbol = Logger.colors.green(Utils.symbols.ok);
}
var args = [
'%s %s', symbol, testname
];
if (time > 20) {
args[0] += ' %s';
args.push(Logger.colors.yellow(Utils.format('(%s)', Utils.formatElapsedTime(time))));
}
return Utils.format.apply(null, args);
};
Reporter.printAssertions = function(test) {
test.assertions.forEach(function(a) {
if (a.failure !== false) {
var message = a.stackTrace.split('\n');
message.unshift(a.fullMsg);
Utils.showStackTrace(message.join('\n'));
}
});
if (test.stackTrace) {
Utils.showStackTrace(test.stackTrace);
}
};
Reporter.prototype.get = function() {
var fileName = __dirname + '/reporters/' + this.reporter + '.js';
if (fs.existsSync(fileName)) {
return require(fileName);
}
fileName = path.resolve(this.reporter);
if (fs.existsSync(fileName)) {
var reporter = require(fileName);
if (typeof reporter.write == 'function') {
return reporter;
}
throw new Error('The reporter module must have a public `write` method defined.');
}
throw new Error('The reporter file name cannot be resolved. Using path: ' + fileName);
};
/**
* @param {object} globals
* @returns {function}
*/
Reporter.prototype.globalReporter = function(globals) {
var reporterFn = Utils.checkFunction('reporter', globals) || function() {};
return Utils.makeFnAsync(2, reporterFn, globals);
};
Reporter.prototype.isDisabled = function() {
return this.options.output_folder === false;
};
Reporter.prototype.createFolder = function(cb) {
if (this.isDisabled()) {
cb(null);
} else {
mkpath(this.options.output_folder, cb);
}
};
Reporter.prototype.save = function() {
var self = this;
var deferred = Q.defer();
this.createFolder(function(err) {
if (self.isDisabled()) {
deferred.resolve();
} else {
if (err) {
deferred.reject(err);
return;
}
var reporter = self.get();
reporter.write(self.globalResults, self.options, function(err) {
if (err) {
console.log(colors.yellow(format('Warning: Failed to save report file to folder: %s', self.options.output_folder)));
console.log(err.stack);
}
deferred.resolve(err);
});
}
});
return deferred.promise;
};
Reporter.prototype.printTotalResults = function() {
var elapsedTime = new Date().getTime() - this.globalStartTime;
process.stdout.write('\n');
if (this.testsFailed()) {
var countMessage = this.getTestsFailedMessage();
console.log(colors.light_red(' _________________________________________________'));
console.log(
format('\n %s', colors.light_red('TEST FAILURE:')),
countMessage,
format('(%s)', Utils.formatElapsedTime(elapsedTime))
);
this.printFailureSummary();
console.log('');
} else {
if (!this.shouldShowSummary()) {
return;
}
var message = this.getTestsPassedMessage();
printf('%s (%s)', message, Utils.formatElapsedTime(elapsedTime));
}
};
Reporter.prototype.testsFailed = function() {
return Object.keys(this.globalResults.modules).some(function(moduleKey) {
return this.globalResults.modules[moduleKey].failures > 0 || this.globalResults.modules[moduleKey].errors > 0;
}.bind(this));
};
Reporter.prototype.shouldShowSummary = function() {
var modules = Object.keys(this.globalResults.modules);
if (modules.length > 1) {
return true;
}
if (modules.length <= 0) {
return false;
}
return Object.keys(this.globalResults.modules[modules[0]].completed).length > 1;
};
Reporter.prototype.hasAssertionCount = function() {
return Object.keys(this.globalResults.modules).length > 0 &&
(this.globalResults.failed > 0 || this.globalResults.passed > 0);
};
Reporter.prototype.getTestsPassedMessage = function() {
var hasCount = this.hasAssertionCount();
var message;
var count;
if (hasCount) {
count = this.globalResults.passed;
message = colors.green(format('OK. %s %s passed.', count, (count > 1 ? ' total assertions' : ' assertion')));
} else {
count = this.getTotalTestsCount();
message = format('%s tests passed.', colors.green('OK. ' + count));
}
return message;
};
Reporter.prototype.getTotalTestsCount = function() {
var module;
return Object.keys(this.globalResults.modules).reduce(function(count, moduleKey) {
module = this.globalResults.modules[moduleKey];
return count + module.tests - module.skipped.length;
}.bind(this), 0);
};
Reporter.prototype.getTestsFailedMessage = function() {
var hasCount = this.hasAssertionCount();
if (!hasCount && this.testResults.errmessages === 0) {
return '';
}
var errorsMsg = '';
var failedMsg = 'assertions';
var passedMsg = format('%s passed', colors.green(this.globalResults.passed));
if (!this.options.start_session) {
failedMsg = 'tests';
var passedCount = Math.max(0, this.getTotalTestsCount() - this.globalResults.failed);
passedMsg = format('%s passed', colors.green(passedCount));
}
var skipped = '';
if (this.testResults.skipped) {
skipped = format(' and %s skipped', colors.cyan(this.testResults.skipped));
}
if (this.globalResults.errors) {
var suffix = this.globalResults.errors > 1 ? 's' : '';
errorsMsg += format('%s error%s during execution, ', colors.red(this.globalResults.errors), suffix);
}
return format('%s %s %s failed, %s%s.', errorsMsg, colors.red(this.globalResults.failed), failedMsg, passedMsg, skipped);
};
Reporter.prototype.printFailureSummary = function() {
Object.keys(this.globalResults.modules).forEach(function(moduleKey) {
var testSuite = this.globalResults.modules[moduleKey];
if (testSuite.failures > 0 || testSuite.errors > 0) {
console.log('\n' + colors.red(format(' %s %s', Utils.symbols.fail, moduleKey)));
Object.keys(testSuite.completed).forEach(function(testcase) {
var test = testSuite.completed[testcase];
if (test.failed > 0 || test.errors > 0) {
printf('\n - %s %s', testcase, colors.yellow('(' + Utils.formatElapsedTime(test.timeMs) + ')'));
if (test.assertions.length > 0 && this.options.start_session) {
Reporter.printAssertions(test);
} else if (test.stackTrace) {
Utils.showStackTrace(test.stackTrace);
}
}
}.bind(this));
if (Array.isArray(testSuite.errmessages)) {
testSuite.errmessages.forEach(function(err) {
console.log('');
Utils.showStackTrace(err);
console.log('');
});
}
if (testSuite.skipped.length > 0) {
console.log(colors.cyan(' SKIPPED:'));
testSuite.skipped.forEach(function(testcase) {
printf(' - %s', testcase);
});
}
}
}.bind(this));
if (Array.isArray(this.globalResults.errmessages)) {
this.globalResults.errmessages.forEach(function(err) {
console.log('');
Utils.showStackTrace(err);
console.log('');
}.bind(this));
}
};

View File

@@ -0,0 +1,96 @@
/**
* https://svn.jenkins-ci.org/trunk/hudson/dtkit/dtkit-format/dtkit-junit-model/src/main/resources/com/thalesgroup/dtkit/junit/model/xsd/junit-4.xsd
* @type {exports}
*/
var fs = require('fs');
var mkpath = require('mkpath');
var path = require('path');
var ejs = require('ejs');
var util = require('util');
var Utils = require('../../util/utils.js');
module.exports = new (function() {
var tmpl = __dirname + '/junit.xml.ejs';
var tmplData;
var globalResults;
function loadTemplate(cb) {
if (tmplData) {
cb(tmplData);
return;
}
fs.readFile(tmpl, function (err, data) {
if (err) {
throw err;
}
tmplData = data.toString();
cb(tmplData);
});
}
function adaptAssertions(module) {
Object.keys(module.completed).forEach(function(item) {
var testcase = module.completed[item];
var assertions = testcase.assertions;
for (var i = 0; i < assertions.length; i++) {
if (assertions[i].stackTrace) {
assertions[i].stackTrace = Utils.stackTraceFilter(assertions[i].stackTrace.split('\n'));
}
}
if (testcase.failed > 0 && testcase.stackTrace) {
var stackParts = testcase.stackTrace.split('\n');
var errorMessage = stackParts.shift();
testcase.stackTrace = Utils.stackTraceFilter(stackParts);
testcase.message = errorMessage;
}
});
}
function writeReport(moduleKey, data, opts, callback) {
var module = globalResults.modules[moduleKey];
var pathParts = moduleKey.split(path.sep);
var moduleName = pathParts.pop();
var output_folder = opts.output_folder;
adaptAssertions(module);
var rendered = ejs.render(data, {
locals: {
module : module,
moduleName : moduleName,
systemerr : globalResults.errmessages.join('\n')
}
});
if (pathParts.length) {
output_folder = path.join(output_folder, pathParts.join(path.sep));
mkpath.sync(output_folder);
}
var filename = path.join(output_folder, opts.filename_prefix + moduleName + '.xml');
fs.writeFile(filename, rendered, function(err) {
callback(err);
globalResults.errmessages.length = 0;
});
}
this.write = function(results, options, callback) {
globalResults = results;
var keys = Object.keys(results.modules);
loadTemplate(function createReport(data) {
var moduleKey = keys.shift();
writeReport(moduleKey, data, options, function(err) {
if (err || (keys.length === 0)) {
callback(err);
} else {
createReport(data);
}
});
});
};
})();

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" ?>
<testsuites errors="<%= module.errors %>"
failures="<%= module.failures %>"
tests="<%= module.tests %>">
<testsuite name="<%= moduleName %>"
errors="<%= module.errors %>" failures="<%= module.failures %>" hostname="" id="" package="<%= module.group || moduleName %>" skipped="<%= (Array.isArray(module.skipped)) ? module.skipped.length : 0 %>"
tests="<%= module.tests %>" time="<%= module.time %>" timestamp="<%= module.timestamp %>">
<% for (var item in module.completed) {
var testcase = module.completed[item];
var assertions = testcase.assertions %>
<testcase name="<%= item %>" time="<%= testcase.time %>" assertions="<%= assertions.length %>"><%
for (var i = 0; i < assertions.length; i++) { %><% if (assertions[i].failure) { %> <failure message="<%= assertions[i].message %>"><%= assertions[i].stackTrace %></failure><% } %>
<% if (assertions[i].screenshots && assertions[i].screenshots.length > 0) { %><system-out><% for (var j = 0; j < assertions[i].screenshots.length; j++) { %>[[ATTACHMENT|<%= assertions[i].screenshots[j] %>]]<% } %></system-out><% } %>
<% }
if (testcase.failed > 0 && testcase.stackTrace) {
%><failure message="<%= testcase.message %>"><%= testcase.stackTrace %></failure><% } %>
</testcase><% if (systemerr != '') {%>
<system-err>
<%= systemerr %>
</system-err><% } %>
<% } %>
<% if (module.skipped && (module.skipped.length > 0)) { %>
<% for (var j = 0; j < module.skipped.length; j++) { %>
<testcase
name="<%= module.skipped[j] %>">
<skipped />
</testcase>
<% } %>
<% } %>
</testsuite>
</testsuites>

287
tests/node_modules/nightwatch/lib/runner/run.js generated vendored Normal file
View File

@@ -0,0 +1,287 @@
var Walk = require('./walk.js');
var TestSuite = require('./testsuite.js');
var Logger = require('../util/logger.js');
var Reporter = require('./reporter.js');
var path = require('path');
var Q = require('q');
var globalResults;
var currentTestSuite;
var finishCallback;
function processListener() {
process.on('exit', function (code) {
var exitCode = code;
if (exitCode === 0 && globalResults && (globalResults.errors > 0 || globalResults.failed > 0)) {
exitCode = 1;
}
process.exit(exitCode);
});
process.on('uncaughtException', function (err) {
if (currentTestSuite) {
var testCase = currentTestSuite.getCurrentTestCase();
if (testCase/* && testCase.running*/) {
testCase.catchHandler(err);
return;
}
}
if (finishCallback) {
finishCallback(err);
}
});
}
function Runner(testSource, opts, additionalOpts, doneCb) {
this.testSource = testSource || [];
this.options = opts;
this.additionalOpts = additionalOpts;
this.doneCb = doneCb || function() {};
this.globalStartTime = new Date().getTime();
this.currentTestSuite = null;
globalResults = this.globalResults = {
passed : 0,
failed : 0,
errors : 0,
skipped : 0,
tests : 0,
errmessages : [],
modules : {}
};
this.setOptions();
}
Runner.prototype.setOptions = function() {
this.options.parallelMode = process.env.__NIGHTWATCH_PARALLEL_MODE == '1';
if (this.options.parallelMode) {
this.options.currentEnv = process.env.__NIGHTWATCH_ENV_KEY;
}
this.options.live_output = this.additionalOpts.live_output;
this.options.detailed_output = this.additionalOpts.detailed_output;
this.options.start_session = this.additionalOpts.start_session;
this.options.report_prefix = '';
this.options.test_worker = this.additionalOpts.test_worker;
};
Runner.prototype.runTestModule = function(modulePath, fullPaths) {
try {
currentTestSuite = this.currentTestSuite = new TestSuite(modulePath, fullPaths, this.options, this.additionalOpts, this.doneCb);
} catch (err) {
this.doneCb(err);
return null;
}
var moduleKey = this.currentTestSuite.getReportKey();
this.globalResults.modules[moduleKey] = {
completed : {},
skipped : null,
time : null,
timestamp : null,
group : this.currentTestSuite.getGroupName()
};
return this.currentTestSuite
.on('testcase:finished', function(results, errors, time) {
this.globalResults.modules[moduleKey].completed[this.currentTestSuite.currentTest] = {
passed : results.passed,
failed : results.failed,
errors : results.errors,
skipped : results.skipped,
assertions : [].concat(results.tests),
timeMs : time,
time : (time/1000).toPrecision(4),
stackTrace : results.stackTrace
};
if (Array.isArray(errors) && errors.length) {
this.globalResults.errmessages = this.globalResults.errmessages.concat(errors);
}
this.globalResults.passed += results.passed;
this.globalResults.failed += results.failed;
this.globalResults.errors += results.errors;
this.globalResults.skipped += results.skipped;
this.globalResults.tests += results.tests.length;
this.globalResults.assertions = this.globalResults.tests;
if (this.options.output && this.options.detailed_output &&
(this.options.modulesNo > 1 || results.tests !== this.globalResults.tests || results.steps.length > 1)
) {
this.currentTestSuite.printResult(time);
} else if (this.options.output && !this.options.detailed_output) {
var error = (results.failed > 0 || results.errors > 0) ? new Error('') : null;
console.log(Reporter.getTestOutput(error, this.currentTestSuite.currentTest, time));
if (error !== null) {
Reporter.printAssertions(results.tests);
}
}
}.bind(this))
.run()
.then(function onTestSuiteResolved(testResults) {
var testSuiteResult = this.globalResults.modules[moduleKey];
testSuiteResult.skipped = testResults.steps;
testSuiteResult.timestamp = testResults.timestamp;
testSuiteResult.time = (testResults.time/1000).toPrecision(4);
testSuiteResult.tests = Object.keys(testSuiteResult.completed).length + (testSuiteResult.skipped && testSuiteResult.skipped.length || 0);
var failures = 0;
var errors = testResults.errors || 0;
Object.keys(testSuiteResult.completed).forEach(function(item) {
if (testSuiteResult.completed[item].failed > 0) {
failures++;
}
});
testSuiteResult.errmessages = testResults.errmessages || [];
testSuiteResult.failures = failures;
testSuiteResult.errors = errors;
if (typeof process.send == 'function') {
process.send(JSON.stringify({
type: 'testsuite_finished',
itemKey: process.env.__NIGHTWATCH_ENV_LABEL,
moduleKey: moduleKey,
results: this.globalResults.modules[moduleKey],
errmessages: testSuiteResult.errmessages,
passed: this.globalResults.passed,
failed: this.globalResults.failed,
errors: this.globalResults.errors,
skipped: this.globalResults.skipped,
tests: this.globalResults.tests
}));
}
return testResults;
}.bind(this));
};
Runner.readPaths = function(testSource, opts, cb) {
var deferred = Q.defer();
cb = cb || function() {};
if (typeof testSource == 'string') {
testSource = [testSource];
}
var fullPaths = testSource.map(function (p) {
if (p.indexOf(process.cwd()) === 0 || path.resolve(p) === p) {
return p;
}
return path.join(process.cwd(), p);
});
if (fullPaths.length === 0) {
throw new Error('No source folder defined. Check configuration.');
}
var errorMessage = ['No tests defined! using source folder:', fullPaths];
if (opts.tag_filter) {
errorMessage.push('; using tags:', opts.tag_filter);
}
Walk.readPaths(fullPaths, function (err, modules) {
if (err) {
if (err.code == 'ENOENT') {
var error = new Error('Cannot read source folder: ' + err.path);
cb(error, false);
deferred.reject(error);
return;
}
cb(err, false);
deferred.reject(err);
return;
}
opts.modulesNo = modules.length;
if (modules.length === 0) {
var error2 = new Error(errorMessage.join(' '));
cb(error2);
deferred.reject(error2);
return;
}
cb(null, modules, fullPaths);
deferred.resolve([modules, fullPaths]);
}, opts);
return deferred.promise;
};
Runner.prototype.run = function runner() {
var deferred = Q.defer();
var self = this;
finishCallback = this.doneCb;
Runner.readPaths(this.testSource, this.options)
.spread(function(modulePaths, fullPaths) {
(function runNextModule() {
var modulePath = modulePaths.shift();
var promise = self.runTestModule(modulePath, fullPaths);
if (promise === null) {
deferred.resolve();
return;
}
promise.then(function(testResults) {
if (modulePaths.length) {
setImmediate(runNextModule);
} else {
var reporter = new Reporter(self.globalResults, testResults, self.globalStartTime, {
output_folder : self.additionalOpts.output_folder,
filename_prefix : self.options.report_prefix,
globals : self.options.globals,
reporter : self.additionalOpts.reporter,
start_session : self.options.start_session
});
if (self.options.output) {
reporter.printTotalResults(self.globalResults, testResults);
}
reporter.save().then(function() {
reporter.globalReporter(self.options.globals)(self.globalResults, function() {
try {
self.doneCb(null, self.globalResults);
deferred.resolve(self.globalResults);
} catch (err) {
deferred.reject(err);
}
});
}, function(err) {
console.log(Logger.colors.yellow('Output folder doesn\'t exist and cannot be created.'));
console.log(err.stack);
self.doneCb(null);
deferred.resolve();
});
}
}, function(err) {
self.doneCb(err, self.globalResults);
}).catch(function(err) {
deferred.reject(err);
});
})();
})
.catch(function(err) {
self.doneCb(err, false);
})
.catch(function(err) {
deferred.reject(err);
});
return deferred.promise;
};
processListener();
module.exports = Runner;

176
tests/node_modules/nightwatch/lib/runner/selenium.js generated vendored Normal file
View File

@@ -0,0 +1,176 @@
/**
* Module dependencies
*/
var path = require('path');
var fs = require('fs');
var child_process = require('child_process');
var util = require('util');
var Logger = require('../util/logger.js');
var SENTINEL = ['Started org.openqa.jetty.jetty.Server', 'INFO - Selenium Server is up and running'];
var DEFAULT_PORT = 4444;
var DEFAULT_HOST = '127.0.0.1';
var DEFAULT_LOG_FILE = 'selenium-debug.log';
function SeleniumServer(settings, callback) {
this.settings = settings;
this.onStarted = callback;
this.port = this.settings.selenium.port || DEFAULT_PORT;
this.host = this.settings.selenium.host;
this.output = '';
this.error_out = '';
this.process = null;
}
SeleniumServer.prototype.setCliArgs = function() {
this.cliOpts = [
'-jar', this.settings.selenium.server_path,
'-port', this.port
];
if (this.host && this.host != DEFAULT_HOST) {
this.cliOpts.push('-host', this.host);
Logger.warn('\n\n[!] The host argument has been removed from Selenium 3 and will throw an exception.\n');
}
if (typeof this.settings.selenium.cli_args == 'object') {
var cli_args = this.settings.selenium.cli_args;
for (var keyName in cli_args) {
if (cli_args[keyName]) {
var property = '';
if (keyName.indexOf('-D') !== 0) {
property += '-D';
}
property += keyName + '=' + cli_args[keyName];
this.cliOpts.unshift(property);
}
}
}
};
SeleniumServer.prototype.start = function() {
if (this.settings.output) {
process.stdout.write(Logger.colors.light_purple('Starting selenium server' + (this.settings.parallelMode ? ' in parallel mode' : '') +'... '));
}
this.setCliArgs();
this.process = child_process.spawn('java', this.cliOpts, {
stdio: ['ignore', 'pipe', 'pipe']
});
this.process.host = this.host;
this.process.port = this.port;
this.exitHandlerFn = this.exitHandler.bind(this);
this.process.on('error', this.onError.bind(this));
this.process.on('exit', this.exitHandlerFn);
this.process.on('close', this.closeHandler.bind(this));
this.process.stdout.on('data', this.onStdoutData.bind(this));
this.process.stderr.on('data', this.onStderrData.bind(this));
};
SeleniumServer.prototype.stop = function(callback) {
if (!this.process || this.process.killed) {
Logger.warn('Selenium process is not started.');
callback(false);
return;
}
try {
this.process.kill();
this.writeLogFile(callback);
} catch (e) {
Logger.warn('Selenium process could not be stopped.');
console.log(e);
callback();
}
};
SeleniumServer.prototype.exitHandler = function (code) {
this.onStarted('Could not start Selenium.', null, this.error_out, code);
};
SeleniumServer.prototype.closeHandler = function() {
Logger.info('Selenium process finished.');
};
SeleniumServer.prototype.writeLogFile = function(callback) {
if (this.settings.selenium.log_path === false) {
callback();
return;
}
if (typeof this.settings.selenium.log_path == 'undefined') {
this.settings.selenium.log_path = '';
}
var filePath = path.resolve(path.join(this.settings.selenium.log_path, DEFAULT_LOG_FILE));
fs.writeFile(filePath, this.output, function(err) {
if (err) {
console.log(Logger.colors.light_red('\nError writing log file to:'), err.path);
}
callback();
});
};
SeleniumServer.prototype.onError = function(err) {
if (err.code == 'ENOENT') {
console.log(Logger.colors.red('\nAn error occurred while trying to start Selenium. ' +
'Check if JAVA is installed on your machine.'));
console.log(util.inspect(err, false, 1, true));
}
};
SeleniumServer.prototype.onStderrData = function(data) {
this.error_out += data.toString();
this.checkProcessStarted(data);
};
SeleniumServer.prototype.onStdoutData = function(data) {
this.checkProcessStarted(data);
};
SeleniumServer.prototype.checkProcessStarted = function(data) {
var output = data.toString();
this.output += output;
var isStarted = SENTINEL.some(function(item) {
return output.indexOf(item) != -1;
});
if (isStarted) {
var exitHandler = this.exitHandlerFn;
this.process.removeListener('exit', exitHandler);
process.stdout.write(Logger.colors.light_purple('started - PID: ' ) + ' ' +
this.process.pid + '\n' + (this.settings.parallelMode ? '\n' : ''));
this.onStarted(null, this.process);
}
};
module.exports = new (function() {
var server;
this.startServer = function (settings, callback) {
if (!settings.selenium || !settings.selenium.start_process || !settings.selenium.server_path) {
callback();
return;
}
server = new SeleniumServer(settings, callback);
server.start();
};
this.stopServer = function (callback) {
callback = callback || function() {};
if (!server) {
console.log('Selenium server is not running.');
callback();
return;
}
server.stop(callback);
};
})();

166
tests/node_modules/nightwatch/lib/runner/testcase.js generated vendored Normal file
View File

@@ -0,0 +1,166 @@
var path = require('path');
var Q = require('q');
var Logger = require('../util/logger.js');
var Utils = require('../util/utils.js');
var Reporter = require('../runner/reporter.js');
var DEFAULT_UNITTEST_HOOK_TIMEOUT = 2000;
function TestCase(suite, testFn, numRetries, maxRetries) {
this.suite = suite;
this.testFn = testFn;
this.numRetries = numRetries;
this.maxRetries = maxRetries;
this.currentDeferred = null;
this._deferredNext = null;
this.running = false;
this.lastTimerId = null;
this.asyncHookTimeout = this.suite.client.globals('asyncHookTimeout') || DEFAULT_UNITTEST_HOOK_TIMEOUT;
}
TestCase.prototype.print = function () {
var opts = this.suite.options;
if (opts.output && opts.start_session && opts.detailed_output) {
process.stdout.write('\n');
if (this.numRetries > 0) {
console.log('Retrying (' + this.numRetries + '/' + this.maxRetries + '): ',
Logger.colors.red(this.testFn));
} else {
console.log((opts.parallelMode && !opts.live_output ? 'Results for: ' : 'Running: '),
Logger.colors.green(this.testFn));
}
}
return this;
};
TestCase.prototype.getResults = function () {
return this.suite.client.results();
};
TestCase.prototype.getErrors = function () {
return this.suite.client.errors();
};
TestCase.prototype.run = function () {
var self = this;
this.currentDeferred = Q.defer();
this.startTime = new Date().getTime();
this.results = null;
this.errors = null;
this.running = true;
this.suite
.beforeEach()
.then(function() {
self._deferredNext = Q.defer();
self.suite.client.once('complete', function(results, errors) {
if (self.suite.client.options.start_session) {
self._deferredNext.resolve({
results: self.getResults(),
errors: self.getErrors()
});
}
}).on('error', function(result) {
self._deferredNext.reject(result);
});
try {
if (self.suite.client.options.start_session) {
self.suite.module.call(self.testFn, self.suite.client.api());
} else {
var doneFn = self.setDoneCallbackTimer(self.doneCallback.bind(self), self.testFn, function(timeoutId) {
timeoutId.currentTest = self.testFn;
self.lastTimerId = timeoutId;
});
self.doneFn = self.suite.module.callAsync(self.testFn, self.suite.client.api(), doneFn, self.suite.expectedAsyncArgs);
}
} catch (err) {
self.catchHandler(err);
return self._deferredNext.promise;
}
self.suite.client.start();
return self._deferredNext.promise;
})
.then(function onSuccess(response) {
return self.suite.afterEach(response.results, response.errors);
}, function onError(error) {
self.currentDeferred.reject(error);
})
.then(function() {
var time = new Date().getTime() - self.startTime;
self.currentDeferred.resolve({
results: self.getResults(),
errors: self.getErrors(),
time : time
});
self.running = false;
})
.catch(function(error) {
self.currentDeferred.reject(error);
self.running = false;
});
return self.currentDeferred.promise;
};
TestCase.prototype.setDoneCallbackTimer = function(done, fnName, onTimerStarted) {
return Utils.setCallbackTimeout(done, fnName, this.asyncHookTimeout, function(err) {
throw err;
}, onTimerStarted);
};
TestCase.prototype.doneCallback = function(err) {
var self = this;
if (this.lastTimerId) {
clearTimeout(this.lastTimerId);
this.lastTimerId = null;
}
if (this.suite.currentHookTimeoutId) {
clearTimeout(this.suite.currentHookTimeoutId);
this.suite.currentHookTimeoutId = null;
}
setImmediate(function() {
if (self._deferredNext) {
self._deferredNext.resolve({
results : self.getResults(),
errors : self.getErrors()
});
}
});
if (Utils.isErrorObject(err)) {
this.setFailed(err);
}
if (!this.suite.options.output || !this.currentDeferred) {
return;
}
if (!this.suite.options.start_session) {
this.currentDeferred.promise.then(function(results) {
console.log(Reporter.getTestOutput(err, this.testFn, results.time));
}.bind(this));
}
if (err && this.suite.options.start_session) {
this.suite.client.terminate();
}
};
TestCase.prototype.setFailed = function(err) {
this.suite.client.handleException(err);
};
TestCase.prototype.catchHandler = function(err) {
this.doneCallback(err);
};
module.exports = TestCase;

589
tests/node_modules/nightwatch/lib/runner/testsuite.js generated vendored Normal file
View File

@@ -0,0 +1,589 @@
var path = require('path');
var util = require('util');
var events = require('events');
var Q = require('q');
var ClientManager = require('./clientmanager.js');
var Module = require('./module.js');
var TestCase = require('./testcase.js');
var Logger = require('../util/logger.js');
var Utils = require('../util/utils.js');
var DEFAULT_ASYNC_HOOK_TIMEOUT = 10000;
function noop() {}
function TestSuite(modulePath, fullPaths, opts, addtOpts) {
events.EventEmitter.call(this);
this['@testCase'] = null;
this.deferred = Q.defer();
this.module = new Module(modulePath, opts, addtOpts);
this.setTestResult();
this.currentTest = '';
this.module.setReportKey(fullPaths, addtOpts.src_folders);
this.options = opts;
this.testMaxRetries = addtOpts.retries || 0;
this.suiteMaxRetries = addtOpts.suite_retries || 0;
this.suiteRetries = 0;
this.suiteName = this.module.getTestSuiteName() || Utils.getTestSuiteName(this.module.moduleKey);
this.setupClient();
this.expectedAsyncArgs = this.options.compatible_testcase_support ? 1 : 2;
this.asyncHookTimeout = this.client.globals('asyncHookTimeout') || DEFAULT_ASYNC_HOOK_TIMEOUT;
this.currentHookTimeoutId = null;
this.initHooks();
}
util.inherits(TestSuite, events.EventEmitter);
TestSuite.prototype.setupClient = function() {
this.updateDesiredCapabilities();
this.client = new ClientManager();
var self = this;
this.client.on('error', function(err) {
self.deferred.reject(err);
});
this.client.init(this.options);
this.client.api('currentEnv', this.options.currentEnv);
this.module.set('client', this.client.api());
if (this.client.endSessionOnFail() && !this.module.endSessionOnFail()) {
this.client.endSessionOnFail(false);
}
};
TestSuite.prototype.initHooks = function() {
var self = this;
var context = this.module.get();
var argsCount;
this.hooks = {};
var hooks = ['before', 'after', ['beforeEach', 'setUp']];
hooks.forEach(function(item) {
var key;
if (Array.isArray(item)) {
key = item[0];
} else {
key = item;
}
var index = self.testResults.steps.indexOf(key);
if (index === -1 && Array.isArray(item)) {
index = self.testResults.steps.indexOf(item[1]);
}
if (Array.isArray(item) && (item.length == 3)) {
argsCount = item[2];
} else {
argsCount = self.expectedAsyncArgs;
}
self.hooks[key] = (function(item, argsCount) {
return function() {
return self.makePromise(function(done) {
var callbackDeffered = false;
var called = false;
var originalFn = self.module.get(key);
var doneFn = function() {
called = true;
if (callbackDeffered) {
return;
}
return done();
};
self.runHookMethod(item, context, argsCount, key, doneFn, this.deferred);
if (originalFn && (originalFn.length == self.expectedAsyncArgs) && self.client.shouldRestartQueue()) {
callbackDeffered = true;
var asyncDoneFn = function(err) {
if (called) {
done();
} else {
callbackDeffered = false;
}
};
if (key == 'before' || key == 'beforeEach') {
self.client.start(asyncDoneFn);
} else if (key == 'after') {
self.client.restartQueue(asyncDoneFn);
}
}
});
};
})(item, argsCount);
if (index > -1) {
self.testResults.steps.splice(index, 1);
self.module.removeKey(item);
}
});
this.initAfterEachHook();
};
TestSuite.prototype.initAfterEachHook = function() {
var self = this;
var api = this.client.api();
var module = this.module.get();
var key = 'afterEach';
var hookFn;
var index = self.testResults.steps.indexOf(key);
if (!module[key]) {
// backwards compatibility
key = 'tearDown';
index = self.testResults.steps.indexOf(key);
}
if (module[key]) {
hookFn = module[key];
if (index > -1) {
// not running with --testcase
self.testResults.steps.splice(index, 1);
self.module.removeKey(key);
}
} else {
hookFn = function() {};
}
var expectedArgs = hookFn.length;
this.hooks.afterEach = function(results, errors) {
self.module
.set('results', results)
.set('errors', errors);
var asyncFn, asyncArgsCount;
if (expectedArgs <= 1) {
asyncArgsCount = 1;
} else {
asyncArgsCount = 2;
}
asyncFn = Utils.makeFnAsync(asyncArgsCount, hookFn, module);
return self.makePromise(function(done, deferred) {
var doneFn = self.adaptDoneCallback(done, 'afterEach', deferred);
self.client.publishTestResults(self.currentTest, self.module.get('results'), errors);
if (expectedArgs < 2) {
// user has only supplied the done callback argument (pre v0.6 behaviour), e.g.:
// afterEach : function(done) { ... }
asyncFn.call(module, doneFn);
} else {
// user has supplied both the client and the done callback argument (v0.6 behaviour), e.g.:
// afterEach : function(browser, done) { ... }
// in which case we may need to restart the queue if any selenium related actions are added
if (self.options.compatible_testcase_support) {
asyncFn.call(module, doneFn);
} else {
asyncFn.call(module, api, function() {
doneFn();
});
}
// this will restart the queue if needed
self.client.checkQueue();
}
});
};
return this;
};
TestSuite.prototype.run = function() {
var self = this;
this.print();
if (this.module.isDisabled()) {
if (this.options.output) {
console.log(Logger.colors.cyan(this.module.getName()), 'module is disabled, skipping...');
}
this.deferred.resolve(this.testResults);
} else {
this.setCurrentTest();
this.globalBeforeEach()
.then(function() {
if (self.client.terminated() && self.client.skipTestcasesOnFail()) {
self.testResults.errmessages = self.client.errors();
self.deferred.resolve(self.testResults);
return null;
}
return self.runTestSuiteModule();
})
.then(function(results) {
return self.globalAfterEach();
})
.then(function() {
self.deferred.resolve(self.testResults);
})
.catch(function(e) {
self.deferred.reject(e);
});
}
return this.deferred.promise;
};
TestSuite.prototype.getCurrentTestCase = function() {
return this['@testCase'];
};
TestSuite.prototype.getReportKey = function() {
return this.module.moduleKey;
};
TestSuite.prototype.getGroupName = function() {
return this.module.groupName;
};
TestSuite.prototype.printResult = function(startTime) {
return this.client.print(startTime);
};
TestSuite.prototype.shouldRetrySuite = function() {
return this.suiteMaxRetries > this.suiteRetries && (this.testResults.failed > 0 || this.testResults.errors > 0);
};
TestSuite.prototype.setTestResult = function() {
this.testResults = {};
this.testResults.steps = this.module.keys.slice(0);
this.clearTestResult();
return this;
};
TestSuite.prototype.clearTestResult = function() {
this.testResults.passed = 0;
this.testResults.failed = 0;
this.testResults.errors = 0;
this.testResults.skipped = 0;
this.testResults.tests = 0;
this.testResults.testcases = {};
this.testResults.timestamp = new Date().toUTCString();
this.testResults.time = 0;
return this;
};
TestSuite.prototype.clearResult = function() {
this.clearTestResult();
this.client.clearGlobalResult();
return this;
};
TestSuite.prototype.printRetry = function() {
if (this.options.output) {
console.log('Retrying: ',
Logger.colors.red('[' + this.suiteName + '] Test Suite '),
'(' + this.suiteRetries + '/' + this.suiteMaxRetries + '): ');
}
};
TestSuite.prototype.retryTestSuiteModule = function() {
this.client.resetTerminated();
this.clearResult();
this.suiteRetries +=1;
this.resetTestCases();
this.printRetry();
return this.globalBeforeEach().then(function() {
return this.runTestSuiteModule();
}.bind(this));
};
TestSuite.prototype.runTestSuiteModule = function() {
var self = this;
return this.before()
.then(function() {
return self.runNextTestCase();
})
.then(function(skipped) {
return self.after();
})
.then(function() {
return self.client.checkQueue();
})
.then(function() {
return self.shouldRetrySuite();
})
.then(function(shouldRetrySuite) {
if (shouldRetrySuite) {
return self.globalAfterEach().then(function() {
return self.retryTestSuiteModule();
});
}
})
.catch(function(e) {
self.testResults.errors++;
throw e;
});
};
TestSuite.prototype.onTestCaseFinished = function(results, errors, time) {
this.testResults.time += time;
this.testResults.testcases[this.currentTest] = this.testResults.testcases[this.currentTest] || {};
this.testResults.testcases[this.currentTest].time = (time/1000).toPrecision(4);
this.emit('testcase:finished', results, errors, time);
};
TestSuite.prototype.resetTestCases = function() {
var self = this;
this.module.resetKeys();
Object.keys(this.hooks).forEach(function(hook) {
self.module.removeKey(hook);
});
};
TestSuite.prototype.setCurrentTest = function() {
var moduleKey = this.getReportKey();
this.client.clearGlobalResult();
this.client.api('currentTest', {
name : this.currentTest,
module : moduleKey.replace(path.sep , '/'),
results : this.testResults,
group : this.getGroupName()
});
return this;
};
TestSuite.prototype.runNextTestCase = function(deferred) {
this.currentTest = this.module.getNextKey();
this.setCurrentTest();
deferred = deferred || Q.defer();
if (this.currentTest) {
this.testResults.steps.splice(this.testResults.steps.indexOf(this.currentTest), 1);
this.runTestCase(this.currentTest, deferred, 0);
} else {
deferred.resolve();
}
return deferred.promise;
};
TestSuite.prototype.runTestCase = function(currentTest, deferred, numRetries) {
var self = this;
this['@testCase'] = new TestCase(this, currentTest, numRetries, this.testMaxRetries);
if (self.client.terminated() && self.client.skipTestcasesOnFail()) {
deferred.resolve(self.module.keys);
return deferred;
}
this['@testCase'].print().run().then(function(response) {
var foundFailures = !!(response.results.failed || response.results.errors);
if (foundFailures && numRetries < self.testMaxRetries) {
numRetries++;
self.client.resetTerminated();
self.clearResult();
self.runTestCase(currentTest, deferred, numRetries);
} else if (foundFailures && self.suiteRetries < self.suiteMaxRetries) {
deferred.resolve(self.module.keys);
} else {
self.onTestCaseFinished(response.results, response.errors, response.time);
if (self.client.terminated() && self.client.skipTestcasesOnFail()) {
deferred.resolve(self.module.keys);
} else {
process.nextTick(function() {
self.runNextTestCase(deferred);
});
}
}
}, function(error) {
deferred.reject(error);
});
return deferred;
};
//////////////////////////////////////////////////////////////////////
// Test suite hooks
//////////////////////////////////////////////////////////////////////
TestSuite.prototype.before = function() {
return this.hooks.before();
};
TestSuite.prototype.after = function() {
return this.hooks.after();
};
TestSuite.prototype.beforeEach = function() {
return this.hooks.beforeEach();
};
TestSuite.prototype.afterEach = function(results, errors) {
return this.hooks.afterEach(results, errors);
};
//////////////////////////////////////////////////////////////////////
// Global hooks
//////////////////////////////////////////////////////////////////////
TestSuite.prototype.globalBeforeEach = function() {
return this.adaptGlobalHook('beforeEach');
};
TestSuite.prototype.globalAfterEach = function() {
return this.adaptGlobalHook('afterEach');
};
TestSuite.prototype.adaptGlobalHook = function(hookName) {
return this.makePromise(function(done, deffered) {
var callbackDeffered = false;
var doneFn = function(err) {
if (callbackDeffered) {
return;
}
var fn = this.adaptDoneCallback(done, 'global ' + hookName, deffered);
return this.onGlobalHookError(err, true, fn);
}.bind(this);
var argsCount;
var expectedCount = 1;
if (Utils.checkFunction(hookName, this.options.globals)) {
argsCount = this.options.globals[hookName].length;
expectedCount = argsCount == 2 ? 2 : 1;
}
var globalHook = this.adaptHookMethod(hookName, this.options.globals, expectedCount);
var args = [doneFn];
if (argsCount == 2) {
args.unshift(this.client.api());
}
globalHook.apply(this.options.globals, args);
if (this.client.shouldRestartQueue()) {
callbackDeffered = true;
if (hookName == 'before' || hookName == 'beforeEach') {
this.client.start(function(err) {
return this.onGlobalHookError(err, false, done);
//if (err) {
// this.addErrorToResults(err);
//}
//done(err);
}.bind(this));
} else {
this.client.restartQueue(function() {
done();
});
}
}
});
};
TestSuite.prototype.onGlobalHookError = function(err, hookErr, done) {
if (err && err.message) {
this.testResults.errors++;
this.testResults.errmessages = [err.message];
}
return done(err, hookErr);
};
TestSuite.prototype.addErrorToResults = function(err) {
this.testResults.errors++;
this.testResults.errmessages = [err.message];
return this;
};
TestSuite.prototype.adaptDoneCallback = function(done, hookName, deferred) {
return Utils.setCallbackTimeout(done, hookName, this.asyncHookTimeout, function(err) {
deferred.reject(err);
}, function(timeoutId) {
this.currentHookTimeoutId = timeoutId;
}.bind(this));
};
//////////////////////////////////////////////////////////////////////
// Utilities
//////////////////////////////////////////////////////////////////////
TestSuite.prototype.makePromise = function (fn) {
var deferred = Q.defer();
try {
fn.call(this, function(err, hookErr) {
// in case of an exception thrown inside a global hook, we need to reject the promise here
if (hookErr && Utils.isErrorObject(err)) {
deferred.reject(err);
} else {
deferred.resolve();
}
}, deferred);
} catch (e) {
deferred.reject(e);
}
return deferred.promise;
};
TestSuite.prototype.updateDesiredCapabilities = function() {
this.options.desiredCapabilities = this.options.desiredCapabilities || {};
if (this.options.sync_test_names || (typeof this.options.sync_test_names == 'undefined')) {
// optionally send the local test name (derived from filename)
// to the remote selenium server. useful for test reporting in cloud service providers
this.options.desiredCapabilities.name = this.suiteName;
}
if (this.module.desiredCapabilities()) {
for (var capability in this.module.desiredCapabilities()) {
if (this.module.desiredCapabilities().hasOwnProperty(capability)) {
this.options.desiredCapabilities[capability] = this.module.desiredCapabilities(capability);
}
}
}
};
TestSuite.prototype.print = function() {
if (this.options.output) {
var testSuiteDisplay;
if (this.options.start_session) {
testSuiteDisplay = '[' + this.suiteName + '] Test Suite';
} else {
testSuiteDisplay = this.module.getTestSuiteName() || this.module.moduleKey;
}
if (this.options.test_worker && !this.options.live_output) {
process.stdout.write('\\n');
}
var output = '\n' + Logger.colors.cyan(testSuiteDisplay) + '\n' + Logger.colors.purple(new Array(testSuiteDisplay.length + 5).join('='));
console.log(output);
}
};
TestSuite.prototype.runHookMethod = function(fn, context, asyncArgCount, hookName, doneFn, deferred) {
var hookFn = this.adaptHookMethod(fn, context, asyncArgCount);
doneFn = Utils.setCallbackTimeout(doneFn, hookName, this.asyncHookTimeout, function(err) {
this.addErrorToResults(err);
this.deferred.resolve(this.testResults);
}.bind(this));
if (this.options.compatible_testcase_support) {
return hookFn.call(context, doneFn);
}
return hookFn.call(context, this.client.api(), doneFn);
};
TestSuite.prototype.adaptHookMethod = function(fn, context, asyncArgCount) {
var hookFn;
if (Array.isArray(fn) && (fn.length >= 2)) {
hookFn = Utils.checkFunction(fn[0], context) || Utils.checkFunction(fn[1], context) || noop;
} else {
hookFn = Utils.checkFunction(fn, context) || noop;
}
return Utils.makeFnAsync(asyncArgCount, hookFn, context);
};
module.exports = TestSuite;

174
tests/node_modules/nightwatch/lib/runner/walk.js generated vendored Normal file
View File

@@ -0,0 +1,174 @@
var path = require('path');
var fs = require('fs');
var minimatch = require('minimatch');
var fileMatcher = require('./filematcher.js');
var Matchers = {};
function isFolderExcluded(resource, opts) {
if (!opts.exclude) {
return false;
}
if (Matchers.exclude) {
return Matchers.exclude.some(function(item) {
if (item.indexOf(resource) === -1) {
return false;
}
try {
return fs.statSync(item).isDirectory();
} catch (err) {
return false;
}
});
}
return false;
}
function addMatcher(type, filePath, opts) {
Matchers[type] = Matchers[type] || [];
var matchers = fileMatcher[type].adaptFilePath(filePath, opts[type]);
if (Array.isArray(matchers)) {
Matchers[type].push.apply(Matchers[type], matchers);
} else {
Matchers[type].push(matchers);
}
}
function walk(dir, done, opts) {
var results = [];
fs.readdir(dir, function(err, list) {
if (err) {
return done(err);
}
var pending = list.length;
if (pending === 0) {
return done(null, results);
}
list.forEach(function(resource) {
resource = path.join(dir, resource);
fs.stat(resource, function(err, stat) {
if (stat && stat.isDirectory()) {
var dirName = resource.split(path.sep).slice(-1)[0];
var isExcluded = isFolderExcluded(resource, opts); // prevent loading of files from an excluded folder
var isSkipped = opts.skipgroup && opts.skipgroup.indexOf(dirName) > -1;
if (isExcluded || isSkipped) {
pending = pending-1;
} else {
walk(resource, function(err, res) {
results = results.concat(res);
pending = pending-1;
if (!pending) {
done(null, results);
}
}, opts);
}
} else {
results.push(resource);
pending = pending-1;
if (!pending) {
done(null, results);
}
}
});
});
});
}
module.exports = {
readPaths : function (paths, cb, opts) {
Matchers.exclude = [];
Matchers.filter = [];
var allmodules = [];
var extensionPattern = /\.js$/;
var modulePaths = paths.slice(0);
(function readSourcePaths() {
var sourcePath = modulePaths.shift();
fs.stat(sourcePath, function(err, stat) {
if (err) {
return cb(err);
}
if (stat.isFile() && extensionPattern.test(sourcePath)) {
sourcePath = sourcePath.replace(extensionPattern, '');
if (allmodules.indexOf(sourcePath) === -1) {
allmodules.push(sourcePath);
}
if (modulePaths.length) {
readSourcePaths();
} else {
cb(null, allmodules);
}
} else if (stat.isDirectory()) {
if (opts.exclude) {
addMatcher('exclude', sourcePath, opts);
}
if (opts.filter) {
addMatcher('filter', sourcePath, opts);
}
walk(sourcePath, function (err, list) {
if (err) {
return cb(err);
}
list.sort();
var modules = list.filter(function (filePath) {
if (!extensionPattern.test(filePath)) {
return false;
}
if (opts.exclude && fileMatcher.exclude.match(filePath, Matchers.exclude)) {
return false;
}
if (opts.filter && !fileMatcher.filter.match(filePath, Matchers.filter)) {
return false;
}
var filename = filePath.split(path.sep).slice(-1)[0];
if (opts.filename_filter) {
return minimatch(filename, opts.filename_filter);
}
if (opts.tag_filter || opts.skiptags) {
return fileMatcher.tags.match(filePath, opts);
}
return true;
});
modules.forEach(function(item) {
var filename = item.replace(extensionPattern, '');
if (allmodules.indexOf(filename) === -1) {
allmodules.push(filename);
}
});
if (modulePaths.length) {
readSourcePaths();
} else {
cb(null, allmodules);
}
}, opts);
} else {
if (modulePaths.length) {
readSourcePaths();
} else {
cb(null, allmodules);
}
}
});
})();
}
};

62
tests/node_modules/nightwatch/lib/util/keys.json generated vendored Normal file
View File

@@ -0,0 +1,62 @@
{
"NULL" : "\uE000",
"CANCEL" : "\uE001",
"HELP" : "\uE002",
"BACK_SPACE" : "\uE003",
"TAB" : "\uE004",
"CLEAR" : "\uE005",
"RETURN" : "\uE006",
"ENTER" : "\uE007",
"SHIFT" : "\uE008",
"CONTROL" : "\uE009",
"ALT" : "\uE00A",
"PAUSE" : "\uE00B",
"ESCAPE" : "\uE00C",
"SPACE" : "\uE00D",
"PAGEUP" : "\uE00E",
"PAGEDOWN" : "\uE00F",
"END" : "\uE010",
"HOME" : "\uE011",
"LEFT_ARROW" : "\uE012",
"UP_ARROW" : "\uE013",
"RIGHT_ARROW" : "\uE014",
"DOWN_ARROW" : "\uE015",
"ARROW_LEFT" : "\uE012",
"ARROW_UP" : "\uE013",
"ARROW_RIGHT" : "\uE014",
"ARROW_DOWN" : "\uE015",
"INSERT" : "\uE016",
"DELETE" : "\uE017",
"SEMICOLON" : "\uE018",
"EQUALS" : "\uE019",
"NUMPAD0" : "\uE01A",
"NUMPAD1" : "\uE01B",
"NUMPAD2" : "\uE01C",
"NUMPAD3" : "\uE01D",
"NUMPAD4" : "\uE01E",
"NUMPAD5" : "\uE01F",
"NUMPAD6" : "\uE020",
"NUMPAD7" : "\uE021",
"NUMPAD8" : "\uE022",
"NUMPAD9" : "\uE023",
"MULTIPLY" : "\uE024",
"ADD" : "\uE025",
"SEPARATOR" : "\uE026",
"SUBTRACT" : "\uE027",
"DECIMAL" : "\uE028",
"DIVIDE" : "\uE029",
"F1" : "\uE031",
"F2" : "\uE032",
"F3" : "\uE033",
"F4" : "\uE034",
"F5" : "\uE035",
"F6" : "\uE036",
"F7" : "\uE037",
"F8" : "\uE038",
"F9" : "\uE039",
"F10" : "\uE03A",
"F11" : "\uE03B",
"F12" : "\uE03C",
"COMMAND" : "\uE03D",
"META" : "\uE03D"
}

197
tests/node_modules/nightwatch/lib/util/logger.js generated vendored Normal file
View File

@@ -0,0 +1,197 @@
var util = require('util');
var Settings = {
log_timestamp : false,
colors : true,
enabled : true
};
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
'Oct', 'Nov', 'Dec'];
function getDate() {
var now = new Date();
return [now.toLocaleDateString(), now.toLocaleTimeString()].join(' ');
}
function pad(n) {
return n < 10 ? '0' + n.toString(10) : n.toString(10);
}
// 26 Feb 16:19:34
function timestamp() {
var d = new Date();
var time = [pad(d.getHours()),
pad(d.getMinutes()),
pad(d.getSeconds())].join(':');
return [d.getDate(), months[d.getMonth()], time].join(' ');
}
function getPrefix(tag, severity) {
var levels = ['EMERG', 'ALERT', 'CRITICAL', 'ERROR', 'WARNING', 'NOTICE', 'INFO', 'DEBUG'];
return tag + ' ' + levels[severity] +' - ';
}
function ConsoleColor() {
var self = this;
var mappings = {
blue : '0;34',
light_blue : '1;34'
};
this.background = new Background();
this.foreground_colors = {};
this.foreground_colors['black'] = '0;30';
this.foreground_colors['dark_gray'] = '1;30';
this.foreground_colors['blue'] = '0;34';
this.foreground_colors['light_blue'] = '1;34';
this.foreground_colors['green'] = '0;32';
this.foreground_colors['light_green'] = '1;32';
this.foreground_colors['cyan'] = '0;36';
this.foreground_colors['light_cyan'] = '1;36';
this.foreground_colors['red'] = '0;31';
this.foreground_colors['light_red'] = '1;31';
this.foreground_colors['purple'] = '0;35';
this.foreground_colors['light_purple'] = '1;35';
this.foreground_colors['brown'] = '0;33';
this.foreground_colors['yellow'] = '1;33';
this.foreground_colors['light_gray'] = '0;37';
this.foreground_colors['white'] = '1;37';
this.foreground_colors['stack_trace'] = '0;90';
this.background_colors = {};
this.background_colors['black'] = '40';
this.background_colors['red'] = '41';
this.background_colors['green'] = '42';
this.background_colors['yellow'] = '43';
this.background_colors['blue'] = '44';
this.background_colors['magenta'] = '45';
this.background_colors['cyan'] = '46';
this.background_colors['light_gray'] = '47';
Object.keys(this.foreground_colors).forEach(function(k) {
ConsoleColor.prototype[k.toLowerCase()] = function Foreground(text, background) {
var string = '\033[' + self.foreground_colors[k.toLowerCase()] + 'm';
if (background !== undefined) {
string += background();
}
string += text + '\033[0m';
return string;
};
});
Object.keys(this.background_colors).forEach(function(k) {
Background.prototype[k.toLowerCase()] = function Background(text) {
return '\033[' + self.background_colors[k.toLowerCase()] + 'm';
};
});
return this;
}
function Background() { return this; }
var colors = new ConsoleColor();
function logObject(obj) {
console.log(util.inspect(obj, {
showHidden : false,
depth : 3,
colors : Settings.colors
}));
}
function logTimestamp() {
if (Settings.log_timestamp) {
return colors.white(timestamp()) + ' ';
}
return '';
}
function logMessage(type, message, args) {
if (!message || !Settings.enabled) {
return;
}
var messageStr = '';
var timestamp = logTimestamp();
switch (type) {
case 'ERROR':
messageStr = colors.yellow(type, colors.background.dark_gray) +' '+
timestamp + colors.light_green(message);
break;
case 'INFO':
messageStr = colors.light_purple(type, colors.background.black) +' '+
timestamp + colors.light_cyan(message);
break;
case 'LOG':
messageStr = colors.white(type+' ', colors.background.black) +' '+
timestamp + colors.white(message);
break;
case 'WARN':
messageStr = colors.light_green(type, colors.background.black) +' '+
timestamp + colors.light_green(message);
break;
}
process.stdout.write(messageStr);
if (args.length > 0) {
var inlineArgs = [];
args.forEach(function(item) {
if (Object.prototype.toString.call(item) === '[object Object]' && Object.keys(item).length > 0) {
if (inlineArgs.length) {
console.log.apply(console, inlineArgs);
inlineArgs = [];
}
logObject(item);
} else {
inlineArgs.push(item);
}
});
if (inlineArgs.length) {
process.stdout.write(' ');
console.log.apply(console, inlineArgs);
inlineArgs = [];
}
} else {
process.stdout.write('\n');
}
}
exports.info = function(message) {
var args = Array.prototype.slice.call(arguments, 1);
logMessage('INFO', message, args);
};
exports.log = function(message) {
var args = Array.prototype.slice.call(arguments, 1);
logMessage('LOG', message, args);
};
exports.warn = function(message) {
var args = Array.prototype.slice.call(arguments, 1);
logMessage('WARN', message, args);
};
exports.error = function(message) {
var args = Array.prototype.slice.call(arguments, 1);
logMessage('ERROR', message, args);
};
exports.disableColors = function () {
Settings.colors = false;
Object.keys(ConsoleColor.prototype).forEach(function (color) {
ConsoleColor.prototype[color] = function (text) {
return text;
};
});
};
exports.disable = function() {
Settings.enabled = false;
};
exports.enable = function() {
Settings.enabled = true;
};
exports.isEnabled = function() {
return Settings.enabled;
};
exports.colors = colors;

288
tests/node_modules/nightwatch/lib/util/utils.js generated vendored Normal file
View File

@@ -0,0 +1,288 @@
var path = require('path');
var fs = require('fs');
var Util = module.exports = {};
var Logger = require('./logger.js');
var formatRegExp = /%[sdj%]/g;
var testSuiteNameRegxp = /(_|-|\.)*([A-Z]*)/g;
var nameSeparatorRegxp = /(\s|\/)/;
Util.formatElapsedTime = function(timeMs, includeMs) {
var seconds = timeMs/1000;
return (seconds < 1 && timeMs + 'ms') ||
(seconds > 1 && seconds < 60 && (seconds + 's')) ||
(Math.floor(seconds/60) + 'm' + ' ' + Math.floor(seconds%60) + 's' + (includeMs ? (' / ' + timeMs + 'ms') : ''));
};
/**
* Wrap a synchronous function, turning it into an async fn with a callback as
* the last argument if necessary. `asyncArgCount` is the expected argument
* count if `fn` is already asynchronous.
*
* @param {number} asyncArgCount
* @param {function} fn
* @param {object} [context]
*/
Util.makeFnAsync = function (asyncArgCount, fn, context) {
if (fn.length === asyncArgCount) {
return fn;
}
return function() {
var args = Array.prototype.slice.call(arguments, 0);
var done = args.pop();
context = context || null;
fn.apply(context, args);
done();
};
};
/**
* Waits a number of ms for a `done` callback to be invoked
*
* @param {function} done
* @param {string} fnName
* @param {number} timeMs
* @param {function} [onCatch]
* @param {function} [onTimerStarted]
* @return {Function}
*/
Util.setCallbackTimeout = function(done, fnName, timeMs, onCatch, onTimerStarted) {
var timeout = setTimeout(function() {
if (onCatch) {
var err = new Error('done() callback timeout of '+ timeMs +' ms was reached while executing "' + fnName + '".' +
' Make sure to call the done() callback when the operation finishes.');
onCatch(err, fnName, timeout);
}
}, timeMs);
if (onTimerStarted) {
onTimerStarted(timeout);
}
return function(ex) {
clearTimeout(timeout);
done(ex, true);
};
};
Util.checkFunction = function(name, parent) {
return parent && (typeof parent[name] == 'function') && parent[name] || false;
};
Util.getTestSuiteName = function(moduleName) {
var words;
moduleName = moduleName.replace(testSuiteNameRegxp, function(match, $0, $1, offset, string) {
if (!match) {
return '';
}
return (offset > 0 && (string.charAt(offset-1) !== ' ') ? ' ':'') + $1;
});
words = moduleName.split(nameSeparatorRegxp).map(function(word, index, matches) {
if (word == '/') {
return ' / ';
}
return word.charAt(0).toUpperCase() + word.substr(1);
});
return words.join('');
};
/**
* A smaller version of util.format that doesn't support json and
* if a placeholder is missing, it is omitted instead of appended
*
* @param f
* @returns {string}
*/
Util.format = function format(f) {
var i = 1;
var args = arguments;
var len = args.length;
return String(f).replace(formatRegExp, function(x) {
if (x === '%%') {
return '%';
}
if (i >= len) {
return x;
}
switch (x) {
case '%s':
return String(args[i++]);
case '%d':
return Number(args[i++]);
default:
return x;
}
});
};
Util.getScreenshotFileName = function(currentTest, is_error, screenshots_path) {
var prefix = currentTest.module + '/' + currentTest.name;
prefix = prefix.replace(/\s/g, '-').replace(/"|'/g, '');
prefix += is_error ? '_ERROR' : '_FAILED';
var d = new Date();
var dateParts = d.toString().replace(/:/g,'').split(' ');
dateParts.shift();
dateParts.pop();
var dateStamp = dateParts.join('-');
return path.resolve(path.join(screenshots_path, prefix + '_' + dateStamp + '.png'));
};
Util.isObject = function(obj) {
return (typeof obj == 'object') && (obj !== null);
};
Util.processAsyncQueue = function(concurrency, files, cb) {
var maxWorkers = Math.min(concurrency, files.length);
var queue = [];
var add = function(item) {
queue.push(item);
};
var workers = 0;
var index = 0;
var next = function() {
workers -= 1;
process();
};
for (var i = 0; i < files.length; i++) {
add(files[i]);
}
var process = function() {
while (workers < maxWorkers) {
workers += 1;
if (queue.length) {
var item = queue.shift();
cb(item, index++, next);
}
}
};
process();
};
Util.getModuleKey = function(filePath, srcFolders, fullPaths) {
var modulePathParts = filePath.split(path.sep);
var diffInFolder = '';
var folder = '';
var parentFolder = '';
var moduleName = modulePathParts.pop();
filePath = modulePathParts.join(path.sep);
if (srcFolders) {
for (var i = 0; i < srcFolders.length; i++) {
folder = path.resolve(srcFolders[i]);
if (fullPaths.length > 1) {
parentFolder = folder.split(path.sep).pop();
}
if (filePath.indexOf(folder) === 0) {
diffInFolder = filePath.substring(folder.length + 1);
break;
}
}
}
return path.join(parentFolder, diffInFolder, moduleName);
};
Util.showStackTraceWithHeadline = function(headline, stack, isErr) {
var logMethod = isErr ? 'error' : 'log';
var stackTrace;
console[logMethod](Logger.colors.red(headline));
if (Array.isArray(stack) && stack.length > 0) {
stackTrace = Util.stackTraceFilter(stack);
} else {
stackTrace = stack;
}
if (stack) {
console[logMethod](Logger.colors.stack_trace(stackTrace));
}
};
Util.stackTraceFilter = function(parts) {
var stack = parts.reduce(function(list, line) {
if (contains(line, [
'node_modules',
'(node.js:',
'(events.js:'
])) {
return list;
}
list.push(line);
return list;
}, []);
return stack.join('\n');
};
var indentRegex = /^/gm;
Util.showStackTrace = function(stack) {
var parts = stack.split('\n');
var headline = parts.shift();
console.log(Logger.colors.red(headline.replace(indentRegex, ' ')));
if (parts.length > 0) {
var result = Util.stackTraceFilter(parts);
console.log(Logger.colors.stack_trace(result.replace(indentRegex, ' ')));
}
};
Util.symbols = (function() {
var ok = String.fromCharCode(10004);
var fail = String.fromCharCode(10006);
if (process.platform === 'win32') {
ok = '\u221A';
fail = '\u00D7';
}
return {
ok: ok,
fail: fail
};
})();
Util.isErrorObject = function(err) {
return err instanceof Error || Object.prototype.toString.call(err) === '[object Error]';
};
// util to replace deprecated fs.existsSync
Util.dirExistsSync = function (path) {
try {
return fs.statSync(path).isDirectory();
} catch (e) {
return false;
}
};
Util.fileExistsSync = function (path) {
try {
return fs.statSync(path).isFile();
} catch (e) {
return false;
}
};
function contains(str, text) {
if (Array.isArray(text)) {
for (var i = 0; i < text.length; i++) {
if (contains(str, text[i])) {
return true;
}
}
}
return str.indexOf(text) > -1;
}

View File

@@ -0,0 +1 @@
../mocha-nightwatch/bin/_mocha

Some files were not shown because too many files have changed in this diff Show More