Current Dev State

This commit is contained in:
Tim Lorsbach
2025-06-23 20:13:54 +02:00
parent b4f9bb277d
commit ded50edaa2
22617 changed files with 4345095 additions and 174 deletions

View File

@ -0,0 +1,432 @@
'use strict';
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
var _ejs = require('ejs');
var _ejs2 = _interopRequireDefault(_ejs);
var _npmInstallPackage = require('npm-install-package');
var _npmInstallPackage2 = _interopRequireDefault(_npmInstallPackage);
var _inquirer = require('inquirer');
var _inquirer2 = _interopRequireDefault(_inquirer);
var _optimist = require('optimist');
var _optimist2 = _interopRequireDefault(_optimist);
var _deepmerge = require('deepmerge');
var _deepmerge2 = _interopRequireDefault(_deepmerge);
var _launcher = require('./launcher');
var _launcher2 = _interopRequireDefault(_launcher);
var _ = require('../');
var _package = require('../package.json');
var _package2 = _interopRequireDefault(_package);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* 💌 Dear contributors 💌
* ========================================================================
* If you are awesome and have created a new reporter, service or framework
* adaption feel free to add it to this list and make a PR so people know
* that your add-on is available and supported. Thanks, you 🚀!
* ========================================================================
*/
var SUPPORTED_FRAMEWORKS = ['mocha', // https://github.com/webdriverio/wdio-mocha-framework
'jasmine', // https://github.com/webdriverio/wdio-jasmine-framework
'cucumber' // https://github.com/webdriverio/wdio-cucumber-framework
];
var SUPPORTED_REPORTER = [' dot - https://github.com/webdriverio/wdio-dot-reporter', ' spec - https://github.com/webdriverio/wdio-spec-reporter', ' junit - https://github.com/webdriverio/wdio-junit-reporter', ' allure - https://github.com/webdriverio/wdio-allure-reporter', ' teamcity - https://github.com/sullenor/wdio-teamcity-reporter', ' json - https://github.com/fijijavis/wdio-json-reporter', ' concise - https://github.com/FloValence/wdio-concise-reporter'];
var SUPPORTED_SERVICES = [' sauce - https://github.com/webdriverio/wdio-sauce-service', ' browserstack - https://github.com/itszero/wdio-browserstack-service', ' testingbot - https://github.com/testingbot/wdio-testingbot-service', ' appium - https://github.com/rhysd/wdio-appium-service', ' firefox-profile - https://github.com/webdriverio/wdio-firefox-profile-service', ' selenium-standalone - https://github.com/webdriverio/wdio-selenium-standalone-service', ' phantomjs - https://github.com/cognitom/wdio-phantomjs-service', ' static-server - https://github.com/LeadPages/wdio-static-server-service', ' visual-regression - https://github.com/zinserjan/wdio-visual-regression-service', ' webpack - https://github.com/leadpages/wdio-webpack-service', ' webpack-dev-server - https://gitlab.com/Vinnl/wdio-webpack-dev-server-service'];
var VERSION = _package2.default.version;
var ALLOWED_ARGV = ['host', 'port', 'path', 'user', 'key', 'logLevel', 'coloredLogs', 'screenshotPath', 'baseUrl', 'waitforTimeout', 'framework', 'reporters', 'suite', 'spec', 'cucumberOpts', 'jasmineOpts', 'mochaOpts', 'connectionRetryTimeout', 'connectionRetryCount', 'watch'];
_optimist2.default.usage('WebdriverIO CLI runner\n\n' + 'Usage: wdio [options] [configFile]\n' + 'Usage: wdio config\n' + 'Usage: wdio repl [browserName]\n' + 'config file defaults to wdio.conf.js\n' + 'The [options] object will override values from the config file.').describe('help', 'prints WebdriverIO help menu').alias('help', 'h').describe('version', 'prints WebdriverIO version').alias('version', 'v').describe('host', 'Selenium server host address').describe('port', 'Selenium server port').describe('path', 'Selenium server path (default: /wd/hub)').describe('user', 'username if using a cloud service as Selenium backend').alias('user', 'u').describe('key', 'corresponding access key to the user').alias('key', 'k').describe('watch', 'watch specs for changes').describe('logLevel', 'level of logging verbosity (default: silent)').alias('logLevel', 'l').describe('coloredLogs', 'if true enables colors for log output (default: true)').alias('coloredLogs', 'c').describe('bail', 'stop test runner after specific amount of tests have failed (default: 0 - don\'t bail)').describe('screenshotPath', 'saves a screenshot to a given path if a command fails').alias('screenshotPath', 's').describe('baseUrl', 'shorten url command calls by setting a base url').alias('baseUrl', 'b').describe('waitforTimeout', 'timeout for all waitForXXX commands (default: 1000ms)').alias('waitforTimeout', 'w').describe('framework', 'defines the framework (Mocha, Jasmine or Cucumber) to run the specs (default: mocha)').alias('framework', 'f').describe('reporters', 'reporters to print out the results on stdout').alias('reporters', 'r').describe('suite', 'overwrites the specs attribute and runs the defined suite').describe('spec', 'run only a certain spec file').describe('cucumberOpts.*', 'Cucumber options, see the full list options at https://github.com/webdriverio/wdio-cucumber-framework#cucumberopts-options').describe('jasmineOpts.*', 'Jasmine options, see the full list options at https://github.com/webdriverio/wdio-jasmine-framework#jasminenodeopts-options').describe('mochaOpts.*', 'Mocha options, see the full list options at http://mochajs.org').string(['host', 'path', 'user', 'key', 'logLevel', 'screenshotPath', 'baseUrl', 'framework', 'reporters', 'suite', 'spec']).boolean(['coloredLogs', 'watch']).default({ coloredLogs: true }).check(function (arg) {
if (arg._.length > 1 && arg._[0] !== 'repl') {
throw new Error('Error: more than one config file specified');
}
});
var argv = _optimist2.default.parse(process.argv.slice(2));
if (argv.help) {
_optimist2.default.showHelp();
process.exit(0);
}
if (argv.version) {
console.log('v' + VERSION);
process.exit(0);
}
/**
* use wdio.conf.js default file name if no config was specified
* otherwise run config sequenz
*/
var configFile = argv._[0];
if (!configFile) {
if (_fs2.default.existsSync('./wdio.conf.js')) {
configFile = './wdio.conf.js';
} else {
argv._[0] = 'config';
}
}
var configMode = false;
if (argv._[0] === 'config') {
configMode = true;
console.log('\n=========================\nWDIO Configuration Helper\n=========================\n');
_inquirer2.default.prompt([{
type: 'list',
name: 'backend',
message: 'Where do you want to execute your tests?',
choices: ['On my local machine', 'In the cloud using Sauce Labs, Browserstack or Testingbot', 'In the cloud using a different service', 'I have my own Selenium cloud']
}, {
type: 'input',
name: 'host',
message: 'What is the host address of that cloud service?',
when: function when(answers) {
return answers.backend.indexOf('different service') > -1;
}
}, {
type: 'input',
name: 'port',
message: 'What is the port on which that service is running?',
default: '80',
when: function when(answers) {
return answers.backend.indexOf('different service') > -1;
}
}, {
type: 'input',
name: 'env_user',
message: 'Environment variable for username',
default: 'SAUCE_USERNAME',
when: function when(answers) {
return answers.backend.indexOf('In the cloud') > -1;
}
}, {
type: 'input',
name: 'env_key',
message: 'Environment variable for access key',
default: 'SAUCE_ACCESS_KEY',
when: function when(answers) {
return answers.backend.indexOf('In the cloud') > -1;
}
}, {
type: 'input',
name: 'host',
message: 'What is the IP or URI to your Selenium standalone server?',
default: '0.0.0.0',
when: function when(answers) {
return answers.backend.indexOf('own Selenium cloud') > -1;
}
}, {
type: 'input',
name: 'port',
message: 'What is the port which your Selenium standalone server is running on?',
default: '4444',
when: function when(answers) {
return answers.backend.indexOf('own Selenium cloud') > -1;
}
}, {
type: 'input',
name: 'path',
message: 'What is the path to your Selenium standalone server?',
default: '/wd/hub',
when: function when(answers) {
return answers.backend.indexOf('own Selenium cloud') > -1;
}
}, {
type: 'list',
name: 'framework',
message: 'Which framework do you want to use?',
choices: SUPPORTED_FRAMEWORKS
}, {
type: 'confirm',
name: 'installFramework',
message: 'Shall I install the framework adapter for you?',
default: true
}, {
type: 'input',
name: 'specs',
message: 'Where are your test specs located?',
default: './test/specs/**/*.js',
when: function when(answers) {
return answers.framework.match(/(mocha|jasmine)/);
}
}, {
type: 'input',
name: 'specs',
message: 'Where are your feature files located?',
default: './features/**/*.feature',
when: function when(answers) {
return answers.framework === 'cucumber';
}
}, {
type: 'input',
name: 'stepDefinitions',
message: 'Where are your step definitions located?',
default: './features/step-definitions',
when: function when(answers) {
return answers.framework === 'cucumber';
}
}, {
type: 'checkbox',
name: 'reporters',
message: 'Which reporter do you want to use?',
choices: SUPPORTED_REPORTER,
filter: function filter(reporters) {
return reporters.map(function (reporter) {
return 'wdio-' + reporter.split(/-/)[0].trim() + '-reporter';
});
}
}, {
type: 'confirm',
name: 'installReporter',
message: 'Shall I install the reporter library for you?',
default: true,
when: function when(answers) {
return answers.reporters.length > 0;
}
}, {
type: 'checkbox',
name: 'services',
message: 'Do you want to add a service to your test setup?',
choices: SUPPORTED_SERVICES,
filter: function filter(services) {
return services.map(function (service) {
return 'wdio-' + service.split(/- /)[0].trim() + '-service';
});
}
}, {
type: 'confirm',
name: 'installServices',
message: 'Shall I install the services for you?',
default: true,
when: function when(answers) {
return answers.services.length > 0;
}
}, {
type: 'input',
name: 'outputDir',
message: 'In which directory should the xunit reports get stored?',
default: './',
when: function when(answers) {
return answers.reporters === 'junit';
}
}, {
type: 'input',
name: 'outputDir',
message: 'In which directory should the json reports get stored?',
default: './',
when: function when(answers) {
return answers.reporters === 'json';
}
}, {
type: 'list',
name: 'logLevel',
message: 'Level of logging verbosity',
default: 'silent',
choices: ['silent', 'verbose', 'command', 'data', 'result', 'error']
}, {
type: 'input',
name: 'screenshotPath',
message: 'In which directory should screenshots gets saved if a command fails?',
default: './errorShots/'
}, {
type: 'input',
name: 'baseUrl',
message: 'What is the base url?',
default: 'http://localhost'
}]).then(function (answers) {
var packagesToInstall = [];
if (answers.installFramework) {
packagesToInstall.push('wdio-' + answers.framework + '-framework');
}
if (answers.installReporter) {
packagesToInstall = packagesToInstall.concat(answers.reporters);
}
if (answers.installServices) {
packagesToInstall = packagesToInstall.concat(answers.services);
}
if (packagesToInstall.length > 0) {
console.log('\nInstalling wdio packages:');
return (0, _npmInstallPackage2.default)(packagesToInstall, { saveDev: true }, function (err) {
if (err) {
throw err;
}
console.log('\nPackages installed successfully, creating configuration file...');
renderConfigurationFile(answers);
});
}
renderConfigurationFile(answers);
process.exit(0);
});
}
function renderConfigurationFile(answers) {
var tpl = _fs2.default.readFileSync(_path2.default.join(__dirname, '/helpers/wdio.conf.ejs'), 'utf8');
var renderedTpl = _ejs2.default.render(tpl, {
answers: answers
});
_fs2.default.writeFileSync(_path2.default.join(process.cwd(), 'wdio.conf.js'), renderedTpl);
console.log('\nConfiguration file was created successfully!\nTo run your tests, execute:\n\n$ wdio wdio.conf.js\n');
}
/**
* sanitize reporters
*/
if (argv.reporters) {
argv.reporters = argv.reporters.split(',');
}
/**
* sanitize cucumberOpts
*/
if (argv.cucumberOpts) {
if (argv.cucumberOpts.tags) {
argv.cucumberOpts.tags = argv.cucumberOpts.tags.split(',');
}
if (argv.cucumberOpts.ignoreUndefinedDefinitions) {
argv.cucumberOpts.ignoreUndefinedDefinitions = argv.cucumberOpts.ignoreUndefinedDefinitions === 'true';
}
if (argv.cucumberOpts.require) {
argv.cucumberOpts.require = argv.cucumberOpts.require.split(',');
}
}
/**
* sanitize jasmineOpts
*/
if (argv.jasmineOpts && argv.jasmineOpts.defaultTimeoutInterval) {
argv.jasmineOpts.defaultTimeoutInterval = parseInt(argv.jasmineOpts.defaultTimeoutInterval, 10);
}
/**
* sanitize mochaOpts
*/
if (argv.mochaOpts) {
if (argv.mochaOpts.timeout) {
argv.mochaOpts.timeout = parseInt(argv.mochaOpts.timeout, 10);
}
if (argv.mochaOpts.compilers) {
argv.mochaOpts.compilers = argv.mochaOpts.compilers.split(',');
}
if (argv.mochaOpts.require) {
argv.mochaOpts.require = argv.mochaOpts.require.split(',');
}
}
var args = {};
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(ALLOWED_ARGV), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var key = _step.value;
if (argv[key] !== undefined) {
args[key] = argv[key];
}
}
/**
* start repl when command is "repl"
*/
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
if (argv._[0] === 'repl') {
var browser = argv._[1] || 'firefox';
var client = (0, _.remote)((0, _deepmerge2.default)(args, {
sync: true,
desiredCapabilities: {
browserName: browser
}
})).init().catch(function (e) {
client.logger.logLevel = 'verbose';
client.logger.error(e);
throw e;
});
/**
* try to enhance client object using wdio-sync (if installed). This enables a better API
* handling due to the result enhancements done by wdio-sync
*/
try {
var sync = require('wdio-sync');
global.browser = { options: { sync: true } };
sync.wrapCommands(client, [], []);
global.$ = function () {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return client.element.apply(client, args);
};
global.$$ = function () {
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return client.elements.apply(client, args).value;
};
global.browser = client;
} catch (e) {}
client.debug().end().then(function () {
return process.exit(0);
}, function (e) {
throw e;
});
/**
* run launch sequence if config command wasn't called
*/
} else if (!configMode) {
var launcher = new _launcher2.default(configFile, args);
launcher.run().then(function (code) {
return process.exit(code);
}, function (e) {
return process.nextTick(function () {
throw e;
});
});
}

View File

@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* The `$$` command is a short way to call the [`elements`](/api/protocol/elements.html) command in order
* to fetch multiple elements on the page. It returns an array with element results that will have an
* extended prototype to call action commands without passing in a selector. However if you still pass
* in a selector it will look for that element first and call the action on that element.
*
* You can chain `$` or `$$` together in order to walk down the DOM tree.
*
* <example>
:index.html
<ul id="menu">
<li><a href="/">Home</a></li>
<li><a href="/">Developer Guide</a></li>
<li><a href="/">API</a></li>
<li><a href="/">Contribute</a></li>
</ul>
:$.js
it('should get text a menu link', function () {
var text = $('#menu');
console.log(text.$$('li')[2].$('a').getText()); // outputs: "API"
// same as
console.log(text.$$('li')[2].getText('a'));
});
* </example>
*
* @alias $$
* @param {String} selector selector to fetch multiple elements
* @type utility
*
*/
var $$ = function $$(selector) {
return this.elements(selector).then(function (res) {
return res.value.map(function (el, i) {
el.value = { ELEMENT: el.ELEMENT };
el.selector = selector;
el.index = i;
return el;
});
});
};
exports.default = $$;
module.exports = exports["default"];

View File

@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* The `$` command is a short way to call the [`element`](/api/protocol/element.html) command in order
* to fetch a single element on the page. It returns an object that with an extended prototype to call
* action commands without passing in a selector. However if you still pass in a selector it will look
* for that element first an call the action on that element.
*
* You can chain `$` or `$$` together in order to walk down the DOM tree.
*
* <example>
:index.html
<ul id="menu">
<li><a href="/">Home</a></li>
<li><a href="/">Developer Guide</a></li>
<li><a href="/">API</a></li>
<li><a href="/">Contribute</a></li>
</ul>
:$.js
it('should get text a menu link', function () {
var text = $('#menu');
console.log(text.$$('li')[2].$('a').getText()); // outputs: "API"
// same as
console.log(text.$$('li')[2].getText('a'));
});
* </example>
*
* @alias $
* @param {String} selector selector to fetch a certain element
* @type utility
*
*/
var $ = function $(selector) {
return this.element(selector);
};
exports.default = $;
module.exports = exports["default"];

View File

@ -0,0 +1,48 @@
/**
*
* Add custom command to client/browser instance. Read more about `addCommand` [here](/guide/usage/customcommands.html).
*
* <example>
:addCommandAsync.js
// adding `async` as function name disables the synchronous behavior of WebdriverIO commands
// in case you need to interact with other 3rd party libraries that support promises
client.addCommand("getUrlAndTitle", function async (customVar) {
return this.url().then(function(urlResult) {
return this.getTitle().then(function(titleResult) {
console.log(customVar); // "a custom variable"
return { url: urlResult.value, title: titleResult }
})
})
})
:addCommand.js
browser.addCommand("getUrlAndTitle", function (customVar) {
return {
url: this.getUrl(),
title: this.getTitle(),
customVar: customVar
}
})
:example.js
it('should use my custom command', function () {
browser.url('http://www.github.com')
var result = browser.getUrlAndTitle('foobar')
assert.strictEqual(result.url, 'https://github.com/')
assert.strictEqual(result.title, 'GitHub · Where software is built')
assert.strictEqual(result.customVar, 'foobar')
})
* </example>
*
* @alias browser.addCommand
* @param {String} commandName name of your custom command
* @param {Function} customMethod your custom method
* @param {Boolean} overwrite if set to `true` you can overwrite existing commands
* @type utility
*
*/
// Nothing to see here!
// You can find the actual implementation in /lib/webdriverio.js
"use strict";

View File

@ -0,0 +1,100 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var addValue = function addValue(selector, value) {
var _this = this;
/*!
* parameter check
*/
if (typeof value === 'number') {
value = '' + value;
}
if (typeof value !== 'string' && !Array.isArray(value)) {
throw new _ErrorHandler.CommandError('number or type of arguments don\'t agree with addValue command');
}
return this.elements(selector).then(function (res) {
if (!res.value || res.value.length === 0) {
/*!
* throw NoSuchElement error if no element was found
*/
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
var self = _this;
var elementIdValueCommands = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(res.value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var elem = _step.value;
elementIdValueCommands.push(self.elementIdValue(elem.ELEMENT, value));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _this.unify(elementIdValueCommands);
});
}; /**
*
* Add a value to an object found by given selector. You can also use unicode
* characters like Left arrow or Back space. WebdriverIO will take care of
* translating them into unicode characters. Youll find all supported characters
* [here](https://w3c.github.io/webdriver/webdriver-spec.html#dfn-character-types).
* To do that, the value has to correspond to a key from the table.
*
* <example>
:addValue.js
it('should demonstrate the addValue command', function () {
var input = $('.input')
input.setValue('test')
input.addValue(123)
// same as
browser.setValue('.input', 'test')
browser.addValue('.input', '123')
var value = elem.getValue()
assert(value === 'test123') // true
})
* </example>
*
* @alias browser.addValue
* @param {String} selector Input element
* @param {String|Number} values value to be added
* @uses protocol/elements, protocol/elementIdValue
* @type action
*
*/
exports.default = addValue;
module.exports = exports['default'];

View File

@ -0,0 +1,43 @@
/**
* You can use `call` to execute any async action within your test spec. The command itself
* it treated like a synchronous function. It accepts promises and stops the execution until
* the promise has resolved.
*
* <example>
:call.js
it('some testing here', function() {
browser.url('http://google.com')
// make an asynchronous call using any 3rd party library supporting promises
// e.g. call to backend or db to inject fixture data
browser.call(function () {
return somePromiseLibrary.someMethod().then(function () {
// ...
})
})
// example for async call to 3rd party library that doesn't support promises
browser.call(function () {
return new Promise(function(resolve, reject) {
someOtherNodeLibrary.someMethod(param1, function(err, res) {
if (err) {
return reject(err)
}
resolve(res)
})
})
})
// continue synchronously
browser.click('#elemA')
browser.setValue('.firstname','webdriverbot')
});
* </example>
*
* @alias browser.call
* @param {Function} callback function to be called
* @type utility
*
*/
"use strict";

View File

@ -0,0 +1,77 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Given a selector corresponding to an `<input type=file>` chooseFile will upload
* the local file to the browser machine and fill the form accordingly. It does not
* submit the form for you. This command only works for desktop browser.
*
* <example>
:call.js
it('uploads a file and fills the form with it', async function () {
var toUpload = path.join(__dirname, '..', '..', 'fixtures', 'cat-to-upload.gif')
browser.chooseFile('#upload-test', toUpload)
browser.getValue('#upload-test')
expect(/cat\-to\-upload\.gif$/.test(val)).to.be.equal(true)
})
* </example>
*
* @alias browser.chooseFile
* @param {String} selector input element
* @param {String} localPath local path to file to be uploaded
* @uses utility/uploadFile, action/addValue
* @type utility
*
*/
var chooseFile = function chooseFile(selector, localPath) {
var _this = this;
/*!
* parameter check
*/
if (typeof localPath !== 'string') {
return new _ErrorHandler.CommandError('number or type of arguments don\'t agree with chooseFile command');
}
/*!
* mobile check
*/
if (this.isMobile) {
return new _ErrorHandler.CommandError('chooseFile command is not supported on mobile platforms');
}
return new _promise2.default(function (resolve, reject) {
_fs2.default.stat(localPath, function (err) {
/* istanbul ignore next */
if (err) {
return reject(new _ErrorHandler.CommandError('File to upload does not exists on your system'));
}
_this.uploadFile(localPath).then(function (res) {
return this.addValue(selector, res.value);
}).then(resolve, reject);
});
});
};
exports.default = chooseFile;
module.exports = exports['default'];

View File

@ -0,0 +1,82 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var clearElement = function clearElement(selector) {
var _this = this;
return this.elements(selector).then(function (res) {
if (!res.value || res.value.length === 0) {
// throw NoSuchElement error if no element was found
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
var elementIdClearCommands = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(res.value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var elem = _step.value;
elementIdClearCommands.push(_this.elementIdClear(elem.ELEMENT, 'value'));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _this.unify(elementIdClearCommands);
});
}; /**
*
* Clear a `<textarea>` or text `<input>` elements value. Make sure you can interact with the
* element before using this command. You can't clear an input element that is disabled or in
* readonly mode.
*
* <example>
:clearElement.js
it('should demonstrate the clearElement command', function () {
var input = $('.input')
input.setValue('test123')
console.log(input.getValue()) // returns 'test123'
input.clearElement()
// or
browser.clearElement('.input')
var value = browser.getValue('.input')
assert(value === ''); // true
})
* </example>
*
* @alias browser.clearElement
* @param {String} selector input element
* @uses protocol/elements, protocol/elementIdClear
* @type action
*
*/
exports.default = clearElement;
module.exports = exports['default'];

View File

@ -0,0 +1,60 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var click = function click(selector) {
var _this = this;
return this.element(selector).then(function (elem) {
/**
* check if element was found and throw error if not
*/
if (!elem.value) {
throw new _ErrorHandler.RuntimeError(7);
}
return _this.elementIdClick(elem.value.ELEMENT);
});
}; /**
*
* Click on an element based on given selector.
*
* <example>
:example.html
<button id="myButton" onclick="document.getElementById('someText').innerHTML='I was clicked'">Click me</button>
<div id="someText">I was not clicked</div>
:click.js
it('should demonstrate the click command', function () {
var myButton = $('#myButton')
myButton.click()
// or
browser.click('#myButton')
var text = browser.getText('#someText');
assert(text === 'I was clicked'); // true
})
:example.js
it('should fetch menu links and visit each page', function () {
links = $$('#menu a');
links.forEach(function (link) {
link.click();
});
});
* </example>
*
* @alias browser.click
* @param {String} selector element to click on. If it matches with more than one DOM-element it automatically clicks on the first element
* @uses protocol/element, protocol/elementIdClick
* @type action
*
*/
exports.default = click;
module.exports = exports['default'];

View File

@ -0,0 +1,52 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var close = function close(windowHandle) {
var _this = this;
if (typeof windowHandle !== 'string') {
return this.getTabIds().then(function (tabIds) {
if (tabIds.length === 0) {
throw new _ErrorHandler.RuntimeError('' + 'Can\'t switch to the next tab because all windows are closed. ' + 'Make sure you keep at least one window open!');
}
return _this.window().switchTab(tabIds[0]);
});
}
return this.window().switchTab(windowHandle);
}; /**
*
* Close current window (and focus on an other window). If no window handle is given
* it automatically switches back to the first handle.
*
* <example>
:close.js
it('should demonstrate the close command', function () {
browser.url('http://github.com')
browser.newWindow('http://google.com')
var title = browser.getTitle()
console.log(title) // outputs: "Google"
browser.close()
title = browser.getTitle()
console.log(title) // outputs: "GitHub · Build software better, together."
})
* </example>
*
* @alias browser.close
* @param {String=} windowHandle new window to focus on
* @uses protocol/window, window/switchTab
* @type window
*
*/
exports.default = close;
module.exports = exports['default'];

View File

@ -0,0 +1,141 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _vm = require('vm');
var _vm2 = _interopRequireDefault(_vm);
var _repl = require('repl');
var _repl2 = _interopRequireDefault(_repl);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var debug = function debug() {
var commandTimeout = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 5000;
var _this = this;
var enableStdout = arguments[1];
var enableLogging = arguments[2];
var commandIsRunning = false;
var logLevel = this.logger.logLevel;
this.logger.logLevel = 'verbose';
this.logger.debug();
if (!enableLogging) {
this.logger.logLevel = logLevel;
}
var myEval = function myEval(cmd, context, filename, callback) {
if (commandIsRunning) {
return;
}
if (cmd === 'browser\n') {
return callback(null, '[WebdriverIO REPL client]');
}
commandIsRunning = true;
var result = void 0;
if (typeof global.wdioSync === 'function') {
return global.wdioSync(function () {
try {
result = _vm2.default.runInThisContext(cmd);
} catch (e) {
commandIsRunning = false;
return callback(e);
}
callback(null, result);
commandIsRunning = false;
})();
}
context.browser = _this;
try {
result = _vm2.default.runInThisContext(cmd);
} catch (e) {
commandIsRunning = false;
return callback(e);
}
if (!result || typeof result.then !== 'function') {
commandIsRunning = false;
return callback(null, result);
}
var timeout = setTimeout(function () {
return callback(new _ErrorHandler.RuntimeError('Command execution timed out'));
}, commandTimeout);
result.then(function (res) {
commandIsRunning = false;
clearTimeout(timeout);
return callback(null, res);
}, function (e) {
commandIsRunning = false;
clearTimeout(timeout);
return callback(e);
});
};
var replServer = _repl2.default.start({
prompt: '> ',
eval: myEval,
input: process.stdin,
output: process.stdout,
useGlobal: true,
ignoreUndefined: true
});
return new _promise2.default(function (resolve) {
replServer.on('exit', function () {
_this.logger.logLevel = logLevel;
resolve();
});
});
}; /**
*
* This command helps you to debug your integration tests. It stops the running browser and gives
* you time to jump into it and check the state of your application (e.g. using the dev tools).
* Your terminal transforms into a [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)
* interface that will allow you to try out certain commands, find elements and test actions on
* them.
*
* [![WebdriverIO REPL](http://webdriver.io/images/repl.gif)](http://webdriver.io/images/repl.gif)
*
* If you run the WDIO testrunner make sure you increase the timeout property of your test framework
* your are using (e.g. Mocha or Jasmine) in order to prevent the continuation due to a test timeout.
* Also avoid to execute the command with multiple capabilities running at the same time.
*
* <iframe width="560" height="315" src="https://www.youtube.com/embed/xWwP-3B_YyE" frameborder="0" allowfullscreen></iframe>
*
* <example>
:debug.js
it('should demonstrate the debug command', function () {
browser.setValue('#input', 'FOO')
browser.debug() // jumping into the browser and change value of #input to 'BAR'
var value = browser.getValue('#input')
console.log(value) // outputs: "BAR"
})
* </example>
*
* @alias browser.debug
* @type utility
*
*/
exports.default = debug;
module.exports = exports['default'];

View File

@ -0,0 +1,60 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Delete cookies visible to the current page. By providing a cookie name it just removes the single cookie.
*
* <example>
:deleteCookie.js
it('should delete cookies', function () {
browser.setCookie({name: 'test', value: '123'})
browser.setCookie({name: 'test2', value: '456'})
browser.setCookie({name: 'test3', value: '789'})
var cookies = browser.getCookie()
console.log(cookies)
// outputs:
// [
// { name: 'test', value: '123' },
// { name: 'test2', value: '456' }
// { name: 'test3', value: '789' }
// ]
browser.deleteCookie('test3')
cookies = browser.getCookie()
console.log(cookies)
// outputs:
// [
// { name: 'test', value: '123' },
// { name: 'test2', value: '456' }
// ]
browser.deleteCookie()
cookies = browser.getCookie()
console.log(cookies) // outputs: []
})
* </example>
*
* @alias browser.deleteCookie
* @param {String=} name name of cookie to be deleted
* @uses protocol/cookie
* @type cookie
*
*/
var deleteCookie = function deleteCookie(name) {
/*!
* parameter check
*/
if (typeof name !== 'string') {
name = null;
}
return this.cookie('DELETE', name);
};
exports.default = deleteCookie;
module.exports = exports['default'];

View File

@ -0,0 +1,64 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var doubleClick = function doubleClick(selector) {
var _this = this;
if (this.isMobile) {
return this.element(selector).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
return _this.touchDoubleClick(res.value.ELEMENT);
});
}
return this.element(selector).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
return _this.moveTo(res.value.ELEMENT);
}).doDoubleClick();
}; /**
*
* Double-click on an element based on given selector.
*
* <example>
:example.html
<button id="myButton" ondblclick="document.getElementById('someText').innerHTML='I was dblclicked'">Click me</button>
<div id="someText">I was not clicked</div>
:doubleClick.js
it('should demonstrate the doubleClick command', function () {
var myButton = $('#myButton')
myButton.doubleClick()
// or
browser.doubleClick('#myButton')
var value = browser.getText('#someText')
assert(value === 'I was dblclicked') // true
})
* </example>
*
* @alias browser.doubleClick
* @param {String} selector element to double click on. If it matches with more than on DOM-element it automatically clicks on the first element
* @uses protocol/element, protocol/moveTo, protocol/doDoubleClick, protocol/touchDoubleClick
* @type action
*
*/
exports.default = doubleClick;
module.exports = exports['default'];

View File

@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Drag an item to a destination element. __Note:__ this command only works for some drag&drop implementation
* and some browser because of the way how Selenium simulates mouse events.
*
* @alias browser.dragAndDrop
* @param {String} sourceElem source selector
* @param {String} destinationElem destination selector
* @uses action/moveToObject, protocol/buttonDown, protocol/buttonUp, property/getLocation, protocol/touchDown, protocol/touchMove, protocol/touchUp
* @type action
*
*/
var dragAndDrop = function dragAndDrop(selector, destinationElem) {
var _this = this;
if (this.isMobile) {
return this.getLocation(selector).then(function (location) {
return _this.touchDown(location.x, location.y);
}).getLocation(destinationElem).then(function (location) {
return _this.touchMove(location.x, location.y).touchUp(location.x, location.y);
});
}
return this.moveToObject(selector).buttonDown().moveToObject(destinationElem).buttonUp();
};
exports.default = dragAndDrop;
module.exports = exports["default"];

View File

@ -0,0 +1,35 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* End the session and close browser. This command is only supported in standalone mode. If you
* are using the wdio testrunner you can't close the browser before your spec finishes. The testrunner
* will close the browser for you after the spec has finished.
*
* However if you want to refresh the browser session you can try the [`reload`](/api/utility/reload.html)
* command.
*
* <example>
:endAsync.js
client
.init() // starts session and opens the browser
.url('http://google.com')
// ... other commands
.end(); // ends session and close browser
* </example>
*
* @alias browser.end
* @uses protocol/session
* @type utility
*
*/
var end = function end() {
return this.session('delete');
};
exports.default = end;
module.exports = exports['default'];

View File

@ -0,0 +1,60 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* End all selenium server sessions at once. Like the [`end`](/api/utility/end.html) command is this command
* only supported in standalone mode.
*
* @alias browser.endAll
* @uses protocol/sessions, protocol/session
* @type utility
*
*/
var endAll = function endAll() {
var _this = this;
return this.sessions().then(function (res) {
var sessionCommands = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(res.value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var session = _step.value;
sessionCommands.push(_this.session('delete', session.id));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _this.unify(sessionCommands);
});
};
exports.default = endAll;
module.exports = exports['default'];

View File

@ -0,0 +1,102 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var getAttribute = function getAttribute(selector, attributeName) {
var _this = this;
/*!
* parameter check
*/
if (typeof attributeName !== 'string') {
throw new _ErrorHandler.CommandError('number or type of arguments don\'t agree with getAttribute command');
}
return this.elements(selector).then(function (res) {
/**
* throw NoSuchElement error if no element was found
*/
if (!res.value || res.value.length === 0) {
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
var elementIdAttributeCommands = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(res.value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var elem = _step.value;
elementIdAttributeCommands.push(_this.elementIdAttribute(elem.ELEMENT, attributeName));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _this.unify(elementIdAttributeCommands, {
extractValue: true
});
});
}; /**
*
* Get an attribute from an DOM-element based on the selector and attribute name.
* Returns a list of attribute values if selector matches multiple elements.
*
* <example>
:index.html
<form action="/submit" method="post" class="loginForm">
<input type="text" name="name" placeholder="username"></input>
<input type="text" name="password" placeholder="password"></input>
<input type="submit" name="submit" value="submit"></input>
</form>
:getAttribute.js
it('should demonstrate the getAttribute command', function () {
var form = $('form')
var attr = form.getAttribute('method')
console.log(attr) // outputs: "post"
// or
console.log(browser.getAttribute('form', 'method')) // outputs: "post"
// if your selector matches multiple elements it returns an array of results
var allInputs = $$('.loginForm input')
console.log(allInputs.map(function(el) { return el.getAttribute('name'); })) // outputs: ['name', 'password', 'submit']
})
* </example>
*
* @alias browser.getAttribute
* @param {String} selector element with requested attribute
* @param {String} attributeName requested attribute
* @return {String|String[]|null} The value of the attribute(s), or null if it is not set on the element.
* @uses protocol/elements, protocol/elementIdAttribute
* @type property
*
*/
exports.default = getAttribute;
module.exports = exports['default'];

View File

@ -0,0 +1,83 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Returns a list of previous called commands + their arguments and execution timestamp.
*
* <example>
:getCommandHistoryAsync.js
client
.init()
.url('http://www.google.com')
.click('#username')
.addValue('#password', 'text')
.pause(2000)
.getCommandHistory().then(function(history){
console.log(history);
// outputs:
// [ { name: 'init', args: [], timestamp: 1487078962707 },
// { name: 'url', args: [ 'http://www.google.com' ], timestamp: 1487078962707 },
// { name: 'click', args: [ 'body' ], timestamp: 1487078962707 },
// { name: 'element',
// args: [ 'body' ],
// timestamp: 1487078962707,
// result:
// { state: 'success',
// sessionId: 'c2aea856-ba18-48c0-8745-aa292f6394bc',
// hCode: 1094372184,
// value: [Object],
// class: 'org.openqa.selenium.remote.Response',
// status: 0,
// selector: 'body' } },
// { name: 'elementIdClick',
// args: [ '0' ],
// timestamp: 1487078962707,
// result:
// { state: 'success',
// sessionId: 'c2aea856-ba18-48c0-8745-aa292f6394bc',
// hCode: 1704637158,
// value: null,
// class: 'org.openqa.selenium.remote.Response',
// status: 0 } },
// { name: 'addValue', args: [ '#lst-ib', 'webdriverio' ], timestamp: 1487078962707 },
// { name: 'elements',
// args: [ '#lst-ib' ],
// timestamp: 1487078962707,
// result:
// { state: 'success',
// sessionId: 'c2aea856-ba18-48c0-8745-aa292f6394bc',
// hCode: 1171202369,
// value: [Object],
// class: 'org.openqa.selenium.remote.Response',
// status: 0,
// selector: '#lst-ib' } },
// { name: 'elementIdValue',
// args: [ '1', 'webdriverio' ],
// timestamp: 1487078962707,
// result:
// { state: 'success',
// sessionId: 'c2aea856-ba18-48c0-8745-aa292f6394bc',
// hCode: 447115314,
// value: null,
// class: 'org.openqa.selenium.remote.Response',
// status: 0 } },
// { name: 'pause', args: [ 2000 ], timestamp: 1487078962707 } ]
})
.end();
* </example>
*
* @alias browser.getCommandHistory
* @return {Object[]} list of recent called commands + their arguments
* @type utility
*
*/
var getCommandHistory = function getCommandHistory() {
return this.commandList.slice(0, -1);
};
exports.default = getCommandHistory;
module.exports = exports["default"];

View File

@ -0,0 +1,61 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Retrieve a [cookie](https://w3c.github.io/webdriver/webdriver-spec.html#cookies)
* visible to the current page. You can query a specific cookie by providing the cookie name or
* retrieve all.
*
* <example>
:getCookie.js
it('should return a cookie for me', function () {
browser.setCookie({name: 'test', value: '123'})
browser.setCookie({name: 'test2', value: '456'})
var testCookie = browser.getCookie('test')
console.log(testCookie); // outputs: { name: 'test', value: '123' }
var allCookies = browser.getCookie()
console.log(allCookies);
// outputs:
// [
// { name: 'test', value: '123' },
// { name: 'test2', value: '456' }
// ]
})
* </example>
*
* @alias browser.getCookie
* @param {String=} name name of requested cookie
* @return {Object|null} requested cookie if existing
* @uses protocol/cookie
* @type cookie
*
*/
var getCookie = function getCookie(name) {
/*!
* paramter check
*/
if (typeof name !== 'string') {
name = null;
}
return this.cookie().then(function (res) {
res.value = res.value || [];
if (typeof name === 'string') {
return res.value.filter(function (cookie) {
return cookie.name === name;
})[0] || null;
}
return res.value || (typeof name === 'string' ? null : []);
});
};
exports.default = getCookie;
module.exports = exports['default'];

View File

@ -0,0 +1,148 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _parseCSS = require('../helpers/parseCSS.js');
var _parseCSS2 = _interopRequireDefault(_parseCSS);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Get a css property from a DOM-element selected by given selector. The return value
* is formatted to be testable. Colors gets parsed via [rgb2hex](https://www.npmjs.org/package/rgb2hex)
* and all other properties get parsed via [css-value](https://www.npmjs.org/package/css-value).
*
* Note that shorthand CSS properties (e.g. background, font, border, margin, padding, list-style, outline,
* pause, cue) are not returned, in accordance with the DOM CSS2 specification - you should directly access
* the longhand properties (e.g. background-color) to access the desired values.
*
* <example>
:example.html
<label id="myLabel" for="input" style="color: #0088cc; font-family: helvetica, arial, freesans, clean, sans-serif, width: 100px">Some Label</label>
:getCssProperty.js
it('should demonstrate the getCssProperty command', function () {
var elem = $('#myLabel')
var color = elem.getCssProperty('color')
console.log(color)
// outputs the following:
// {
// property: 'color',
// value: 'rgba(0, 136, 204, 1)',
// parsed: {
// hex: '#0088cc',
// alpha: 1,
// type: 'color',
// rgba: 'rgba(0, 136, 204, 1)'
// }
// }
var font = elem.getCssProperty('font-family')
console.log(font)
// outputs the following:
// {
// property: 'font-family',
// value: 'helvetica',
// parsed: {
// value: [ 'helvetica', 'arial', 'freesans', 'clean', 'sans-serif' ],
// type: 'font',
// string: 'helvetica, arial, freesans, clean, sans-serif'
// }
// }
var width = elem.getCssProperty('width')
console.log(width)
// outputs the following:
// {
// property: 'width',
// value: '100px',
// parsed: {
// type: 'number',
// string: '100px',
// unit: 'px',
// value: 100
// }
// }
})
* </example>
*
* @alias browser.getCssProperty
* @param {String} selector element with requested style attribute
* @param {String} cssProperty css property name
* @uses protocol/elements, protocol/elementIdCssProperty
* @type property
*
*/
var getCssProperty = function getCssProperty(selector, cssProperty) {
var _this = this;
/*!
* parameter check
*/
if (typeof cssProperty !== 'string') {
throw new _ErrorHandler.CommandError('number or type of arguments don\'t agree with getCssProperty command');
}
return this.elements(selector).then(function (res) {
if (!res.value || res.value.length === 0) {
// throw NoSuchElement error if no element was found
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
var elementIdCssPropertyCommands = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(res.value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var elem = _step.value;
elementIdCssPropertyCommands.push(_this.elementIdCssProperty(elem.ELEMENT, cssProperty));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _promise2.default.all(elementIdCssPropertyCommands);
}).then(function (result) {
/**
* result already unwrapped when command was reran
*/
if (!Array.isArray(result)) {
return result;
}
return (0, _parseCSS2.default)(result, cssProperty);
});
};
exports.default = getCssProperty;
module.exports = exports['default'];

View File

@ -0,0 +1,36 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Retrieve the current window handle.
*
* <example>
:getCurrenteTabId.js
it('should return the current tab id', function () {
browser.url('http://webdriver.io')
var tabId = browser.getCurrentTabId()
console.log(tabid)
// outputs something like the following:
// "CDwindow-C43FB686-949D-4232-828B-583398FBD0C0"
})
* </example>
*
* @alias browser.getCurrentTabId
* @return {String} the window handle URL of the current focused window
* @uses protocol/windowHandle
* @type window
*
*/
var getCurrentTabId = function getCurrentTabId() {
return this.unify(this.windowHandle(), {
extractValue: true
});
};
exports.default = getCurrentTabId;
module.exports = exports["default"];

View File

@ -0,0 +1,113 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var getElementSize = function getElementSize(selector, prop) {
return this.elements(selector).then(function (res) {
/**
* throw NoSuchElement error if no element was found
*/
if (!res.value || res.value.length === 0) {
throw new _ErrorHandler.CommandError(7, selector || this.lastResult.selector);
}
var elementIdSizeCommands = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(res.value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var elem = _step.value;
elementIdSizeCommands.push(this.elementIdSize(elem.ELEMENT));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _promise2.default.all(elementIdSizeCommands);
}).then(function (sizes) {
/**
* result already unwrapped when command was reran
*/
if (!Array.isArray(sizes)) {
return sizes;
}
sizes = sizes.map(function (size) {
if (typeof prop === 'string' && prop.match(/(width|height)/)) {
return size.value[prop];
}
return {
width: size.value.width,
height: size.value.height
};
});
return sizes.length === 1 ? sizes[0] : sizes;
});
}; /**
*
* Get the width and height for an DOM-element based given selector.
*
* <example>
:getElementSize.js
it('should give me the size of an element', function () {
browser.url('http://github.com')
var logo = $('.octicon-mark-github')
var size = logo.getElementSize()
// or
size = browser.getElementSize('.octicon-mark-github')
console.log(size) // outputs: { width: 32, height: 32 }
var width = logo.getElementSize('width')
// or
width = browser.getElementSize('.octicon-mark-github', 'width')
console.log(width) // outputs: 32
var height = logo.getElementSize('height')
// or
height = browser.getElementSize('.octicon-mark-github', 'height')
console.log(height) // outputs: 32
})
* </example>
*
* @alias browser.getElementSize
* @param {String} selector element with requested size
* @param {String*} prop size to receive (optional "width|height")
* @return {Object|Number} requested element size (`{ width: <Number>, height: <Number> }`) or actual width/height as number if prop param is given
* @uses protocol/elements, protocol/elementIdSize
* @type property
*
*/
exports.default = getElementSize;
module.exports = exports['default'];

View File

@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Get the current geolocation.
*
* <example>
:getGeoLocation.js
it('should return my current location', function () {
var location = browser.getGeoLocation()
console.log(location)
// outputs:
// {
// latitude: 51.1045407,
// longitude: 13.2017384,
// altitude: 20.23345
// }
})
* </example>
*
* @alias browser.getGeoLocation
* @return {Object} the current geo location (`{latitude: number, longitude: number, altitude: number}`)
* @uses protocol/location
* @type mobile
*
*/
var getGeoLocation = function getGeoLocation() {
return this.unify(this.location(), {
extractValue: true
});
};
exports.default = getGeoLocation;
module.exports = exports["default"];

View File

@ -0,0 +1,69 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _assign = require('babel-runtime/core-js/object/assign');
var _assign2 = _interopRequireDefault(_assign);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Get the details of the Selenium Grid node running a session
*
* <example>
:grid.js
it('should return grid information', function () {
console.log(browser.getGridNodeDetails())
// {
// success: true,
// msg: "proxy found !",
// id: "MacMiniA10",
// request: {
// ...
// configuration: {
// ...
// },
// capabilities: [
// {
// ...
// }
// ]
// }
// }
})
* </example>
*
* @alias browser.getGridNodeDetails
* @uses grid/gridTestSession, grid/gridProxyDetails
* @type grid
*/
var getGridNodeDetails = function getGridNodeDetails() {
var _this = this;
return this.gridTestSession().then(function (session) {
return _this.gridProxyDetails(session.proxyId).then(function (details) {
delete session.msg;
delete session.success;
delete details.msg;
delete details.success;
delete details.id;
return (0, _assign2.default)(details, session);
});
}).catch(function (e) {
if (e.seleniumStack && e.seleniumStack.type === 'GridApiError') {
return {
error: e.message
};
}
});
};
exports.default = getGridNodeDetails;
module.exports = exports['default'];

View File

@ -0,0 +1,69 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var _getHTML = require('../scripts/getHTML');
var _getHTML2 = _interopRequireDefault(_getHTML);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Get source code of specified DOM element by selector.
*
* <example>
:index.html
<div id="test">
<span>Lorem ipsum dolor amet</span>
</div>
:getHTML.js
it('should get html for certain elements', function () {
var outerHTML = browser.getHTML('#test');
console.log(outerHTML);
// outputs:
// "<div id="test"><span>Lorem ipsum dolor amet</span></div>"
var innerHTML = browser.getHTML('#test', false);
console.log(innerHTML);
// outputs:
// "<span>Lorem ipsum dolor amet</span>"
});
* </example>
*
* @alias browser.getHTML
* @param {String} selector element to get the current DOM structure from
* @param {Boolean=} includeSelectorTag if true it includes the selector element tag (default: true)
* @uses action/selectorExecute
* @type property
*
*/
var getHTML = function getHTML(selector, includeSelectorTag) {
var _this = this;
/**
* we can't use default values for function parameter here because this would
* break the ability to chain the command with an element if includeSelectorTag is used
*/
includeSelectorTag = typeof includeSelectorTag === 'boolean' ? includeSelectorTag : true;
return this.selectorExecute(selector, _getHTML2.default, includeSelectorTag).then(function (html) {
/**
* throw NoSuchElement error if no element was found
*/
if (!html) {
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
return html && html.length === 1 ? html[0] : html;
});
};
exports.default = getHTML;
module.exports = exports['default'];

View File

@ -0,0 +1,93 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var getLocation = function getLocation(selector, prop) {
var _this = this;
return this.elements(selector).then(function (res) {
/**
* throw NoSuchElement error if no element was found
*/
if (!res.value || res.value.length === 0) {
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
var results = [];
var that = _this;
return new _promise2.default(function (resolve, reject) {
var hasError = false;
function processNext() {
var current = res.value.pop();
return that.elementIdLocation(current.ELEMENT).catch(function (err) {
hasError = true;
reject(err);
}).then(function (location) {
if (hasError) {
return;
}
if (prop === 'x' || prop === 'y') {
results.push(location.value[prop]);
} else {
results.push({
x: location.value.x,
y: location.value.y
});
}
if (res.value.length) {
return processNext();
} else {
resolve(results.length === 1 ? results[0] : results);
}
});
}
return processNext();
});
});
}; /**
*
* Determine an elements location on the page. The point (0, 0) refers to
* the upper-left corner of the page.
*
* <example>
:getLocation.js
it('should get the location of one or multiple elements', function () {
browser.url('http://github.com');
var location = browser.getLocation('.octicon-mark-github');
console.log(location); // outputs: { x: 150, y: 20 }
var xLocation = browser.getLocation('.octicon-mark-github', 'x')
console.log(xLocation); // outputs: 150
var yLocation = browser.getLocation('.octicon-mark-github', 'y')
console.log(yLocation); // outputs: 20
});
* </example>
*
* @alias browser.getLocation
* @param {String} selector element with requested position offset
* @param {String} property can be "x" or "y" to get a result value directly for easier assertions
* @return {Object|Object[]} The X and Y coordinates for the element on the page (`{x:number, y:number}`)
* @uses protocol/elements, protocol/elementIdLocation
* @type property
*
*/
exports.default = getLocation;
module.exports = exports['default'];

View File

@ -0,0 +1,108 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var getLocationInView = function getLocationInView(selector, prop) {
var _this = this;
return this.elements(selector).then(function (res) {
/**
* throw NoSuchElement error if no element was found
*/
if (!res.value || res.value.length === 0) {
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
var elementIdLocationInViewCommands = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(res.value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var elem = _step.value;
elementIdLocationInViewCommands.push(_this.elementIdLocationInView(elem.ELEMENT));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _promise2.default.all(elementIdLocationInViewCommands);
}).then(function (locations) {
/**
* result already unwrapped when command was reran
*/
if (!Array.isArray(locations)) {
return locations;
}
locations = locations.map(function (location) {
if (typeof prop === 'string' && prop.match(/(x|y)/)) {
return location.value[prop];
}
return {
x: location.value.x,
y: location.value.y
};
});
return locations.length === 1 ? locations[0] : locations;
});
}; /**
*
* Determine an elements location on the screen once it has been scrolled into view.
*
* <example>
:getLocationInView.js
it('should get the location of one or multiple elements in view', function () {
browser.url('http://github.com');
var location = browser.getLocationInView('.octicon-mark-github');
console.log(location); // outputs: { x: 150, y: 20 }
var xLocation = browser.getLocationInView('.octicon-mark-github', 'x')
console.log(xLocation); // outputs: 150
var yLocation = browser.getLocationInView('.octicon-mark-github', 'y')
console.log(yLocation); // outputs: 20
});
* </example>
*
* @alias browser.getLocationInView
* @param {String} selector element with requested position offset
* @return {Object|Object[]} The X and Y coordinates for the element on the page (`{x:number, y:number}`)
*
* @uses protocol/elements, protocol/elementIdLocationInView
* @type property
*
*/
exports.default = getLocationInView;
module.exports = exports['default'];

View File

@ -0,0 +1,40 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var getOrientation = function getOrientation() {
if (!this.isMobile) {
throw new _ErrorHandler.CommandError('getOrientation command is not supported on non mobile platforms');
}
return this.unify(this.orientation(), {
lowercase: true,
extractValue: true
});
}; /**
*
* Get the current browser orientation. This command only works for mobile environments like Android Emulator,
* iOS Simulator or on real devices.
*
* <example>
:getOrientation.js
it('should get the orientation of my mobile device', function () {
var orientation = browser.getOrientation();
console.log(orientation); // outputs: "landscape"
});
* </example>
*
* @alias browser.getOrientation
* @return {String} device orientation (`landscape/portrait`)
* @uses protocol/orientation
* @for android, ios
* @type mobile
*
*/
exports.default = getOrientation;
module.exports = exports['default'];

View File

@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Get source code of the page. This command won't work in mobile environments for native apps. If you running
* hybrid tests make sure that you are in the webview before calling this command.
*
* <example>
:getSource.js
it('should get the source of the html document', function () {
browser.url('http://webdriver.io');
var source = browser.getSource();
console.log(source); // outputs: "<!DOCTYPE html>\n<title>Webdriver.io</title>..."
});
* </example>
*
* @alias browser.getSource
* @return {String} source code of current website
* @uses protocol/source
* @type property
*
*/
var getSource = function getSource() {
return this.unify(this.source(), {
extractValue: true
});
};
exports.default = getSource;
module.exports = exports["default"];

View File

@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Retrieve a list of all window handles available in the session. You can use these handles to switch
* to a different tab.
*
* <example>
:getTabIds.js
it('should get the source of the html document', function () {
browser.url('http://webdriver.io');
var tabIds = browser.getTabIds();
console.log(tabIds); // outputs: ['f9b387e0-99bd-11e6-8881-d3174a61fdce']
browser.newWindow('http://google.com');
tabIds = browser.getTabIds();
console.log(tabIds); // outputs: ['f9b387e0-99bd-11e6-8881-d3174a61fdce', 'fb4e9a40-99bd-11e6-8881-d3174a61fdce' ]
});
* </example>
*
* @alias browser.getTabIds
* @return {String[]} a list of window handles
* @uses protocol/windowHandles
* @type window
*
*/
var getTabIds = function getTabIds() {
return this.unify(this.windowHandles(), {
extractValue: true
});
};
exports.default = getTabIds;
module.exports = exports["default"];

View File

@ -0,0 +1,82 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var getTagName = function getTagName(selector) {
var _this = this;
return this.elements(selector).then(function (res) {
/**
* throw NoSuchElement error if no element was found
*/
if (!res.value || res.value.length === 0) {
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
var elementIdNameCommands = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(res.value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var elem = _step.value;
elementIdNameCommands.push(_this.elementIdName(elem.ELEMENT));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _this.unify(elementIdNameCommands, {
extractValue: true
});
});
}; /**
*
* Get tag name of a DOM-element found by given selector.
*
* <example>
:index.html
<div id="elem">Lorem ipsum</div>
:getTagName.js
it('should demonstrate the getTagName command', function () {
var elem = $('#elem');
var tagName = elem.getTagName();
console.log(tagName); // outputs: "div"
})
* </example>
*
* @alias browser.getTagName
* @param {String} selector element with requested tag name
* @return {String|String[]} the element's tag name, as a lowercase string
* @uses protocol/elements, protocol/elementIdName
* @type property
*
*/
exports.default = getTagName;
module.exports = exports['default'];

View File

@ -0,0 +1,101 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var getText = function getText(selector) {
var _this = this;
return this.elements(selector).then(function (res) {
/**
* throw NoSuchElement error if no element was found
*/
if (!res.value || res.value.length === 0) {
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
var elementIdTextCommands = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(res.value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var elem = _step.value;
elementIdTextCommands.push(_this.elementIdText(elem.ELEMENT));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _this.unify(elementIdTextCommands, {
extractValue: true
});
});
}; /**
*
* Get the text content from a DOM-element found by given selector. Make sure the element
* you want to request the text from [is interactable](http://www.w3.org/TR/webdriver/#interactable)
* otherwise you will get an empty string as return value. If the element is disabled or not
* visible and you still want to receive the text content use [getHTML](http://webdriver.io/api/property/getHTML.html)
* as a workaround.
*
* <example>
:index.html
<div id="elem">
Lorem ipsum <strong>dolor</strong> sit amet,<br>
consetetur sadipscing elitr
</div>
<span style="display: none">I am invisible</span>
:getText.js
it('should get text of an element or elements', function () {
var text = browser.getText('#elem');
console.log(text);
// outputs the following:
// "Lorem ipsum dolor sit amet,consetetur sadipscing elitr"
var spanText = browser.getText('span');
console.log(text);
// outputs "" (empty string) since element is not interactable
});
it('get content from table cell', function () {
browser.url('http://the-internet.herokuapp.com/tables');
var rows = $$('#table1 tr');
var columns = rows[1].$$('td'); // get columns of 2nd row
console.log(columns[2].getText()); // get text of 3rd column
});
* </example>
*
* @alias browser.getText
* @param {String} selector element with requested text
* @return {String|String[]} content of selected element (all HTML tags are removed)
* @uses protocol/elements, protocol/elementIdText
* @type property
*
*/
exports.default = getText;
module.exports = exports['default'];

View File

@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Get the title of current opened website. This command only works for browser environments or on mobile
* devices with webview enabled (hybrid tests).
*
* <example>
:getTitle.js
it('should get the title of the document', function () {
browser.url('http://webdriver.io');
var title = browser.getTitle()
console.log(title);
// outputs the following:
// "WebdriverIO - WebDriver bindings for Node.js"
});
* </example>
*
* @alias browser.getTitle
* @return {String} current page title
* @uses protocol/title
* @type property
*
*/
var getTitle = function getTitle() {
return this.unify(this.title(), {
extractValue: true
});
};
exports.default = getTitle;
module.exports = exports["default"];

View File

@ -0,0 +1,36 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Get the url of current opened website.
*
* <example>
:getUrl.js
it('should get the url of the current page', function () {
browser.url('http://webdriver.io');
var url = browser.getUrl();
console.log(url);
// outputs the following:
// "http://webdriver.io"
});
* </example>
*
* @alias browser.getUrl
* @return {String} current page url
* @uses protocol/url
* @type property
*
*/
var getUrl = function getUrl() {
return this.unify(this.url(), {
extractValue: true
});
};
exports.default = getUrl;
module.exports = exports["default"];

View File

@ -0,0 +1,82 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var getValue = function getValue(selector) {
var _this = this;
return this.elements(selector).then(function (res) {
/**
* throw NoSuchElement error if no element was found
*/
if (!res.value || res.value.length === 0) {
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
var elementIdAttributeCommands = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(res.value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var elem = _step.value;
elementIdAttributeCommands.push(_this.elementIdAttribute(elem.ELEMENT, 'value'));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _this.unify(elementIdAttributeCommands, {
extractValue: true
});
});
}; /**
*
* Get the value of a `<textarea>`, `<select>` or text `<input>` found by given selector.
*
* <example>
:index.html
<input type="text" value="John Doe" id="username">
:getValue.js
it('should demonstrate the getValue command', function () {
var inputUser = $('#username');
var value = inputUser.getValue();
console.log(value); // outputs: "John Doe"
});
* </example>
*
* @alias browser.getValue
* @param {String} selector input, textarea, or select element
* @return {String} requested input value
* @uses protocol/elements, protocol/elementIdAttribute
* @type property
*
*/
exports.default = getValue;
module.exports = exports['default'];

View File

@ -0,0 +1,55 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getViewportSize = require('../scripts/getViewportSize');
var _getViewportSize2 = _interopRequireDefault(_getViewportSize);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var getViewportSize = function getViewportSize(prop) {
return this.execute(_getViewportSize2.default).then(function (res) {
if (typeof prop === 'string' && prop.match(/(width|height)/)) {
prop = 'screen' + prop.slice(0, 1).toUpperCase() + prop.slice(1);
return res.value[prop];
}
return {
width: res.value.screenWidth || 0,
height: res.value.screenHeight || 0
};
});
}; /**
*
* Get viewport size of the current browser window. This command only works on desktop browser or in a mobile
* environment with a webview enabled.
*
* <example>
:getViewportSize.js
it('should return the viewport size', function () {
browser.url('http://webdriver.io');
var size = browser.getViewportSize()
console.log(size); // outputs: {width: 1024, height: 768}
var width = browser.getViewportSize('width')
console.log(size); // outputs: 1024
var height = browser.getViewportSize('height');
console.log(height); // outputs: 768
});
* </example>
*
* @alias browser.getViewportSize
* @param {String} property if "width" or "height" is set it returns only that property
* @return {Object} viewport width and height of the browser
* @uses protocol/execute
* @type window
*
*/
exports.default = getViewportSize;
module.exports = exports['default'];

View File

@ -0,0 +1,65 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var hasFocus = function hasFocus(selector) {
var _this = this;
return this.unify(this.elements(selector).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value || !res.value.length) {
throw new _ErrorHandler.RuntimeError(7);
}
return res.value;
}).then(function (elements) {
return _this.execute(function (elemArray) {
var focused = document.activeElement;
if (!focused) {
return false;
}
return elemArray.filter(function (elem) {
return focused === elem;
}).length > 0;
}, elements);
}), {
extractValue: true
});
}; /**
*
* Return true or false if the selected DOM-element currently has focus. If the selector matches
* multiple elements, it will return true if one of the elements has focus.
*
* <example>
:index.html
<input name="login" autofocus="" />
:hasFocus.js
it('should detect the focus of an element', function () {
browser.url('/');
var loginInput = $('[name="login"]');
console.log(loginInput.hasFocus()); // outputs: false
loginInput.click();
console.log(loginInput.hasFocus()); // outputs: true
})
* </example>
*
* @alias browser.hasFocus
* @param {String} selector selector for element(s) to test for focus
* @return {Boolean} true if one of the matching elements has focus
* @uses protocol/execute protocol/elements
* @type state
*
*/
exports.default = hasFocus;
module.exports = exports['default'];

View File

@ -0,0 +1,35 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var hold = function hold(selector) {
var _this = this;
return this.element(selector).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
return _this.touchLongClick(res.value.ELEMENT);
});
}; /**
*
* Long press on an element using finger motion events. This command works only in a
* mobile context.
*
* @alias browser.hold
* @param {String} selector element to hold on
* @uses protocol/element, protocol/touchLongClick
* @type mobile
*
*/
exports.default = hold;
module.exports = exports['default'];

View File

@ -0,0 +1,88 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var isEnabled = function isEnabled(selector) {
var _this = this;
return this.elements(selector).then(function (res) {
/**
* throw NoSuchElement error if no element was found
*/
if (!res.value || res.value.length === 0) {
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
var elementIdEnabledCommands = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(res.value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var elem = _step.value;
elementIdEnabledCommands.push(_this.elementIdEnabled(elem.ELEMENT));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _this.unify(elementIdEnabledCommands, {
extractValue: true
});
});
}; /**
*
* Return true or false if the selected DOM-element found by given selector is enabled.
*
* <example>
:index.html
<input type="text" name="inputField" class="input1">
<input type="text" name="inputField" class="input2" disabled>
<input type="text" name="inputField" class="input3" disabled="disabled">
:isEnabled.js
it('should detect if an element is enabled', function () {
var isEnabled = browser.isEnabled('.input1');
console.log(isEnabled); // outputs: true
var isEnabled2 = browser.isEnabled('.input2');
console.log(isEnabled2); // outputs: false
var isEnabled3 = browser.isEnabled('.input3')
console.log(isEnabled3); // outputs: false
});
* </example>
*
* @alias browser.isEnabled
* @param {String} selector DOM-element
* @return {Boolean|Boolean[]} true if element(s)* (is|are) enabled
* @uses protocol/elements, protocol/elementIdEnabled
* @type state
*
*/
exports.default = isEnabled;
module.exports = exports['default'];

View File

@ -0,0 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Returns true if at least one element is existing by given selector
*
* <example>
:index.html
<div id="notDisplayed" style="display: none"></div>
<div id="notVisible" style="visibility: hidden"></div>
<div id="notInViewport" style="position:absolute; left: 9999999"></div>
<div id="zeroOpacity" style="opacity: 0"></div>
:isExisting.js
it('should detect if elements are existing', function () {
var isExisting;
isExisting = browser.isExisting('#someRandomNonExistingElement');
console.log(isExisting); // outputs: false
isExisting = browser.isExisting('#notDisplayed');
console.log(isExisting); // outputs: true
isExisting = browser.isExisting('#notVisible');
console.log(isExisting); // outputs: true
isExisting = browser.isExisting('#notInViewport');
console.log(isExisting); // outputs: true
isExisting = browser.isExisting('#zeroOpacity');
console.log(isExisting); // outputs: true
});
* </example>
*
* @alias browser.isExisting
* @param {String} selector DOM-element
* @return {Boolean|Boolean[]} true if element(s)* [is|are] existing
* @uses protocol/elements
* @type state
*
*/
var isExisting = function isExisting(selector) {
return this.elements(selector).then(function (res) {
if (Array.isArray(res.value) && res.value.length > 0) {
return true;
}
return false;
});
};
exports.default = isExisting;
module.exports = exports["default"];

View File

@ -0,0 +1,88 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var isSelected = function isSelected(selector) {
var _this = this;
return this.elements(selector).then(function (res) {
/**
* throw NoSuchElement error if no element was found
*/
if (!res.value || res.value.length === 0) {
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
var elementIdSelectedCommands = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(res.value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var elem = _step.value;
elementIdSelectedCommands.push(_this.elementIdSelected(elem.ELEMENT));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _this.unify(elementIdSelectedCommands, {
extractValue: true
});
});
}; /**
*
* Return true or false if an `<option>` element, or an `<input>` element of type
* checkbox or radio is currently selected found by given selector.
*
* <example>
:index.html
<select name="selectbox" id="selectbox">
<option value="John Doe">John Doe</option>
<option value="Layla Terry" selected="selected">Layla Terry</option>
<option value="Bill Gilbert">Bill Gilbert"</option>
</select>
:isSelected.js
it('should detect if an element is selected', function () {
var element = $('[value="Layla Terry"]');
console.log(element.isSelected()); // outputs: true
browser.selectByValue('#selectbox', 'Bill Gilbert');
console.log(element.isSelected()); // outputs: false
});
* </example>
*
* @alias browser.isSelected
* @param {String} selector option element or input of type checkbox or radio
* @return {Boolean|Boolean[]} true if element is selected
* @uses protocol/elements, protocol/elementIdSelected
* @type state
*
*/
exports.default = isSelected;
module.exports = exports['default'];

View File

@ -0,0 +1,95 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require("babel-runtime/core-js/get-iterator");
var _getIterator3 = _interopRequireDefault(_getIterator2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Return true if the selected DOM-element found by given selector is visible. Returns an array if multiple DOM-elements are found for the given selector.
*
* <example>
:index.html
<div id="notDisplayed" style="display: none"></div>
<div id="notVisible" style="visibility: hidden"></div>
<div id="notInViewport" style="position:absolute; left: 9999999"></div>
<div id="zeroOpacity" style="opacity: 0"></div>
:isVisible.js
it('should detect if an element is visible', function () {
var isVisible = browser.isVisible('#notDisplayed');
console.log(isVisible); // outputs: false
isVisible = browser.isVisible('#notVisible');
console.log(isVisible); // outputs: false
isVisible = browser.isVisible('#notExisting');
console.log(isVisible); // outputs: false
isVisible = browser.isVisible('#notInViewport');
console.log(isVisible); // outputs: true
isVisible = browser.isVisible('#zeroOpacity');
console.log(isVisible); // outputs: true
});
* </example>
*
* @alias browser.isVisible
* @param {String} selector DOM-element
* @return {Boolean|Boolean[]} true if element(s)* [is|are] visible
* @uses protocol/elements, protocol/elementIdDisplayed
* @type state
*
*/
var isVisible = function isVisible(selector) {
var _this = this;
return this.elements(selector).then(function (res) {
/**
* if element does not exist it is automatically not visible ;-)
*/
if (!res.value || res.value.length === 0) {
return false;
}
var elementIdDisplayedCommands = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(res.value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var elem = _step.value;
elementIdDisplayedCommands.push(_this.elementIdDisplayed(elem.ELEMENT));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _this.unify(elementIdDisplayedCommands, {
extractValue: true
});
});
};
exports.default = isVisible;
module.exports = exports["default"];

View File

@ -0,0 +1,63 @@
'use strict';
var _isWithinViewport = require('../scripts/isWithinViewport');
var _isWithinViewport2 = _interopRequireDefault(_isWithinViewport);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
module.exports = function isVisibleWithinViewport(selector) {
return this.selectorExecute(selector, _isWithinViewport2.default).then(function (res) {
if (Array.isArray(res) && res.length === 1) {
return res[0];
}
return res;
}, function (err) {
/**
* if element does not exist it is automatically not visible :-)
*/
if (err.message.indexOf('NoSuchElement') > -1) {
return true;
}
throw err;
});
}; /**
*
* Return true if the selected DOM-element found by given selector is visible and within the viewport.
*
* <example>
:index.html
<div id="notDisplayed" style="display: none"></div>
<div id="notVisible" style="visibility: hidden"></div>
<div id="notInViewport" style="position:absolute; left: 9999999"></div>
<div id="zeroOpacity" style="opacity: 0"></div>
:isVisibleWithinViewport.js
:isVisible.js
it('should detect if an element is visible', function () {
var isVisibleWithinViewport = browser.isVisibleWithinViewport('#notDisplayed');
console.log(isVisibleWithinViewport); // outputs: false
isVisibleWithinViewport = browser.isVisibleWithinViewport('#notVisible');
console.log(isVisibleWithinViewport); // outputs: false
isVisibleWithinViewport = browser.isVisibleWithinViewport('#notExisting');
console.log(isVisibleWithinViewport); // outputs: false
isVisibleWithinViewport = browser.isVisibleWithinViewport('#notInViewport');
console.log(isVisibleWithinViewport); // outputs: false
isVisibleWithinViewport = browser.isVisibleWithinViewport('#zeroOpacity');
console.log(isVisibleWithinViewport); // outputs: false
});
* </example>
*
* @alias browser.isVisibleWithinViewport
* @param {String} selector DOM-element
* @return {Boolean|Boolean[]} true if element(s)* [is|are] visible
* @uses protocol/selectorExecute, protocol/timeoutsAsyncScript
* @type state
*
*/

View File

@ -0,0 +1,30 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _handleMouseButtonCommand = require('../helpers/handleMouseButtonCommand');
var _handleMouseButtonCommand2 = _interopRequireDefault(_handleMouseButtonCommand);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var leftClick = function leftClick(selector, xoffset, yoffset) {
return _handleMouseButtonCommand2.default.call(this, selector, 'left', xoffset, yoffset);
}; /**
*
* Apply left click on an element. If selector is not provided, click on the last
* moved-to location.
*
* @alias browser.leftClick
* @param {String} selector element to click on
* @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.
* @uses protocol/element, protocol/buttonPress
* @type action
*
*/
exports.default = leftClick;
module.exports = exports['default'];

View File

@ -0,0 +1,30 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _handleMouseButtonCommand = require('../helpers/handleMouseButtonCommand');
var _handleMouseButtonCommand2 = _interopRequireDefault(_handleMouseButtonCommand);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var middleClick = function middleClick(selector, xoffset, yoffset) {
return _handleMouseButtonCommand2.default.call(this, selector, 'middle', xoffset, yoffset);
}; /**
*
* Apply middle click on an element. If selector is not provided, click on the last
* moved-to location.
*
* @alias browser.middleClick
* @param {String} selector element to click on
* @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.
* @uses protocol/element, protocol/buttonPress
* @type action
*
*/
exports.default = middleClick;
module.exports = exports['default'];

View File

@ -0,0 +1,73 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var moveToObject = function moveToObject(selector, xoffset, yoffset) {
var _this = this;
/**
* check for offset params
*/
var hasOffsetParams = true;
if (typeof xoffset !== 'number' && typeof yoffset !== 'number') {
hasOffsetParams = false;
}
if (this.isMobile) {
return this.element(selector).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
return _this.elementIdSize(res.value.ELEMENT).then(function (size) {
return _this.elementIdLocation(res.value.ELEMENT).then(function (location) {
return { size: size, location: location };
});
});
}).then(function (res) {
var x = res.location.value.x + res.size.value.width / 2;
var y = res.location.value.y + res.size.value.height / 2;
if (hasOffsetParams) {
x = res.location.value.x + xoffset;
y = res.location.value.y + yoffset;
}
return _this.touchMove(x, y);
});
}
return this.element(selector).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
return _this.moveTo(res.value.ELEMENT, xoffset, yoffset);
});
}; /**
*
* Move the mouse by an offset of the specificed element. If an element is provided but no
* offset, the mouse will be moved to the center of the element. If the element is not
* visible, it will be scrolled into view.
*
* @alias browser.moveToObject
* @param {String} selector element to move to
* @param {Number} xoffset X offset to move to, relative to the top-left corner of the element. If not specified, the mouse will move to the middle of the element.
* @param {Number} yoffset Y offset to move to, relative to the top-left corner of the element. If not specified, the mouse will move to the middle of the element.
* @uses protocol/element, protocol/elementIdLocation
* @type action
*
*/
exports.default = moveToObject;
module.exports = exports['default'];

View File

@ -0,0 +1,71 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _newWindow = require('../scripts/newWindow');
var _newWindow2 = _interopRequireDefault(_newWindow);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Open new window in browser. This command is the equivalent function to `window.open()`. This command does not
* work in mobile environments.
*
* __Note:__ When calling this command you automatically switch to the new window.
*
* <example>
:newWindowSync.js
it('should open a new tab', function () {
browser.url('http://google.com')
console.log(browser.getTitle()); // outputs: "Google"
browser.newWindow('http://webdriver.io', 'WebdriverIO window', 'width=420,height=230,resizable,scrollbars=yes,status=1')
console.log(browser.getTitle()); // outputs: "WebdriverIO - WebDriver bindings for Node.js"
browser.close()
console.log(browser.getTitle()); // outputs: "Google"
});
* </example>
*
* @alias browser.newWindow
* @param {String} url website URL to open
* @param {String} windowName name of the new window
* @param {String} windowFeatures features of opened window (e.g. size, position, scrollbars, etc.)
* @uses protocol/execute, window/getTabIds, window/switchTab
* @type window
*
*/
var newWindow = function newWindow(url) {
var _this = this;
var windowName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var windowFeatures = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
/*!
* parameter check
*/
if (typeof url !== 'string' || typeof windowName !== 'string' || typeof windowFeatures !== 'string') {
throw new _ErrorHandler.CommandError('number or type of arguments don\'t agree with newWindow command');
}
/*!
* mobile check
*/
if (this.isMobile) {
throw new _ErrorHandler.CommandError('newWindow command is not supported on mobile platforms');
}
return this.execute(_newWindow2.default, url, windowName, windowFeatures).getTabIds().then(function (tabs) {
return _this.switchTab(tabs[tabs.length - 1]);
});
};
exports.default = newWindow;
module.exports = exports['default'];

View File

@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise = require("babel-runtime/core-js/promise");
var _promise2 = _interopRequireDefault(_promise);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Pauses execution for a specific amount of time. It is recommended to not use this command to wait for an
* element to show up. In order to avoid flaky test results it is better to use commands like
* [`waitforExist`](/api/utility/waitForExist.html) or other waitFor* commands.
*
* <example>
:pause.js
it('should pause the execution', function () {
var starttime = new Date().getTime();
browser.pause(3000);
var endtime = new Date().getTime();
console.log(endtime - starttime); // outputs: 3000
});
* </example>
*
* @alias browser.pause
* @param {Number} milliseconds time in ms
* @type utility
*
*/
var pause = function pause() {
var milliseconds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1000;
return new _promise2.default(function (resolve) {
return setTimeout(resolve, milliseconds);
});
};
exports.default = pause;
module.exports = exports["default"];

View File

@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Release touch sequence on specific element.
*
* @alias browser.release
* @param {String} selector element to release on
* @uses property/getLocation, protocol/touchUp
* @type mobile
*
*/
var release = function release(selector) {
var _this = this;
return this.getLocation(selector).then(function (res) {
return _this.touchUp(res.x, res.y);
});
};
exports.default = release;
module.exports = exports["default"];

View File

@ -0,0 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise = require("babel-runtime/core-js/promise");
var _promise2 = _interopRequireDefault(_promise);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Creates a new Selenium session with your current capabilities. This is useful if you
* test highly stateful application where you need to clean the browser session between
* the tests in your spec file to avoid creating hundreds of single test files with WDIO.
* Be careful though, this command affects your test time tremendously since spawning
* new Selenium sessions is very time consuming especially when using cloud services.
*
* <example>
:reloadSync.js
it('should reload my session', function () {
console.log(browser.sessionId); // outputs: e042b3f3cd5a479da4e171825e96e655
browser.reload();
console.log(browser.sessionId); // outputs: 9a0d9bf9d4864160aa982c50cf18a573
})
* </example>
*
* @alias browser.reload
* @type utility
*
*/
var reload = function reload() {
var _this = this;
var oldSessionId = this.requestHandler.sessionID;
return this.end().init().then(function (res) {
var newSessionId = _this.requestHandler.sessionID;
if (!Array.isArray(_this.options.onReload)) {
return _promise2.default.resolve();
}
return _promise2.default.all(_this.options.onReload.map(function (hook) {
return hook(oldSessionId, newSessionId);
}));
}).catch(function (e) {
console.log("Error in onReload hook: \"" + e.stack + "\"");
});
};
exports.default = reload;
module.exports = exports["default"];

View File

@ -0,0 +1,30 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _handleMouseButtonCommand = require('../helpers/handleMouseButtonCommand');
var _handleMouseButtonCommand2 = _interopRequireDefault(_handleMouseButtonCommand);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var rightClick = function rightClick(selector, xoffset, yoffset) {
return _handleMouseButtonCommand2.default.call(this, selector, 'right', xoffset, yoffset);
}; /**
*
* Apply right click on an element. If selector is not provided, click on the last
* moved-to location.
*
* @alias browser.rightClick
* @param {String} selector element to click on
* @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.
* @uses protocol/element, protocol/buttonPress
* @type action
*
*/
exports.default = rightClick;
module.exports = exports['default'];

View File

@ -0,0 +1,64 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = saveScreenshot;
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
var _safeBuffer = require('safe-buffer');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Save a screenshot as a base64 encoded PNG with the current state of the browser. Be aware that some Selenium driver
* are taking screenshots of the whole document (e.g. phantomjs) and others only of the current viewport. If you want
* to always be sure that the screenshot has the size of the whole document, use [wdio-screenshot](https://www.npmjs.com/package/wdio-screenshot)
* to enhance this command with that functionality.
*
* This command also doesn't support the [element screenshot](https://w3c.github.io/webdriver/webdriver-spec.html#take-element-screenshot)
* feature yet as it isn't supported by most of the drivers. However the protocol command for it is available
* to use (see [elementIdScreenshot](http://webdriver.io/api/protocol/elementIdScreenshot.html)).
*
* <example>
:saveScreenshot.js
it('should save a screenshot of the browser view', function () {
// receive screenshot as Buffer
var screenshot = browser.saveScreenshot(); // returns base64 string buffer
fs.writeFileSync('./myShort.png', screenshot)
// save screenshot to file and receive as Buffer
screenshot = browser.saveScreenshot('./snapshot.png');
// save screenshot to file
browser.saveScreenshot('./snapshot.png');
});
* </example>
*
* @alias browser.saveScreenshot
* @param {Function|String=} filename path to the generated image (relative to the execution directory)
* @uses protocol/screenshot
* @type utility
*
*/
function saveScreenshot(filename) {
var _this = this;
return this.screenshot().then(function (res) {
_this.emit('screenshot', { data: res.value, filename: filename });
var screenshot = new _safeBuffer.Buffer(res.value, 'base64');
if (typeof filename === 'string') {
_fs2.default.writeFileSync(filename, screenshot);
}
return screenshot;
});
}
module.exports = exports['default'];

View File

@ -0,0 +1,108 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _ErrorHandler = require('../utils/ErrorHandler');
var _scroll = require('../scripts/scroll');
var _scroll2 = _interopRequireDefault(_scroll);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Scroll to a specific element. You can also append/pass two offset values as parameter
* to scroll to a specific position.
*
* <example>
:scroll.js
it('should demonstrate the scroll command', function () {
var elem = $('#myElement');
// scroll to specific element
elem.scroll();
// scroll to specific element with offset
// scroll offset will be added to elements position
elem.scroll(100, 100);
// scroll to specific x and y position
browser.scroll(0, 250);
});
* </example>
*
* @alias browser.scroll
* @param {String=} selector element to scroll to
* @param {Number=} xoffset x offset to scroll to
* @param {Number=} yoffset y offset to scroll to
* @uses protocol/element, protocol/elementIdLocation, protocol/touchScroll, protocol/execute
* @type utility
*
*/
var scroll = function scroll(selector, xoffset, yoffset) {
var _this = this;
/**
* we can't use default values for function parameter here because this would
* break the ability to chain the command with an element if an offset is used
*/
xoffset = typeof xoffset === 'number' ? xoffset : 0;
yoffset = typeof yoffset === 'number' ? yoffset : 0;
if (typeof selector === 'number' && typeof xoffset === 'number') {
yoffset = xoffset;
xoffset = selector;
selector = null;
}
if (this.isMobile) {
var queue = _promise2.default.resolve();
if (selector) {
queue = this.element(selector);
}
return queue.then(function (res) {
/**
* check if element was found and throw error if not
*/
if (res && !res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
if (typeof res !== 'undefined') {
selector = res.value.ELEMENT;
}
return _this.touchScroll(selector, xoffset, yoffset);
});
}
if (selector) {
return this.element(selector).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
return _this.elementIdLocation(res.value.ELEMENT);
}).then(function (location) {
return _this.execute(_scroll2.default, location.value.x + xoffset, location.value.y + yoffset);
});
}
return this.execute(_scroll2.default, xoffset, yoffset);
};
exports.default = scroll;
module.exports = exports['default'];

View File

@ -0,0 +1,88 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var selectByAttribute = function selectByAttribute(selector, attribute, value) {
var _this = this;
/**
* convert value into string
*/
if (typeof value === 'number') {
value = value.toString();
}
/**
* get options element by xpath
*/
return this.element(selector).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
/**
* find option elem using xpath
*/
var normalized = '[normalize-space(@' + attribute.trim() + ') = "' + value.trim() + '"]';
return _this.elementIdElement(res.value.ELEMENT, './option' + normalized + '|./optgroup/option' + normalized);
}).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
/**
* select option
*/
return _this.elementIdClick(res.value.ELEMENT);
});
}; /**
*
* Select option with a specific value.
*
* <example>
:example.html
<select id="selectbox">
<option value="someValue0">uno</option>
<option value="someValue1">dos</option>
<option value="someValue2">tres</option>
<option value="someValue3">cuatro</option>
<option value="someValue4">cinco</option>
<option name="someName5" value="someValue5">seis</option>
</select>
:selectByAttribute.js
it('should demonstrate the selectByAttribute command', function () {
var selectBox = $('#selectbox');
var value = selectBox.getValue();
console.log(value); // returns "someValue0"
selectBox.selectByAttribute('value', 'someValue3');
console.log(selectBox.getValue()); // returns "someValue3"
selectBox.selectByAttribute('name', 'someName5');
console.log(selectBox.getValue()); // returns "someValue5"
});
* </example>
*
* @alias browser.selectByAttribute
* @param {String} selector select element that contains the options
* @param {String} attribute attribute of option element to get selected
* @param {String} value value of option element to get selected
* @uses protocol/element, protocol/elementIdClick, protocol/elementIdElement
* @type action
*
*/
exports.default = selectByAttribute;
module.exports = exports['default'];

View File

@ -0,0 +1,72 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var selectByIndex = function selectByIndex(selector, index) {
var _this = this;
/*!
* negative index check
*/
if (index < 0) {
throw new _ErrorHandler.CommandError('index needs to be 0 or any other positive number');
}
return this.element(selector).then(function (element) {
/**
* check if element was found and throw error if not
*/
if (!element.value) {
throw new _ErrorHandler.RuntimeError(7);
}
return _this.elementIdElements(element.value.ELEMENT, '<option>');
}).then(function (elements) {
if (elements.value.length === 0) {
throw new _ErrorHandler.CommandError('select element (' + selector + ') doesn\'t contain any option element');
}
if (elements.value.length - 1 < index) {
throw new _ErrorHandler.CommandError('option with index "' + index + '" not found. Select element (' + selector + ') only contains ' + elements.value.length + ' option elements');
}
return _this.elementIdClick(elements.value[index].ELEMENT);
});
}; /**
*
* Select option with a specific index.
*
* <example>
:example.html
<select id="selectbox">
<option value="someValue0">uno</option>
<option value="someValue1">dos</option>
<option value="someValue2">tres</option>
<option value="someValue3">cuatro</option>
<option value="someValue4">cinco</option>
<option value="someValue5">seis</option>
</select>
:selectByIndex.js
it('should demonstrate the selectByIndex command', function () {
var selectBox = $('#selectbox');
console.log(selectBox.getValue()); // returns "someValue0"
selectBox.selectByIndex(4);
console.log(selectBox.getValue()); // returns "someValue4"
});
* </example>
*
* @alias browser.selectByIndex
* @param {String} selector select element that contains the options
* @param {Number} index option index
* @uses protocol/element, protocol/elementIdElements, protocol/elementIdClick
* @type action
*
*/
exports.default = selectByIndex;
module.exports = exports['default'];

View File

@ -0,0 +1,44 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Select option with a specific value.
*
* <example>
:example.html
<select id="selectbox">
<option value="someValue0">uno</option>
<option value="someValue1">dos</option>
<option value="someValue2">tres</option>
<option value="someValue3">cuatro</option>
<option value="someValue4">cinco</option>
<option value="someValue5">seis</option>
</select>
:selectByValue.js
it('should demonstrate the selectByValue command', function () {
var selectBox = $('#selectbox');
console.log(selectBox.getValue()); // returns "someValue0"
selectBox.selectByValue('someValue3');
console.log(selectBox.getValue()); // returns "someValue3"
});
* </example>
*
* @alias browser.selectByValue
* @param {String} selector select element that contains the options
* @param {String} value value of option element to get selected
* @uses protocol/element, protocol/elementIdClick, protocol/elementIdElement
* @type action
*
*/
var selectByValue = function selectByValue(selector, value) {
return this.selectByAttribute(selector, 'value', value);
};
exports.default = selectByValue;
module.exports = exports['default'];

View File

@ -0,0 +1,81 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var selectByVisibleText = function selectByVisibleText(selector, text) {
var _this = this;
/**
* get select element
*/
return this.element(selector).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
/**
* find option elem using xpath
*/
var formatted = '"' + text.trim() + '"';
if (/"/.test(text)) {
formatted = 'concat("' + text.trim().split('"').join('", \'"\', "') + '")'; // escape quotes
}
var normalized = '[normalize-space(.) = ' + formatted + ']';
return _this.elementIdElement(res.value.ELEMENT, './option' + normalized + '|./optgroup/option' + normalized);
}).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
/**
* select option
*/
return _this.elementIdClick(res.value.ELEMENT);
});
}; /**
*
* Select option which's displayed text matches the argument.
*
* <example>
:example.html
<select id="selectbox">
<option value="someValue0">uno</option>
<option value="someValue1">dos</option>
<option value="someValue2">tres</option>
<option value="someValue3">cuatro</option>
<option value="someValue4">cinco</option>
<option value="someValue5">seis</option>
</select>
:selectByVisibleText.js
it('demonstrate the selectByVisibleText command', function () {
var selectBox = $('#selectbox');
console.log(selectBox.getText('option:checked')); // returns "uno"
selectBox.selectByVisibleText('cuatro');
console.log(selectBox.getText('option:checked')); // returns "cuatro"
})
* </example>
*
* @alias browser.selectByVisibleText
* @param {String} selector select element that contains the options
* @param {String} text text of option element to get selected
* @uses protocol/element, protocol/elementIdClick, protocol/elementIdElement
* @type action
*
*/
exports.default = selectByVisibleText;
module.exports = exports['default'];

View File

@ -0,0 +1,109 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ensureClientSideSelectorSupport = require('../helpers/ensureClientSideSelectorSupport');
var _ensureClientSideSelectorSupport2 = _interopRequireDefault(_ensureClientSideSelectorSupport);
var _createSelectorScript = require('../scripts/createSelectorScript');
var _createSelectorScript2 = _interopRequireDefault(_createSelectorScript);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var selectorExecute = function selectorExecute(selector, script) {
var _this = this;
for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
/**
* if selectorExecute gets executed with element as first citizen like
*
* ```js
* var elem = $('#elem');
* elem.selectorExecute(function () {...}, some, args);
* ```
*/
if (typeof selector === 'function' && this.lastResult && typeof this.lastResult.selector === 'string') {
args.unshift(script);
script = selector;
selector = [this.lastResult.selector];
/**
* if selectorExecute gets executed by getHTML
*/
} else if (selector === null) {
selector = [this.lastResult.selector];
}
if (typeof selector === 'string') {
selector = [selector];
}
if (!Array.isArray(selector)) {
throw new _ErrorHandler.CommandError('Argument \'selector\' must be string or array of strings.');
}
if (!/string|function/.test(typeof script)) {
throw new _ErrorHandler.CommandError('Argument \'script\' must be a function or string.');
}
var fullScript = _createSelectorScript2.default.call(this, script, selector, args);
return _ensureClientSideSelectorSupport2.default.call(this).execute(fullScript).then(function (res) {
var result = res && res.value;
if (result && result.message === 'NoSuchElement') {
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
return result;
});
}; /**
* Works just like execute, only you can use selectors to pass html elements to
* the function you wish to execute in the browser.
*
* The function fn will receive every resolved selector as an array of html elements,
* even if there is only one result, or no result.
* These arrays are the first arguments the function fn receives.
* If you pass an array of selectors, the resulting html element arrays are returned in the same order.
*
* All arguments you append after function fn are added as the arguments after the html arrays.
* You can use any JSON value or a function as such an argument.
*
* <example>
:selectorExecute.js
it('it inject JavaScript to the page', function () {
var divCount = browser.selectorExecute("//div", function(divs, message) {
return divs.length + message;
}, " divs on the page");
console.log(divCount); // returns, for example, "68 divs on the page"
var divLinkCount = browser.selectorExecute(["//div", "=Read Post"], function(divs, links) {
var message = 'There are ';
message += divs.length + ' divs on the page';
message += ' and ';
message += links.length + ' links with an link text "' + links[0].text + '"';
return message;
});
console.log(divLinkCount); // returns, for example, "There are 68 divs on the page and 42 links with an link text 'Read Post'"
});
* </example>
*
* @alias browser.selectorExecute
* @param {String|Array.<String>} selectors single selector or array of selectors
* @param {Function} script function to get executed in the browser
* @param {...*} [argument1,...,argumentN] arguments added to fn. Can be any JSON value or function
* @uses protocol/execute
* @type action
*/
exports.default = selectorExecute;
module.exports = exports['default'];

View File

@ -0,0 +1,108 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ensureClientSideSelectorSupport = require('../helpers/ensureClientSideSelectorSupport');
var _ensureClientSideSelectorSupport2 = _interopRequireDefault(_ensureClientSideSelectorSupport);
var _createSelectorScript = require('../scripts/createSelectorScript');
var _createSelectorScript2 = _interopRequireDefault(_createSelectorScript);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var selectorExecuteAsync = function selectorExecuteAsync(selector, script) {
var _this = this;
for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
/**
* if selectorExecuteAsync gets executed with element as first citizen like
*
* ```js
* var elem = $('#elem');
* elem.selectorExecuteAsync(function () {...}, some, args);
* ```
*/
if (typeof selector === 'function' && this.lastResult && typeof this.lastResult.selector === 'string') {
args.unshift(script);
script = selector;
selector = [this.lastResult.selector];
/**
* if selectorExecuteAsync gets executed by getHTML
*/
} else if (selector === null) {
selector = [this.lastResult.selector];
}
if (typeof selector === 'string') {
selector = [selector];
}
if (!Array.isArray(selector)) {
throw new _ErrorHandler.CommandError('Argument \'selector\' must be string or array of strings.');
}
if (!/string|function/.test(typeof script)) {
throw new _ErrorHandler.CommandError('Argument \'script\' must be a function or string.');
}
var fullScript = _createSelectorScript2.default.call(this, script, selector, args);
return _ensureClientSideSelectorSupport2.default.call(this).executeAsync(fullScript).then(function (res) {
var result = res && res.value;
if (result && result.message === 'NoSuchElement') {
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
return result;
});
}; /**
* Works just like execute, only you can use Selenium selector strategies to pass html elements to
* the asynchronous function you wish to execute in the browser.
*
* The asynchronous function fn will receive every resolved selector as an array of html elements,
* even if there is only one result, or no result.
* These arrays are the first arguments the function fn receives.
* If you pass an array of selectors, the resulting html element arrays are returned in the same order.
*
* All arguments you append after function fn are added as the arguments after the html arrays.
* You can use any JSON value or a function as such an argument.
*
* <example>
:selectorExecuteAsync.js
it('should be able to inject JavaScript into the page that can be execute asynchronously', function () {
var divCount = browser.selectorExecuteAsync("//div", function(divs, message, callback) {
callback(divs.length + message);
}, " divs on the page")
console.log(divCount); // returns, for example, "68 divs on the page"
var divLinkCount = browser.selectorExecuteAsync(["//div", "=Read Post"], function(divs, links, callback) {
var message = 'There are ';
message += divs.length + ' divs on the page';
message += ' and ';
message += links.length + ' links with an link text "' + links[0].text + '"';
callback(message);
})
console.log(divLinkCount); // returns, for example, "There are 68 divs on the page and 42 links with an link text 'Read Post'"
});
* </example>
*
* @alias browser.selectorExecuteAsync
* @param {String|Array.<String>} selectors single selector or array of selectors
* @param {Function} script asynchronous function to get executed in the browser
* @param {...*} [argument1,...,argumentN] arguments added to fn. Can be any JSON value or function
* @uses protocol/execute
* @type action
*/
exports.default = selectorExecuteAsync;
module.exports = exports['default'];

View File

@ -0,0 +1,43 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var setCookie = function setCookie(cookieObj) {
/*!
* parameter check
*/
if (typeof cookieObj !== 'object') {
throw new _ErrorHandler.CommandError('Please specify a cookie object to set (see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#cookie-json-object for documentation.');
}
return this.cookie('POST', cookieObj);
}; /**
*
* Sets a [cookie](https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#cookie-json-object)
* for current page. Make sure you are on the page that should receive the cookie. You can't set
* a cookie for an arbitrary page without being on that page.
*
* <example>
:setCookie.js
it('should set a cookie for the page', function () {
browser.url('/')
browser.setCookie({name: 'test', value: '123'});
var cookies = browser.getCookie();
console.log(cookies); // outputs: [{ name: 'test', value: '123' }]
});
* </example>
*
* @alias browser.setCookie
* @param {Object} cookie cookie object
* @uses protocol/cookie
* @type cookie
*
*/
exports.default = setCookie;
module.exports = exports['default'];

View File

@ -0,0 +1,30 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var setGeoLocation = function setGeoLocation(location) {
/*!
* parameter check
*/
if (typeof location !== 'object' || location.latitude === undefined || location.longitude === undefined || location.altitude === undefined) {
throw new _ErrorHandler.CommandError('location object need to have a latitude, longitude and altitude attribute');
}
return this.location(location);
}; /**
*
* Set the current geo location.
*
* @alias browser.setGeoLocation
* @param {Object} location the new location (`{latitude: number, longitude: number, altitude: number}`)
* @uses protocol/location
* @type mobile
*
*/
exports.default = setGeoLocation;
module.exports = exports['default'];

View File

@ -0,0 +1,41 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var setOrientation = function setOrientation(orientation) {
/*!
* parameter check
*/
if (typeof orientation !== 'string') {
throw new _ErrorHandler.CommandError('number or type of arguments don\'t agree with setOrientation command');
}
return this.orientation(orientation.toUpperCase());
}; /**
*
* Set a device orientation.
*
* <example>
:setOrientation.js
it('should set a geo location for the device', function () {
browser.setOrientation('landscape');
var orientation = browser.getOrientation();
console.log(orientation); // outputs: "landscape"
});
* </example>
*
* @alias browser.setOrientation
* @param {String} orientation the new browser orientation (`landscape/portrait`)
* @uses protocol/orientation
* @type mobile
* @for android, ios
*
*/
exports.default = setOrientation;
module.exports = exports['default'];

View File

@ -0,0 +1,95 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var setValue = function setValue(selector, value) {
var _this = this;
/*!
* parameter check
*/
if (typeof value === 'number') {
value = value.toString();
}
if (typeof value !== 'string' && !Array.isArray(value)) {
throw new _ErrorHandler.CommandError('number or type of arguments don\'t agree with setValue command');
}
return this.elements(selector).then(function (res) {
/**
* throw NoSuchElement error if no element was found
*/
if (!res.value || res.value.length === 0) {
throw new _ErrorHandler.CommandError(7, selector || _this.lastResult.selector);
}
var elementIdValueCommands = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(res.value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var elem = _step.value;
elementIdValueCommands.push(_this.elementIdClear(elem.ELEMENT).elementIdValue(elem.ELEMENT, value));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return _this.unify(elementIdValueCommands);
});
}; /**
*
* Send a sequence of key strokes to an element (clears value before). You can also use
* unicode characters like Left arrow or Back space. WebdriverIO will take care of
* translating them into unicode characters. Youll find all supported characters
* [here](https://w3c.github.io/webdriver/webdriver-spec.html#dfn-character-types).
* To do that, the value has to correspond to a key from the table.
*
* <example>
:setValue.js
it('should set value for a certain element', function () {
var input = $('.input');
input.setValue('test123');
// same as
browser.setValue('.input', 'test123');
console.log(input.getValue()); // outputs: 'test123'
});
* </example>
*
* @alias browser.setValue
* @param {String} selector Input element
* @param {String|Number|Array} values Input element
* @uses protocol/elements, protocol/elementIdClear, protocol/elementIdValue
* @type action
*
*/
exports.default = setValue;
module.exports = exports['default'];

View File

@ -0,0 +1,99 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getViewportSize = require('../scripts/getViewportSize');
var _getViewportSize2 = _interopRequireDefault(_getViewportSize);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* This command changes the viewport size of the browser. When talking about browser size we have to differentiate
* between the actual window size of the browser application and the document/viewport size of the website. The
* window size will always be bigger since it includes the height of any menu or status bars.
*
* The command tries to resize the browser multiple times (max 5 times) because Webdriver only allows to change
* the window size and doesn't take the viewport into consideration. This is handled by WebdriverIO internally.
*
* <example>
:setViewportSize.js
it('should resize the current viewport', function () {
browser.setViewportSize({
width: 500,
height: 500
});
var windowSize = browser.windowHandleSize();
console.log(windowSize.value); // outputs: { width: 500, height: 602 }
});
* </example>
*
* @alias browser.setViewportSize
* @param {Object} size window width/height
* @param {Boolean} type set to `false` to change window size, `true` (default) to change viewport size
* @uses protocol/execute, protocol/windowHandleSize
* @type window
*
*/
var MAX_TRIES = 5;
var setViewportSize = function setViewportSize(size, type) {
/**
* parameter check
*/
if (typeof size !== 'object' || typeof size.width !== 'number' || typeof size.height !== 'number' || typeof type !== 'undefined' && typeof type !== 'boolean') {
throw new _ErrorHandler.CommandError('number or type of arguments don\'t agree with setViewportSize command');
}
var shouldIndent = typeof type === 'undefined' ? true : type;
return shouldIndent ? _setViewportSize.call(this, size) : this.windowHandleSize(size);
};
/**
* to set viewport size properly we need to execute the process multiple times
* since the difference between the inner and outer size changes when browser
* switch between fullscreen modes or visibility of scrollbar
*/
var _setViewportSize = function _setViewportSize(size) {
var _this = this;
var retryNo = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
/**
* get window size
*/
return this.windowHandleSize().then(function (windowHandleSize) {
/**
* get viewport size
*/
return _this.execute(_getViewportSize2.default).then(function (viewportSize) {
var widthDiff = windowHandleSize.value.width - viewportSize.value.screenWidth;
var heightDiff = windowHandleSize.value.height - viewportSize.value.screenHeight;
/**
* change window size with indent
*/
return _this.windowHandleSize({
width: size.width + widthDiff,
height: size.height + heightDiff
});
}).execute(_getViewportSize2.default).then(function (res) {
/**
* if viewport size not equals desired size, execute process again
*/
if (retryNo < MAX_TRIES && (res.value.screenWidth !== size.width || res.value.screenHeight !== size.height)) {
return _setViewportSize.call(_this, size, ++retryNo);
}
});
});
};
exports.default = setViewportSize;
module.exports = exports['default'];

View File

@ -0,0 +1,53 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var submitForm = function submitForm(selector) {
var _this = this;
return this.element(selector).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
return _this.submit(res.value.ELEMENT);
});
}; /**
*
* Submits a form found by given selector. The submit command may also be applied
* to any element that is a descendant of a `<form>` element.
*
* <example>
:index.html
<form action="/form.php" method="post" id="loginForm">
<label for="username">User:</label>
<input type="text" name="username" id="username">
<label for="password">Password:</label>
<input type="password" name="password" id="password">
<input type="submit" value="Login">
</form>
:submitForm.js
it('should submit login form', function () {
browser.setValue('#username', 'foobar');
browser.setValue('#password', 'test123');
browser.submitForm('#loginForm');
});
* </example>
*
* @alias browser.submitForm
* @param {String} selector form element
* @uses protocol/element, protocol/submit
* @type action
*
*/
exports.default = submitForm;
module.exports = exports['default'];

View File

@ -0,0 +1,54 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var swipe = function swipe(selector, xoffset, yoffset, speed) {
var _this = this;
if (arguments.length === 2 && typeof selector === 'number' && typeof xoffset === 'number') {
/*!
* you don't care where the swipe starts on the screen
*/
var xspeed = selector;
var yspeed = xoffset;
return this.touchFlick(xspeed, yspeed);
}
/*!
* command starts at a particular screen location
*/
return this.element(selector).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
return _this.touchFlick(res.value.ELEMENT.toString(), xoffset, yoffset, speed);
});
}; /**
*
* Perform a swipe on the screen or an element. If you want to swipe on a specific
* element make sure you provide a selector argument. If not just pass `xoffset`
* and `yoffset` as command arguments.
*
* Start at a particular screen location.
*
* @alias browser.swipe
* @param {String=} selector element to swipe on
* @param {Number=} xoffset x offset of swipe gesture (in pixels or relative units)
* @param {Number=} yoffset y offset of swipe gesture (in pixels or relative units)
* @param {Number=} speed time (in seconds) to spend performing the swipe
* @uses protocol/element, protocol/touchFlick
* @type mobile
*
*/
exports.default = swipe;
module.exports = exports['default'];

View File

@ -0,0 +1,36 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Perform a swipe down on an element.
*
* @alias browser.swipeDown
* @param {String} selector element to swipe on
* @param {Number=} yoffset y offset of swipe gesture (in pixels or relative units)
* @param {Number} speed number of pixels go per second
* @uses mobile/swipe
* @type mobile
*
*/
var swipeDown = function swipeDown(selector, yOffset, speed) {
/**
* we can't use default values for function parameter here because this would
* break the ability to chain the command with an element if reverse is used
*/
yOffset = typeof yOffset === 'number' ? yOffset : 100;
speed = typeof speed === 'number' ? speed : 100;
/**
* make sure yoffset is positive so we scroll up
*/
yOffset = yOffset < 0 ? yOffset * -1 : yOffset;
return this.pause(100).swipe(selector, 0, yOffset, speed);
};
exports.default = swipeDown;
module.exports = exports['default'];

View File

@ -0,0 +1,36 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Perform a swipe left on an element.
*
* @alias browser.swipeLeft
* @param {String} selector element to swipe on
* @param {Number=} xoffset x offset of swipe gesture (in pixels or relative units)
* @param {Number} speed time (in seconds) to spend performing the swipe
* @uses mobile/flick
* @type mobile
*
*/
var swipeLeft = function swipeLeft(selector, xOffset, speed) {
/**
* we can't use default values for function parameter here because this would
* break the ability to chain the command with an element if reverse is used
*/
xOffset = typeof xOffset === 'number' ? xOffset : 100;
speed = typeof speed === 'number' ? speed : 100;
/**
* make sure xoffset is positive so we scroll right
*/
xOffset = xOffset > 0 ? xOffset * -1 : xOffset;
return this.pause(100).swipe(selector, xOffset, 0, speed);
};
exports.default = swipeLeft;
module.exports = exports['default'];

View File

@ -0,0 +1,36 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Perform a swipe right on an element.
*
* @alias browser.swipeRight
* @param {String} selector element to swipe on
* @param {Number=} xoffset x offset of swipe gesture (in pixels or relative units)
* @param {Number} speed time (in seconds) to spend performing the swipe
* @uses mobile/swipe
* @type mobile
*
*/
var swipeRight = function swipeRight(selector, xOffset, speed) {
/**
* we can't use default values for function parameter here because this would
* break the ability to chain the command with an element if reverse is used
*/
xOffset = typeof xOffset === 'number' ? xOffset : -100;
speed = typeof speed === 'number' ? speed : 100;
/**
* make sure xoffset is negative so we scroll left
*/
xOffset = xOffset < 0 ? xOffset * -1 : xOffset;
return this.pause(100).swipe(selector, xOffset, 0, speed);
};
exports.default = swipeRight;
module.exports = exports['default'];

View File

@ -0,0 +1,36 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Perform a swipe up on an element.
*
* @alias browser.swipeUp
* @param {String} selector element to swipe on
* @param {Number=} yoffset y offset of swipe gesture (in pixels or relative units)
* @param {Number} speed time (in seconds) to spend performing the swipe
* @uses mobile/swipe
* @type mobile
*
*/
var swipeUp = function swipeUp(selector, yOffset, speed) {
/**
* we can't use default values for function parameter here because this would
* break the ability to chain the command with an element if reverse is used
*/
yOffset = typeof yOffset === 'number' ? yOffset : -100;
speed = typeof speed === 'number' ? speed : 100;
/**
* make sure yoffset is negative so we scroll down
*/
yOffset = yOffset > 0 ? yOffset * -1 : yOffset;
return this.pause(100).swipe(selector, 0, yOffset, speed);
};
exports.default = swipeUp;
module.exports = exports['default'];

View File

@ -0,0 +1,39 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
*
* Switch focus to a particular tab / window handle.
*
* @alias browser.switchTab
* @param {String=} windowHandle window handle URL to focus on (if no handle was specified the command switches to the first available one)
* @uses protocol/window, window/getTabIds, window/switchTab
* @type window
*
*/
var switchTab = function switchTab(windowHandle) {
var _this = this;
/*!
* parameter check
*/
if (typeof windowHandle !== 'string') {
windowHandle = null;
}
if (windowHandle) {
return this.window(windowHandle);
}
return this.windowHandles().then(function (tabIds) {
if (tabIds && tabIds.value && tabIds.value.length) {
return _this.switchTab(tabIds.value[0]);
}
});
};
exports.default = switchTab;
module.exports = exports['default'];

View File

@ -0,0 +1,35 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* Put finger on an element (only in mobile context).
*
* @alias browser.touch
* @param {String} selector element to put finger on
* @param {Boolean} longClick if true touch click will be long (default: false)
* @uses property/getLocation, protocol/touchClick
* @type mobile
* @uses android
*
*/
var touch = function touch(selector, longClick) {
var _this = this;
/**
* we can't use default values for function parameter here because this would
* break the ability to chain the command with an element if reverse is used
*/
longClick = typeof longClick === 'boolean' ? longClick : false;
var touchCommand = longClick ? 'touchLongClick' : 'touchClick';
return this.getLocation(selector).then(function (val) {
return _this[touchCommand](val.x, val.y);
});
};
exports.default = touch;
module.exports = exports['default'];

View File

@ -0,0 +1,297 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
exports.default = touchAction;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* The Touch Action API provides the basis of all gestures that can be automated in Appium.
* It is currently only available to native apps and can not be used to interact with webapps.
* At its core is the ability to chain together _ad hoc_ individual actions, which will then be
* applied to an element in the application on the device. The basic actions that can be used are:
*
* - press (pass selector or (x,y) or both)
* - longPress (pass selector or (x,y) or both)
* - tap (pass selector or (x,y) or both)
* - moveTo (pass selector or (x,y) or both)
* - wait (pass ms (as milliseconds))
* - release (no arguments)
*
* If you use the touchAction command with a selector you don't need to pass the selector to each
* action. It will be propagated by the internally (if no x or y parameters are given).
*
* <example>
:touchAction.js
it('should do a touch gesture', function () {
var screen = $('//UITextbox');
// simple touch action on element
screen.touchAction('tap');
// same as
browser.touchAction('//UITextbox', 'tap')
// simple touch action using x y variables
browser.touchAction({
actions: 'tap', x: 300, y:200
})
// simple touch action using selector and x y variables
// tap location is 30px right and 20px down relative from the center of the element
browser.touchAction({
actions: 'tap', x: 30, y:20, selector: '//UIAApplication[1]/UIAElement[2]'
})
// multi action on an element (drag&drop)
screen.touchAction([
'press',
{ action: 'moveTo', x: 200, y: 0 },
'release'
])
// same as
browser.touchAction('//UITextbox', [
'press',
{ action: 'moveTo', x: 200, y: 0},
'release'
])
// drag&drop to element
screen.touchAction([
'press',
{ action: 'moveTo', selector: '//UIAApplication[1]/UIAElement[2]' },
'release'
]))
});
:multiTouchAction.js
it('should do a multitouch gesture', function () {
// drag&drop with two fingers 200px down
browser.touchAction([
[{action: 'press', x: 10, y: 10}, { action: 'moveTo', x: 0, y: 200 }, 'release'],
[{action: 'press', x: 100, y: 10}, { action: 'moveTo', x: 0, y: 200 }, 'release']]
])
})
* </example>
*
* @param {String} selector selector to execute the touchAction on
* @param {String} action action to execute
*
* @see https://saucelabs.com/blog/appium-sauce-labs-bootcamp-chapter-2-touch-actions
* @type mobile
* @for android, ios
* @uses mobile/performTouchAction, mobile/performMultiAction
*
*/
var TOUCH_ACTIONS = ['press', 'longPress', 'tap', 'moveTo', 'wait', 'release'];
var POS_ACTIONS = TOUCH_ACTIONS.slice(0, -2);
var ACCEPTED_OPTIONS = ['x', 'y', 'selector', 'element'];
function touchAction(selector, actions) {
var _this = this;
if (typeof selector !== 'string' || TOUCH_ACTIONS.indexOf(selector) > -1) {
actions = selector;
selector = this.lastResult;
}
if (!Array.isArray(actions)) {
actions = [actions];
}
/**
* check if multiAction
*/
if (Array.isArray(actions[0])) {
actions = formatArgs(selector, actions);
return _promise2.default.all(getSelectors.call(this, actions, true)).then(function (jsonElements) {
actions = replaceSelectorsById(actions, jsonElements);
return _this.performMultiAction({ actions: actions });
});
}
actions = formatArgs(selector, actions);
return _promise2.default.all(getSelectors.call(this, actions)).then(function (jsonElements) {
actions = replaceSelectorsById(actions, jsonElements);
return _this.performTouchAction({ actions: actions });
});
}
/**
* helper to determine if action has proper option arguments
* ('press', 'longPress', 'tap', 'moveTo' need at least some kind of position information)
* @param {String} action name of action
* @param {Object} options action options
* @return {Boolean} True if don't need any options or has a position option
*/
var hasValidActionOptions = function hasValidActionOptions(action, options) {
return POS_ACTIONS.indexOf(action) < 0 || POS_ACTIONS.indexOf(action) > -1 && (0, _keys2.default)(options).length > 0;
};
var formatArgs = function formatArgs(selector, actions) {
return actions.map(function (action) {
if (Array.isArray(action)) {
return formatArgs(selector, action);
}
var formattedAction = { action: action.action, options: {} };
/**
* propagate selector or element to options object
*/
if (selector &&
// selector is given as string `e.g. browser.touchAction(selector, 'tap')`
typeof selector === 'string' &&
// don't propagate for actions that don't require element options
POS_ACTIONS.indexOf(typeof action === 'string' ? action : formattedAction.action) > -1 &&
// don't propagate if user has x and y set
!(isFinite(action.x) && isFinite(action.y))) {
formattedAction.options.selector = selector;
} else if (selector &&
// selector is given by previous command
// e.g. $(selector).touchAction('tap')
selector.value &&
// don't propagate for actions that don't require element options
POS_ACTIONS.indexOf(typeof action === 'string' ? action : formattedAction.action) > -1 &&
// don't propagate if user has x and y set
!(isFinite(action.x) && isFinite(action.y))) {
formattedAction.options.element = selector.value.ELEMENT;
}
if (typeof action === 'string') {
if (!hasValidActionOptions(action, formattedAction.options)) {
throw new Error('Touch action "' + action + '" doesn\'t have proper options. Make sure certain actions like ' + (POS_ACTIONS.join(', ') + ' have position options like "selector", "x" or "y".'));
}
formattedAction.action = action;
/**
* remove options property if empyt
*/
if ((0, _keys2.default)(formattedAction.options).length === 0) {
delete formattedAction.options;
}
return formattedAction;
}
if (isFinite(action.x)) formattedAction.options.x = action.x;
if (isFinite(action.y)) formattedAction.options.y = action.y;
if (action.ms) formattedAction.options.ms = action.ms;
if (action.selector && POS_ACTIONS.indexOf(formattedAction.action) > -1) {
formattedAction.options.selector = action.selector;
}
if (action.element) {
formattedAction.options.element = action.element;
delete formattedAction.options.selector;
}
/**
* remove options property if empyt
*/
if ((0, _keys2.default)(formattedAction.options).length === 0) {
delete formattedAction.options;
}
/**
* option check
* make sure action has proper options before sending command to Appium
*/
if (formattedAction.action === 'release' && formattedAction.options) {
throw new Error('action "release" doesn\'t accept any options ' + ('("' + (0, _keys2.default)(formattedAction.options).join('", "') + '" found)'));
} else if (formattedAction.action === 'wait' && ((0, _keys2.default)(formattedAction.options).indexOf('x') > -1 || (0, _keys2.default)(formattedAction.options).indexOf('y') > -1)) {
throw new Error('action "wait" doesn\'t accept x, y options');
} else if (POS_ACTIONS.indexOf(formattedAction.action) > -1) {
for (var option in formattedAction.options) {
if (ACCEPTED_OPTIONS.indexOf(option) === -1) {
throw new Error('action "' + formattedAction.action + '" doesn\'t accept "' + option + '" as option');
}
}
if ((0, _keys2.default)(formattedAction.options || {}).length === 0) {
throw new Error('Touch actions like "' + formattedAction.action + '" need at least some kind of ' + 'position information like "selector", "x" or "y" options, you\'ve none given.');
}
}
return formattedAction;
});
};
var getSelectors = function getSelectors(actions) {
var _this2 = this;
var isMultiAction = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var queriedSelectors = [];
/**
* flatten actions array
*/
if (isMultiAction) {
actions = [].concat.apply([], actions);
}
return actions
/**
* map down to list of selectors
*/
.map(function (action) {
return action.options && action.options.selector;
})
/**
* filter actions without selector and unique selectors
*/
.filter(function (selector) {
var res = Boolean(selector) && queriedSelectors.indexOf(selector) === -1;
queriedSelectors.push(selector);
return res;
})
/**
* call element command on selectors
*/
.map(function (selector) {
return _this2.element(selector);
});
};
/**
* replaces selector action properties with element ids after they got fetched
* @param {Object[]} actions list of actions
* @param {Object[]} elements list of fetched elements
* @return {Object[]} list of actions with proper element ids
*/
var replaceSelectorsById = function replaceSelectorsById(actions, elements) {
return actions.map(function (action) {
if (Array.isArray(action)) {
return replaceSelectorsById(action, elements);
}
if (!action.options || !action.options.selector) {
return action;
}
elements.forEach(function (element) {
if (action.options.selector === element.selector) {
action.options.element = element.value.ELEMENT;
delete action.options.selector;
}
});
return action;
});
};
module.exports = exports['default'];

View File

@ -0,0 +1,69 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _archiver = require('archiver');
var _archiver2 = _interopRequireDefault(_archiver);
var _ErrorHandler = require('../utils/ErrorHandler');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Uploads a file to the selenium server by using the [`file`](/api/protocol/file.html) command. Note that
* this command might not be supported as it is an undocumented Selenium command.
*
* @alias browser.uploadFile
* @param {String} localPath local path to file
* @type utility
* @uses protocol/file
*
*/
var uploadFile = function uploadFile(localPath) {
var _this = this;
/*!
* parameter check
*/
if (typeof localPath !== 'string') {
throw new _ErrorHandler.CommandError('number or type of arguments don\'t agree with uploadFile command');
}
var zipData = [];
var source = _fs2.default.createReadStream(localPath);
return new _promise2.default(function (resolve, reject) {
(0, _archiver2.default)('zip').on('error', function (e) {
throw new Error(e);
}).on('data', function (data) {
return zipData.push(data);
}).on('end', function () {
return _this.file(Buffer.concat(zipData).toString('base64')).then(resolve, reject);
}).append(source, { name: _path2.default.basename(localPath) }).finalize(function (err) {
/* istanbul ignore next */
if (err) {
reject(err);
}
});
});
};
exports.default = uploadFile;
module.exports = exports['default'];

View File

@ -0,0 +1,114 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Wait for an element (selected by css selector) for the provided amount of
* milliseconds to be (dis/en)abled. If multiple elements get queryied by given
* selector, it returns true (or false if reverse flag is set) if at least one
* element is (dis/en)abled.
*
* <example>
:index.html
<input type="text" id="username" value="foobar" disabled="disabled"></input>
<script type="text/javascript">
setTimeout(function () {
document.getElementById('username').disabled = false
}, 2000);
</script>
:waitForEnabledExample.js
it('should detect when element is enabled', function () {
browser.waitForEnabled('#username', 3000);
// same as
elem = $('#username');
elem.waitForEnabled(3000)
});
* </example>
*
* @alias browser.waitForEnabled
* @param {String} selector element to wait for
* @param {Number=} ms time in ms (default: 500)
* @param {Boolean=} reverse if true it waits for the opposite (default: false)
* @uses utility/waitUntil, state/isEnabled
* @type utility
*
*/
var waitForEnabled = function waitForEnabled(selector, ms, reverse) {
var _this = this;
/**
* we can't use default values for function parameter here because this would
* break the ability to chain the command with an element if reverse is used, like
*
* ```js
* var elem = $('#elem');
* elem.waitForXXX(10000, true);
* ```
*/
reverse = typeof reverse === 'boolean' ? reverse : false;
/*!
* ensure that ms is set properly
*/
if (typeof ms !== 'number') {
ms = this.options.waitforTimeout;
}
var isReversed = reverse ? '' : 'not';
var errorMsg = 'element ("' + (selector || this.lastResult.selector) + '") still ' + isReversed + ' enabled after ' + ms + 'ms';
return this.waitUntil(function () {
return _this.isEnabled(selector).then(function (isEnabled) {
if (!Array.isArray(isEnabled)) {
return isEnabled !== reverse;
}
var result = reverse;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(isEnabled), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var val = _step.value;
if (!reverse) {
result = result || val;
} else {
result = result && val;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return result !== reverse;
});
}, ms, errorMsg);
};
exports.default = waitForEnabled;
module.exports = exports['default'];

View File

@ -0,0 +1,108 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Wait for an element (selected by css selector) for the provided amount of
* milliseconds to be present within the DOM. Returns true if the selector
* matches at least one element that exists in the DOM, otherwise throws an
* error. If the reverse flag is true, the command will instead return true
* if the selector does not match any elements.
*
* <example>
:waitForExistSyncExample.js
it('should display a notification message after successful form submit', function () {
var form = $('form');
var notification = $('.notification');
form.submit();
notification.waitForExist(5000); // same as `browser.waitForExist('.notification', 5000)`
expect(notification.getText()).to.be.equal('Data transmitted successfully!')
});
* </example>
*
* @alias browser.waitForExist
* @param {String} selector CSS selector to query
* @param {Number=} ms time in ms (default: 500)
* @param {Boolean=} reverse if true it instead waits for the selector to not match any elements (default: false)
* @uses utility/waitUntil, state/isExisting
* @type utility
*
*/
var waitForExist = function waitForExist(selector, ms, reverse) {
var _this = this;
/**
* we can't use default values for function parameter here because this would
* break the ability to chain the command with an element if reverse is used, like
*
* ```js
* var elem = $('#elem');
* elem.waitForXXX(10000, true);
* ```
*/
reverse = typeof reverse === 'boolean' ? reverse : false;
/*!
* ensure that ms is set properly
*/
if (typeof ms !== 'number') {
ms = this.options.waitforTimeout;
}
var isReversed = reverse ? '' : 'not';
var errorMsg = 'element ("' + (selector || this.lastResult.selector) + '") still ' + isReversed + ' existing after ' + ms + 'ms';
return this.waitUntil(function () {
return _this.isExisting(selector).then(function (isExisting) {
if (!Array.isArray(isExisting)) {
return isExisting !== reverse;
}
var result = reverse;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(isExisting), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var val = _step.value;
if (!reverse) {
result = result || val;
} else {
result = result && val;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return result !== reverse;
});
}, ms, errorMsg);
};
exports.default = waitForExist;
module.exports = exports['default'];

View File

@ -0,0 +1,117 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Wait for an option or radio/checkbox element (selected by css selector) for the provided amount of
* milliseconds to be (un)selected or (un)checked. If multiple elements get queryied by given
* selector, it returns true (or false if reverse flag is set) if at least one element is (un)selected.
*
* <example>
:index.html
<select>
<option value="1" id="option1">1</option>
<option value="2" id="option2" selected="selected">2</option>
<option value="3" id="option3">3</option>
</select>
<script type="text/javascript">
setTimeout(function () {
document.getElementById('option1').selected = true;
}, 2000);
</script>
:waitForSelectedExample.js
it('should detect when an option is selected', function () {
browser.waitForSelected('#option1', 3000);
// same as
elem = $('#option1');
elem.waitForSelected(3000)
});
* </example>
*
* @alias browser.waitForSelected
* @param {String} selector element to wait for
* @param {Number=} ms time in ms (default: 500)
* @param {Boolean=} reverse if true it waits for the opposite (default: false)
* @uses utility/waitUntil, state/isSelected
* @type utility
*
*/
var waitForSelected = function waitForSelected(selector, ms, reverse) {
var _this = this;
/**
* we can't use default values for function parameter here because this would
* break the ability to chain the command with an element if reverse is used, like
*
* ```js
* var elem = $('#elem');
* elem.waitForXXX(10000, true);
* ```
*/
reverse = typeof reverse === 'boolean' ? reverse : false;
/*!
* ensure that ms is set properly
*/
if (typeof ms !== 'number') {
ms = this.options.waitforTimeout;
}
var isReversed = reverse ? '' : 'not';
var errorMsg = 'element ("' + (selector || this.lastResult.selector) + '") still ' + isReversed + ' selected after ' + ms + 'ms';
return this.waitUntil(function () {
return _this.isSelected(selector).then(function (isSelected) {
if (!Array.isArray(isSelected)) {
return isSelected !== reverse;
}
var result = reverse;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(isSelected), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var val = _step.value;
if (!reverse) {
result = result || val;
} else {
result = result && val;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return result !== reverse;
});
}, ms, errorMsg);
};
exports.default = waitForSelected;
module.exports = exports['default'];

View File

@ -0,0 +1,114 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Wait for an element (selected by css selector) for the provided amount of
* milliseconds to have text/content. If multiple elements get queryied by given
* selector, it returns true (or false if reverse flag is set) if at least one
* element has text/content.
*
* <example>
:index.html
<div id="elem"></div>
<script type="text/javascript">
setTimeout(function () {
document.getElementById('elem').innerHTML = 'some text';
}, 2000);
</script>
:waitForTextExample.js
it('should detect when element has text', function () {
browser.waitForText('#elem', 3000);
// same as
elem = $('#elem');
elem.waitForText(3000)
});
* </example>
*
* @alias browser.waitForText
* @param {String} selector element to wait for
* @param {Number=} ms time in ms (default: 500)
* @param {Boolean=} reverse if true it waits for the opposite (default: false)
* @uses utility/waitUntil, property/getText
* @type utility
*
*/
var waitForText = function waitForText(selector, ms, reverse) {
var _this = this;
/**
* we can't use default values for function parameter here because this would
* break the ability to chain the command with an element if reverse is used, like
*
* ```js
* var elem = $('#elem');
* elem.waitForXXX(10000, true);
* ```
*/
reverse = typeof reverse === 'boolean' ? reverse : false;
/*!
* ensure that ms is set properly
*/
if (typeof ms !== 'number') {
ms = this.options.waitforTimeout;
}
var isReversed = reverse ? 'with' : 'without';
var errorMsg = 'element ("' + (selector || this.lastResult.selector) + '") still ' + isReversed + ' text after ' + ms + 'ms';
return this.waitUntil(function () {
return _this.getText(selector).then(function (text) {
if (!Array.isArray(text)) {
return text !== '' !== reverse;
}
var result = reverse;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(text), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var val = _step.value;
if (!reverse) {
result = result || val !== '';
} else {
result = result && val === '';
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return result !== reverse;
});
}, ms, errorMsg);
};
exports.default = waitForText;
module.exports = exports['default'];

View File

@ -0,0 +1,114 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Wait for an element (selected by css selector) for the provided amount of
* milliseconds to have a value. If multiple elements get queryied by given
* selector, it returns true (or false if reverse flag is set) if at least one
* element has a value.
*
* <example>
:index.html
<input name="someInput" id="elem" value=""></input>
<script type="text/javascript">
setTimeout(function () {
document.getElementById('elem').value = 'some text';
}, 2000);
</script>
:waitForValueExample.js
it('should detect when element has value', function () {
browser.waitForValue('#elem', 3000);
// same as
elem = $('#elem');
elem.waitForValue(3000)
});
* </example>
*
* @alias browser.waitForValue
* @param {String} selector element to wait
* @param {Number=} ms time in ms (default: 500)
* @param {Boolean=} reverse if true it waits for the opposite (default: false)
* @uses utility/waitUntil, property/getValue
* @type utility
*
*/
var waitForValue = function waitForValue(selector, ms, reverse) {
var _this = this;
/**
* we can't use default values for function parameter here because this would
* break the ability to chain the command with an element if reverse is used, like
*
* ```js
* var elem = $('#elem');
* elem.waitForXXX(10000, true);
* ```
*/
reverse = typeof reverse === 'boolean' ? reverse : false;
/*!
* ensure that ms is set properly
*/
if (typeof ms !== 'number') {
ms = this.options.waitforTimeout;
}
var isReversed = reverse ? 'with' : 'without';
var errorMsg = 'element ("' + (selector || this.lastResult.selector) + '") still ' + isReversed + ' a value after ' + ms + 'ms';
return this.waitUntil(function () {
return _this.getValue(selector).then(function (value) {
if (!Array.isArray(value)) {
return value !== '' !== reverse;
}
var result = reverse;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(value), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var val = _step.value;
if (!reverse) {
result = result || val !== '';
} else {
result = result && val === '';
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return result !== reverse;
});
}, ms, errorMsg);
};
exports.default = waitForValue;
module.exports = exports['default'];

View File

@ -0,0 +1,114 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* Wait for an element (selected by css selector) for the provided amount of
* milliseconds to be (in)visible. If multiple elements get queryied by given
* selector, it returns true (or false if reverse flag is set) if at least one
* element is visible.
*
* <example>
:index.html
<div id="elem" style="visibility: hidden;">Hello World!</div>
<script type="text/javascript">
setTimeout(function () {
document.getElementById('elem').style.visibility = 'visible';
}, 2000);
</script>
:waitForVisibleExample.js
it('should detect when element is visible', function () {
browser.waitForVisible('#elem', 3000);
// same as
elem = $('#elem');
elem.waitForVisible(3000)
});
* </example>
*
* @alias browser.waitForVisible
* @param {String} selector element to wait for
* @param {Number=} ms time in ms (default: 500)
* @param {Boolean=} reverse if true it waits for the opposite (default: false)
* @uses utility/waitUntil, state/isVisible
* @type utility
*
*/
var waitForVisible = function waitForVisible(selector, ms, reverse) {
var _this = this;
/**
* we can't use default values for function parameter here because this would
* break the ability to chain the command with an element if reverse is used, like
*
* ```js
* var elem = $('#elem');
* elem.waitForXXX(10000, true);
* ```
*/
reverse = typeof reverse === 'boolean' ? reverse : false;
/*!
* ensure that ms is set properly
*/
if (typeof ms !== 'number') {
ms = this.options.waitforTimeout;
}
var isReversed = reverse ? '' : 'not';
var errorMsg = 'element ("' + (selector || this.lastResult.selector) + '") still ' + isReversed + ' visible after ' + ms + 'ms';
return this.waitUntil(function () {
return _this.isVisible(selector).then(function (isVisible) {
if (!Array.isArray(isVisible)) {
return isVisible !== reverse;
}
var result = reverse;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(isVisible), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var val = _step.value;
if (!reverse) {
result = result || val;
} else {
result = result && val;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return result !== reverse;
});
}, ms, errorMsg);
};
exports.default = waitForVisible;
module.exports = exports['default'];

View File

@ -0,0 +1,92 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
exports.default = function (condition, timeout, timeoutMsg, interval) {
/*!
* ensure that timeout and interval are set properly
*/
if (typeof timeout !== 'number') {
timeout = this.options.waitforTimeout;
}
if (typeof interval !== 'number') {
interval = this.options.waitforInterval;
}
var fn = void 0;
if (typeof condition === 'function') {
fn = condition.bind(this);
} else {
fn = function fn() {
return _promise2.default.resolve(condition);
};
}
var isSync = this.options.sync;
var timer = new _Timer2.default(interval, timeout, fn, true, isSync);
return timer.catch(function (e) {
if (e.message === 'timeout' && typeof timeoutMsg === 'string') {
throw new _ErrorHandler.WaitUntilTimeoutError(timeoutMsg);
}
if (e.type === 'NoSuchElement') {
throw new _ErrorHandler.WaitUntilTimeoutError(e.message);
}
throw new _ErrorHandler.WaitUntilTimeoutError('Promise was rejected with the following reason: ' + e.message);
});
};
var _ErrorHandler = require('../utils/ErrorHandler');
var _Timer = require('../utils/Timer');
var _Timer2 = _interopRequireDefault(_Timer);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
module.exports = exports['default']; /**
*
* This wait command is your universal weapon if you want to wait on something. It expects a condition
* and waits until that condition is fulfilled with a truthy value. A condition can be either a promise
* or a command result. The commands within the condition are getting executed synchronously like in
* your test.
*
* A common example is to wait until a certain element contains a certain text (see example).
*
* <example>
:example.html
<div id="someText">I am some text</div>
<script>
setTimeout(function() {
$('#someText').html('I am now different');
}, 1000);
</script>
:waitUntil.js
it('should wait until text has changed', function () {
browser.waitUntil(function () {
return browser.getText('#someText') === 'I am now different'
}, 5000, 'expected text to be different after 5s');
});
* </example>
*
*
* @alias browser.waitUntil
* @param {Function|Promise} condition condition to wait on
* @param {Number=} timeout timeout in ms (default: 500)
* @param {String=} timeoutMsg error message to throw when waitUntil times out
* @param {Number=} interval interval between condition checks (default: 500)
* @uses utility/pause
* @type utility
*
*/

View File

@ -0,0 +1,225 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* logger colors
*/
var COLORS = {
black: '\x1b[0;30m',
dkgray: '\x1b[1;30m',
brick: '\x1b[0;31m',
red: '\x1b[1;31m',
dkred: '\x1b[31m',
green: '\x1b[0;32m',
lime: '\x1b[1;32m',
brown: '\x1b[0;33m',
yellow: '\x1b[1;33m',
navy: '\x1b[0;34m',
blue: '\x1b[1;34m',
violet: '\x1b[0;35m',
magenta: '\x1b[1;35m',
teal: '\x1b[0;36m',
cyan: '\x1b[1;36m',
ltgray: '\x1b[0;37m',
white: '\x1b[1;37m',
reset: '\x1b[0m'
};
/**
* Webdriver Errors
*/
var Unknown = { id: 'Unknown', status: -1, message: 'Remote end send an unknown status code.' };
var Success = { id: 'Success', status: 0, message: 'The command executed successfully.' };
var NoSuchDriver = { id: 'NoSuchDriver', status: 6, message: 'A session is either terminated or not started' };
var NoSuchElement = { id: 'NoSuchElement', status: 404, message: 'An element could not be located on the page using the given search parameters.' };
var NoSuchFrame = { id: 'NoSuchFrame', status: 400, message: 'A request to switch to a frame could not be satisfied because the frame could not be found.' };
var UnknownCommand = { id: 'UnknownCommand', status: 404, 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.' };
var StaleElementReference = { id: 'StaleElementReference', status: 400, message: 'An element command failed because the referenced element is no longer attached to the DOM.' };
var ElementNotVisible = { id: 'ElementNotVisible', status: 11, message: 'An element command could not be completed because the element is not visible on the page.' };
var InvalidElementState = { id: 'InvalidElementState', status: 400, message: 'An element command could not be completed because the element is in an invalid state (e.g. attempting to click a disabled element).' };
var UnknownError = { id: 'UnknownError', status: 500, message: 'An unknown server-side error occurred while processing the command.' };
var ElementIsNotSelectable = { id: 'ElementIsNotSelectable', status: 400, message: 'An attempt was made to select an element that cannot be selected.' };
var JavaScriptError = { id: 'JavaScriptError', status: 500, message: 'An error occurred while executing user supplied JavaScript.' };
var XPathLookupError = { id: 'XPathLookupError', status: 19, message: 'An error occurred while searching for an element by XPath.' };
var Timeout = { id: 'Timeout', status: 408, message: 'An operation did not complete before its timeout expired.' };
var NoSuchWindow = { id: 'NoSuchWindow', status: 400, message: 'A request to switch to a different window could not be satisfied because the window could not be found.' };
var InvalidCookieDomain = { id: 'InvalidCookieDomain', status: 400, message: 'An illegal attempt was made to set a cookie under a different domain than the current page.' };
var UnableToSetCookie = { id: 'UnableToSetCookie', status: 500, message: 'A request to set a cookie\'s value could not be satisfied.' };
var UnexpectedAlertOpen = { id: 'UnexpectedAlertOpen', status: 500, message: 'A modal dialog was open, blocking this operation' };
var NoAlertOpenError = { id: 'NoAlertOpenError', status: 400, message: 'An attempt was made to operate on a modal dialog when one was not open.' };
var ScriptTimeout = { id: 'ScriptTimeout', status: 408, message: 'A script did not complete before its timeout expired.' };
var InvalidElementCoordinates = { id: 'InvalidElementCoordinates', status: 400, message: 'The coordinates provided to an interactions operation are invalid.' };
var IMENotAvailable = { id: 'IMENotAvailable', status: 30, message: 'IME was not available.' };
var IMEEngineActivationFailed = { id: 'IMEEngineActivationFailed', status: 31, message: 'An IME engine could not be started.' };
var InvalidSelector = { id: 'InvalidSelector', status: 400, message: 'Argument was an invalid selector (e.g. XPath/CSS).' };
var SessionNotCreatedException = { id: 'SessionNotCreatedException', status: 500, message: 'A new session could not be created.' };
var ElementNotScrollable = { id: 'ElementNotScrollable', status: 34, message: 'Element cannot be scrolled into view.' };
var SelectorTimeoutError = { id: 'SelectorTimeoutError', status: 100, message: 'Request timed out after the element was still found on the page.' };
var NoSessionIdError = { id: 'NoSessionIdError', status: 101, message: 'A session id is required for this command but wasn\'t found in the response payload' };
var GridApiError = { id: 'GridApiError', status: 102, message: 'A call to the Selenium Grid API failed' };
var ElementClickIntercepted = { id: 'ElementClickIntercepted', status: 400, message: 'The Element Click command could not be completed because the element receiving the events is obscuring the element that was requested clicked.' };
var ElementNotInteractable = { id: 'ElementNotInteractable', status: 400, message: 'A command could not be completed because the element is not pointer- or keyboard interactable.' };
var InsecureCertificate = { id: 'InsecureCertificate', status: 400, message: 'Navigation caused the user agent to hit a certificate warning, which is usually the result of an expired or invalid TLS certificate.' };
var InvalidArgument = { id: 'InvalidArgument', status: 400, message: 'The arguments passed to a command are either invalid or malformed.' };
var InvalidSessionId = { id: 'InvalidSessionId', status: 404, message: 'Occurs if the given session id is not in the list of active sessions, meaning the session either does not exist or that its not active.' };
var MoveTargetOutOfBounds = { id: 'MoveTargetOutOfBounds', status: 500, message: 'The target for mouse interaction is not in the browsers viewport and cannot be brought into that viewport.' };
var NoSuchCookie = { id: 'NoSuchCookie', status: 404, message: 'No cookie matching the given path name was found amongst the associated cookies of the current browsing contexts active document.' };
var UnableToCaptureScreen = { id: 'UnableToCaptureScreen', status: 500, message: 'A screen capture was made impossible.' };
var UnknownMethod = { id: 'UnknownMethod', status: 405, message: 'The requested command matched a known URL but did not match an method for that URL.' };
var UnsupportedOperation = { id: 'UnsupportedOperation', status: 500, message: 'Indicates that a command that should have executed properly cannot be supported for some reason.' };
/**
* selenium error codes
* https://w3c.github.io/webdriver/webdriver-spec.html#dfn-error-code
*/
var ERROR_CODES = {
'-1': Unknown,
'0': Success,
'6': NoSuchDriver,
'7': NoSuchElement,
'8': NoSuchFrame,
'9': UnknownCommand,
'10': StaleElementReference,
'11': ElementNotVisible,
'12': InvalidElementState,
'13': UnknownError,
'15': ElementIsNotSelectable,
'17': JavaScriptError,
'19': XPathLookupError,
'21': Timeout,
'23': NoSuchWindow,
'24': InvalidCookieDomain,
'25': UnableToSetCookie,
'26': UnexpectedAlertOpen,
'27': NoAlertOpenError,
'28': ScriptTimeout,
'29': InvalidElementCoordinates,
'30': IMENotAvailable,
'31': IMEEngineActivationFailed,
'32': InvalidSelector,
'33': SessionNotCreatedException,
'34': ElementNotScrollable,
// WebdriverIO specific error codes
'100': SelectorTimeoutError,
'101': NoSessionIdError,
'102': GridApiError,
// W3C Webdriver errors
'element click intercepted': ElementClickIntercepted,
'element not selectable': ElementIsNotSelectable,
'element not interactable': ElementNotInteractable,
'insecure certificate': InsecureCertificate,
'invalid argument': InvalidArgument,
'invalid cookie domain': InvalidCookieDomain,
'invalid coordinates': InvalidElementCoordinates,
'invalid element state': InvalidElementState,
'invalid selector': InvalidSelector,
'invalid session id': InvalidSessionId,
'javascript error': JavaScriptError,
'move target out of bounds': MoveTargetOutOfBounds,
'no such alert': NoAlertOpenError,
'no such cookie': NoSuchCookie,
'no such element': NoSuchElement,
'no such frame': NoSuchFrame,
'no such window': NoSuchWindow,
'script timeout': ScriptTimeout,
'session not created': SessionNotCreatedException,
'stale element reference': StaleElementReference,
'timeout': Timeout,
'unable to set cookie': UnableToSetCookie,
'unable to capture screen': UnableToCaptureScreen,
'unexpected alert open': UnexpectedAlertOpen,
'unknown command': UnknownCommand,
'unknown error': UnknownError,
'unknown method': UnknownMethod,
'unsupported operation': UnsupportedOperation
};
/**
* unicode characters
* https://w3c.github.io/webdriver/webdriver-spec.html#character-types
*/
var UNICODE_CHARACTERS = {
'NULL': '\uE000',
'Unidentified': '\uE000',
'Cancel': '\uE001',
'Help': '\uE002',
'Back space': '\uE003',
'Backspace': '\uE003',
'Tab': '\uE004',
'Clear': '\uE005',
'Return': '\uE006',
'Enter': '\uE007',
'Shift': '\uE008',
'Control': '\uE009',
'Alt': '\uE00A',
'Pause': '\uE00B',
'Escape': '\uE00C',
'Space': '\uE00D',
' ': '\uE00D',
'Pageup': '\uE00E',
'PageUp': '\uE00E',
'Page_Up': '\uE00E',
'Pagedown': '\uE00F',
'PageDown': '\uE00F',
'Page_Down': '\uE00F',
'End': '\uE010',
'Home': '\uE011',
'Left arrow': '\uE012',
'Arrow_Left': '\uE012',
'ArrowLeft': '\uE012',
'Up arrow': '\uE013',
'Arrow_Up': '\uE013',
'ArrowUp': '\uE013',
'Right arrow': '\uE014',
'Arrow_Right': '\uE014',
'ArrowRight': '\uE014',
'Down arrow': '\uE015',
'Arrow_Down': '\uE015',
'ArrowDown': '\uE015',
'Insert': '\uE016',
'Delete': '\uE017',
'Semicolon': '\uE018',
'Equals': '\uE019',
'Numpad 0': '\uE01A',
'Numpad 1': '\uE01B',
'Numpad 2': '\uE01C',
'Numpad 3': '\uE01D',
'Numpad 4': '\uE01E',
'Numpad 5': '\uE01F',
'Numpad 6': '\uE020',
'Numpad 7': '\uE021',
'Numpad 8': '\uE022',
'Numpad 9': '\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',
'Zenkaku_Hankaku': '\uE040',
'ZenkakuHankaku': '\uE040'
};
exports.COLORS = COLORS;
exports.ERROR_CODES = ERROR_CODES;
exports.UNICODE_CHARACTERS = UNICODE_CHARACTERS;

View File

@ -0,0 +1,10 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = depcrecate;
function depcrecate(commandName) {
console.warn('WARNING: the "' + commandName + '" command will be depcrecated soon. Please use a ' + 'different command in order to avoid failures in your test after updating WebdriverIO.');
}
module.exports = exports['default'];

View File

@ -0,0 +1,67 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var DEFAULT_HOST = '127.0.0.1';
var DEFAULT_PORT = 4444;
/**
* helper to detect the Selenium backend according to given capabilities
*/
var detectSeleniumBackend = function detectSeleniumBackend(capabilities) {
/**
* don't detect anything if host or port is given
*/
if (capabilities.host || capabilities.port) {
return {
host: capabilities.host || DEFAULT_HOST,
port: capabilities.port || DEFAULT_PORT
};
}
/**
* local Selenium server
*/
if (!capabilities.user || !capabilities.key) {
return {
host: DEFAULT_HOST,
port: DEFAULT_PORT
};
}
/**
* browserstack
* e.g. zHcv9sZ39ip8ZPsxBVJ2
*/
if (capabilities.key.length === 20) {
return {
host: 'hub.browserstack.com',
port: 80
};
}
/**
* testingbot
* e.g. ec337d7b677720a4dde7bd72be0bfc67
*/
if (capabilities.key.length === 32) {
return {
host: 'hub.testingbot.com',
port: 80
};
}
/**
* Sauce Labs
* e.g. 50aa152c-1932-B2f0-9707-18z46q2n1mb0
*/
return {
protocol: 'https',
host: 'ondemand.saucelabs.com',
port: 443
};
};
exports.default = detectSeleniumBackend;
module.exports = exports['default'];

View File

@ -0,0 +1,41 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var WGXPATH_PATH = require.resolve('wgxpath');
var wgxpathSrc = void 0;
/**
* Ensures document.evaluate() in the browser.
*/
var ensureClientSideSelectorSupport = function ensureClientSideSelectorSupport() {
var _this = this;
return this.execute('return !!document.evaluate;').then(function (res) {
if (res.value) {
return true;
}
/**
* Don't read in unless necessary
*/
if (!wgxpathSrc) {
wgxpathSrc = _fs2.default.readFileSync(WGXPATH_PATH);
wgxpathSrc = wgxpathSrc.toString().split('module.exports')[0];
}
return _this.execute(wgxpathSrc + '\nwgxpath.install(window);');
});
};
exports.default = ensureClientSideSelectorSupport;
module.exports = exports['default'];

View File

@ -0,0 +1,114 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _q = require('q');
var _q2 = _interopRequireDefault(_q);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = [
/**
* stale reference error handler
*/
function (e) {
if (!e.seleniumStack || e.seleniumStack.type !== 'StaleElementReference') {
return;
}
/**
* get through command list and find most recent command where an element(s)
* command contained the failing json web element
*/
var failingCommand = this.commandList.slice(-1)[0];
var commandToRepeat = void 0;
for (var i = this.commandList.length - 1; i >= 0; --i) {
var command = this.commandList[i];
if (!command.result || !command.result.value) {
continue;
}
if (command.name !== 'element' && command.name !== 'elements') {
continue;
}
if (command.name === 'element' && command.result.value.ELEMENT !== failingCommand.args[0]) {
continue;
}
// Ensure an array when evaluating the result, so the logic is the same for 'element' and 'elements' commands
var results = Array.isArray(command.result.value) ? command.result.value : command.result.value !== null ? [command.result.value] : [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(results), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var result = _step.value;
if (result.ELEMENT === failingCommand.args[0]) {
commandToRepeat = this.commandList[i - 1];
/**
* when using elements as first citizen , e.g. div.getTagName() and rerun
* the command due to StaleElementReference exception we store the unshifted
* `null` as selector. In order to have a valid selector when rerunning it we
* have to put in the actual selector
*/
var preSelector = this.commandList[i].result.selector;
if (commandToRepeat.args[0] === null && typeof preSelector === 'string') {
commandToRepeat.args[0] = preSelector;
/**
* clear lastResult as we inject the actual selector to parameter list
*/
if (this.lastResult) {
delete this.lastResult.value;
}
}
break;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
if (commandToRepeat) {
break;
}
}
if (!commandToRepeat) {
return;
}
/**
* reset lastPromise so we can resolve it after rerun
*/
this.lastPromise = (0, _q2.default)();
return this[commandToRepeat.name].apply(this, commandToRepeat.args);
}];
module.exports = exports['default'];

View File

@ -0,0 +1,151 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var DEFAULT_SELECTOR = 'css selector';
var DIRECT_SELECTOR_REGEXP = /^(id|css selector|xpath|link text|partial link text|name|tag name|class name|-android uiautomator|-ios uiautomation|accessibility id):(.+)/;
var findStrategy = function findStrategy() {
var value = arguments.length <= 0 ? undefined : arguments[0];
var relative = arguments.length > 1 ? arguments.length <= 1 ? undefined : arguments[1] : false;
var xpathPrefix = relative ? './/' : '//';
/**
* set default selector
*/
var using = DEFAULT_SELECTOR;
if (typeof value !== 'string') {
throw new _ErrorHandler.ProtocolError('selector needs to be typeof `string`');
}
if (arguments.length === 3) {
return {
using: arguments.length <= 0 ? undefined : arguments[0],
value: arguments.length <= 1 ? undefined : arguments[1]
};
}
/**
* check if user has specified locator strategy directly
*/
var match = value.match(DIRECT_SELECTOR_REGEXP);
if (match) {
return {
using: match[1],
value: match[2]
};
}
// check value type
// use id strategy if value starts with # and doesnt contain any other CSS selector-relevant character
// regex to match ids from http://stackoverflow.com/questions/18938390/regex-to-match-ids-in-a-css-file
if (value.search(/^#-?[_a-zA-Z]+[_a-zA-Z0-9-]*$/) > -1) {
using = 'id';
value = value.slice(1);
// use xPath strategy if value starts with //
} else if (value.indexOf('/') === 0 || value.indexOf('(') === 0 || value.indexOf('../') === 0 || value.indexOf('./') === 0 || value.indexOf('*/') === 0) {
using = 'xpath';
// use link text startegy if value startes with =
} else if (value.indexOf('=') === 0) {
using = 'link text';
value = value.slice(1);
// use partial link text startegy if value startes with *=
} else if (value.indexOf('*=') === 0) {
using = 'partial link text';
value = value.slice(2);
// recursive element search using the UiAutomator library (Android only)
} else if (value.indexOf('android=') === 0) {
using = '-android uiautomator';
value = value.slice(8);
// recursive element search using the UIAutomation library (iOS-only)
} else if (value.indexOf('ios=') === 0) {
using = '-ios uiautomation';
value = value.slice(4);
// recursive element search using accessibility id
} else if (value.indexOf('~') === 0) {
using = 'accessibility id';
value = value.slice(1);
// class name mobile selector
// for iOS = UIA...
// for Android = android.widget
} else if (value.slice(0, 3) === 'UIA' || value.slice(0, 15) === 'XCUIElementType' || value.slice(0, 14).toLowerCase() === 'android.widget') {
using = 'class name';
// use tag name strategy if value contains a tag
// e.g. "<div>" or "<div />"
} else if (value.search(/<[a-zA-Z-]+( \/)*>/g) >= 0) {
using = 'tag name';
value = value.replace(/<|>|\/|\s/g, '');
// use name strategy if value queries elements with name attributes
// e.g. "[name='myName']" or '[name="myName"]'
} else if (value.search(/^\[name=("|')([a-zA-z0-9\-_. ]+)("|')]$/) >= 0) {
using = 'name';
value = value.match(/^\[name=("|')([a-zA-z0-9\-_. ]+)("|')]$/)[2];
// any element with given text e.g. h1=Welcome
} else if (value.search(/^[a-z0-9]*=(.)+$/) >= 0) {
var query = value.split(/=/);
var tag = query.shift();
using = 'xpath';
value = '' + xpathPrefix + (tag.length ? tag : '*') + '[normalize-space() = "' + query.join('=') + '"]';
// any element containing given text
} else if (value.search(/^[a-z0-9]*\*=(.)+$/) >= 0) {
var _query = value.split(/\*=/);
var _tag = _query.shift();
using = 'xpath';
value = '' + xpathPrefix + (_tag.length ? _tag : '*') + '[contains(., "' + _query.join('*=') + '")]';
// any element with certian class or id + given content
} else if (value.search(/^[a-z0-9]*(\.|#)-?[_a-zA-Z]+[_a-zA-Z0-9-]*=(.)+$/) >= 0) {
var _query2 = value.split(/=/);
var _tag2 = _query2.shift();
var classOrId = _tag2.substr(_tag2.search(/(\.|#)/), 1) === '#' ? 'id' : 'class';
var classOrIdName = _tag2.slice(_tag2.search(/(\.|#)/) + 1);
_tag2 = _tag2.substr(0, _tag2.search(/(\.|#)/));
using = 'xpath';
value = '' + xpathPrefix + (_tag2.length ? _tag2 : '*') + '[contains(@' + classOrId + ', "' + classOrIdName + '") and normalize-space() = "' + _query2.join('=') + '"]';
// any element with certian class or id + has certain content
} else if (value.search(/^[a-z0-9]*(\.|#)-?[_a-zA-Z]+[_a-zA-Z0-9-]*\*=(.)+$/) >= 0) {
var _query3 = value.split(/\*=/);
var _tag3 = _query3.shift();
var _classOrId = _tag3.substr(_tag3.search(/(\.|#)/), 1) === '#' ? 'id' : 'class';
var _classOrIdName = _tag3.slice(_tag3.search(/(\.|#)/) + 1);
_tag3 = _tag3.substr(0, _tag3.search(/(\.|#)/));
using = 'xpath';
value = xpathPrefix + (_tag3.length ? _tag3 : '*') + '[contains(@' + _classOrId + ', "' + _classOrIdName + '") and contains(., "' + _query3.join('*=') + '")]';
value = '' + xpathPrefix + (_tag3.length ? _tag3 : '*') + '[contains(@' + _classOrId + ', "' + _classOrIdName + '") and contains(., "' + _query3.join('*=') + '")]';
// allow to move up to the parent or select current element
} else if (value === '..' || value === '.') {
using = 'xpath';
}
return {
using: using,
value: value
};
};
exports.default = findStrategy;
module.exports = exports['default'];

View File

@ -0,0 +1,95 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var COMMAND_TYPES = ['protocol', 'commands'];
/**
* helper to find all implemented commands
*
* @return {String[]} list of implemented command names
*/
var getImplementedCommands = function getImplementedCommands() {
var commands = {};
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(COMMAND_TYPES), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var commandType = _step.value;
var dir = _path2.default.join(__dirname, '..', commandType);
var files = _fs2.default.readdirSync(dir);
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = (0, _getIterator3.default)(files), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var filename = _step2.value;
var commandName = filename.slice(0, -3);
/**
* addCommand only there for documentation purposes
*/
if (commandName === 'addCommand') {
continue;
}
commands[commandName] = require(_path2.default.join(dir, commandName));
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return commands;
};
exports.default = getImplementedCommands;
module.exports = exports['default'];

View File

@ -0,0 +1,51 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
/**
* call must be scoped to the webdriverio client
*/
var handleMouseButtonCommand = function handleMouseButtonCommand(selector, button, xoffset, yoffset) {
var _this = this;
/**
* mobile only supports simple clicks
*/
if (this.isMobile) {
return this.click(selector);
}
/**
* just press button if no selector is given
*/
if (selector === undefined) {
return this.buttonPress(button);
}
return this.element(selector).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
/**
* simulate event in safari
*/
if (_this.desiredCapabilities.browserName === 'safari') {
return _this.moveTo(res.value.ELEMENT, xoffset, yoffset).execute(function (elem, x, y, button) {
return window._wdio_simulate(elem, 'mousedown', 0, 0, button) && window._wdio_simulate(elem, 'mouseup', 0, 0, button);
}, res.value, xoffset, yoffset, button);
}
return _this.moveTo(res.value.ELEMENT, xoffset, yoffset).buttonPress(button);
});
};
exports.default = handleMouseButtonCommand;
module.exports = exports['default'];

View File

@ -0,0 +1,24 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var BUTTON_ENUM = {
left: 0,
middle: 1,
right: 2
};
/**
* call must be scoped to the webdriverio client
*/
var handleMouseButtonProtocol = function handleMouseButtonProtocol(requestPath, button) {
if (typeof button !== 'number') {
button = BUTTON_ENUM[button || 'left'];
}
return this.requestHandler.create(requestPath, { button: button });
};
exports.default = handleMouseButtonProtocol;
module.exports = exports['default'];

View File

@ -0,0 +1,35 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* check if selenium response contains an element result
* @param {object} result response object from the driver
* @return {Boolean} returns
* 0 if response was not an element result
* 1 if response was a element result
* 2 if response was an elements result
*/
function hasElementResult(result) {
/**
* check for element call
*/
if (result && (result.value && result.value.ELEMENT || typeof result.selector === 'string' && result.value === null)) {
return 1;
}
/**
* check for elements call
*/
if (result && Array.isArray(result.value) && result.value.filter(function (r) {
return !r.ELEMENT;
}).length === 0) {
return 2;
}
return 0;
}
exports.default = hasElementResult;
module.exports = exports['default'];

View File

@ -0,0 +1,25 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* check if current platform is mobile device
*
* @param {Object} caps capabilities
* @return {Boolean} true if platform is mobile device
*/
var mobileDetector = function mobileDetector(caps) {
var isMobile = !!(typeof caps['appium-version'] !== 'undefined' || typeof caps['device-type'] !== 'undefined' || typeof caps['deviceType'] !== 'undefined' || typeof caps['device-orientation'] !== 'undefined' || typeof caps['deviceOrientation'] !== 'undefined' || typeof caps.deviceName !== 'undefined' ||
// Check browserName for specific values
caps.browserName === '' || caps.browserName !== undefined && (caps.browserName.toLowerCase() === 'ipad' || caps.browserName.toLowerCase() === 'iphone' || caps.browserName.toLowerCase() === 'android'));
var isIOS = !!(caps.platformName && caps.platformName.match(/iOS/i) || caps.deviceName && caps.deviceName.match(/(iPad|iPhone)/i));
var isAndroid = !!(caps.platformName && caps.platformName.match(/Android/i) || caps.browserName && caps.browserName.match(/Android/i));
return { isMobile: isMobile, isIOS: isIOS, isAndroid: isAndroid };
};
exports.default = mobileDetector;
module.exports = exports['default'];

View File

@ -0,0 +1,116 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _cssValue = require('css-value');
var _cssValue2 = _interopRequireDefault(_cssValue);
var _rgb2hex = require('rgb2hex');
var _rgb2hex2 = _interopRequireDefault(_rgb2hex);
var _sanitize = require('./sanitize');
var _sanitize2 = _interopRequireDefault(_sanitize);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var parse = function parse(cssPropertyValue, cssProperty) {
if (!cssPropertyValue || !cssPropertyValue.value) {
return null;
}
var parsedValue = {
property: cssProperty,
value: cssPropertyValue.value.toLowerCase().trim()
};
if (parsedValue.value.indexOf('rgb') === 0) {
/**
* remove whitespaces in rgb values
*/
parsedValue.value = parsedValue.value.replace(/\s/g, '');
/**
* parse color values
*/
var color = parsedValue.value;
parsedValue.parsed = (0, _rgb2hex2.default)(parsedValue.value);
parsedValue.parsed.type = 'color';
parsedValue.parsed[/[rgba]+/g.exec(color)[0]] = color;
} else if (parsedValue.property === 'font-family') {
var font = (0, _cssValue2.default)(cssPropertyValue.value);
var string = parsedValue.value;
var value = cssPropertyValue.value.split(/,/).map(_sanitize2.default.css);
parsedValue.value = _sanitize2.default.css(font[0].value || font[0].string);
parsedValue.parsed = { value: value, type: 'font', string: string };
} else {
/**
* parse other css properties
*/
try {
parsedValue.parsed = (0, _cssValue2.default)(cssPropertyValue.value);
if (parsedValue.parsed.length === 1) {
parsedValue.parsed = parsedValue.parsed[0];
}
if (parsedValue.parsed.type && parsedValue.parsed.type === 'number' && parsedValue.parsed.unit === '') {
parsedValue.value = parsedValue.parsed.value;
}
} catch (e) {
// TODO improve css-parse lib to handle properties like
// `-webkit-animation-timing-function : cubic-bezier(0.25, 0.1, 0.25, 1)
}
}
return parsedValue;
};
var parseCSS = function parseCSS(response, cssProperty) {
var parsedCSS = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(response), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var res = _step.value;
parsedCSS.push(parse(res, cssProperty));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
if (parsedCSS.length === 1) {
return parsedCSS[0];
} else if (parsedCSS.length === 0) {
return null;
}
return parsedCSS;
};
exports.default = parseCSS;
module.exports = exports['default'];

View File

@ -0,0 +1,44 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* executes methods in try/catch block
*/
var safeExecute = function safeExecute(f, param) {
return function exec() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var result = void 0;
args = param || args;
if (typeof f !== 'function') {
return args[0];
}
/**
* we need to catch errors here as we would stop the
* execution and the promise (and the test) will never
* finish
*/
try {
result = f.apply(this, args);
} catch (e) {
var error = e;
if (e instanceof Error === false) {
error = new Error(e);
}
return error;
}
return result;
};
};
exports.default = safeExecute;
module.exports = exports['default'];

View File

@ -0,0 +1,148 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _stringify = require('babel-runtime/core-js/json/stringify');
var _stringify2 = _interopRequireDefault(_stringify);
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _jsonStringifySafe = require('json-stringify-safe');
var _jsonStringifySafe2 = _interopRequireDefault(_jsonStringifySafe);
var _validator = require('validator');
var _validator2 = _interopRequireDefault(_validator);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var OBJLENGTH = 10;
var ARRLENGTH = 10;
var STRINGLIMIT = 1000;
var STRINGTRUNCATE = 200;
var sanitizeString = function sanitizeString(str) {
if (!str) {
return '';
}
return String(str).replace(/^.*\/([^/]+)\/?$/, '$1').replace(/\./g, '_').replace(/\s/g, '').toLowerCase();
};
/**
* formats capability object into sanitized string for e.g.filenames
* @param {Object} caps Selenium capabilities
*/
var caps = function caps(_caps) {
if (!_caps) {
return '';
}
var result = void 0;
/**
* mobile caps
*/
if (_caps.deviceName) {
result = [sanitizeString(_caps.deviceName), sanitizeString(_caps.platformName), sanitizeString(_caps.platformVersion), sanitizeString(_caps.app)];
} else {
result = [sanitizeString(_caps.browserName), sanitizeString(_caps.version), sanitizeString(_caps.platform), sanitizeString(_caps.app)];
}
result = result.filter(function (n) {
return n !== undefined && n !== '';
});
return result.join('.');
};
/**
* formats arguments into string
* @param {Array} args arguments object
*/
var args = function args(_args) {
if (!_args || !Array.isArray(_args)) {
return '';
}
return _args.map(function (arg) {
if (typeof arg === 'function' || typeof arg === 'string' && arg.indexOf('return (function') === 0) {
return '<Function>';
} else if (typeof arg === 'string') {
return '"' + arg + '"';
} else if (Array.isArray(arg)) {
return arg.join(', ');
}
return arg;
}).join(', ');
};
var css = function css(value) {
if (!value) {
return value;
}
return value.trim().replace(/'/g, '').replace(/"/g, '').toLowerCase();
};
/**
* Limit the length of an arbitrary variable of any type, suitable for being logged or displayed
* @param {Any} val Any variable
* @return {Any} Limited var of same type
*/
var limit = function limit(val) {
if (!val) return val;
// Ensure we're working with a copy
val = JSON.parse((0, _jsonStringifySafe2.default)(val));
switch (Object.prototype.toString.call(val)) {
case '[object String]':
if (val.length > 100 && _validator2.default.isBase64(val)) {
return '[base64] ' + val.length + ' bytes';
}
if (val.length > STRINGLIMIT) {
return val.substr(0, STRINGTRUNCATE) + (' ... (' + (val.length - STRINGTRUNCATE) + ' more bytes)');
}
return val;
case '[object Array]':
var length = val.length;
if (length > ARRLENGTH) {
val = val.slice(0, ARRLENGTH);
val.push('(' + (length - ARRLENGTH) + ' more items)');
}
return val.map(limit);
case '[object Object]':
var keys = (0, _keys2.default)(val);
var removed = [];
for (var i = 0, l = keys.length; i < l; i++) {
if (i < OBJLENGTH) {
val[keys[i]] = limit(val[keys[i]]);
} else {
delete val[keys[i]];
removed.push(keys[i]);
}
}
if (removed.length) {
val._ = keys.length - OBJLENGTH + ' more keys: ' + (0, _stringify2.default)(removed);
}
return val;
}
return val;
};
exports.default = {
css: css,
args: args,
caps: caps,
limit: limit
};
module.exports = exports['default'];

View File

@ -0,0 +1,321 @@
exports.config = {
<% if(answers.host && answers.port) { %>//
// =====================
// Server Configurations
// =====================
// Host address of the running Selenium server. This information is usually obsolete as
// WebdriverIO automatically connects to localhost. Also, if you are using one of the
// supported cloud services like Sauce Labs, Browserstack, or Testing Bot you don't
// need to define host and port information because WebdriverIO can figure that out
// according to your user and key information. However, if you are using a private Selenium
// backend you should define the host address, port, and path here.
//
host: '<%= answers.host %>',
port: <%= answers.port %>,
path: '<%= answers.path %>',
<% }
if(answers.env_user && answers.env_key) { %>
//
// =================
// Service Providers
// =================
// WebdriverIO supports Sauce Labs, Browserstack, and Testing Bot (other cloud providers
// should work too though). These services define specific user and key (or access key)
// values you need to put in here in order to connect to these services.
//
user: process.env.<%= answers.env_user %>,
key: process.env.<%= answers.env_key %>,
<% }
if(answers.backend.indexOf('In the cloud') > -1) { %>
<% } %>
//
// ==================
// Specify Test Files
// ==================
// Define which test specs should run. The pattern is relative to the directory
// from which `wdio` was called. Notice that, if you are calling `wdio` from an
// NPM script (see https://docs.npmjs.com/cli/run-script) then the current working
// directory is where your package.json resides, so `wdio` will be called from there.
//
specs: [
'<%= answers.specs %>'
],
// Patterns to exclude.
exclude: [
// 'path/to/excluded/files'
],
//
// ============
// Capabilities
// ============
// Define your capabilities here. WebdriverIO can run multiple capabilities at the same
// time. Depending on the number of capabilities, WebdriverIO launches several test
// sessions. Within your capabilities you can overwrite the spec and exclude options in
// order to group specific specs to a specific capability.
//
// First, you can define how many instances should be started at the same time. Let's
// say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have
// set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec
// files and you set maxInstances to 10, all spec files will get tested at the same time
// and 30 processes will get spawned. The property handles how many capabilities
// from the same test should run tests.
//
maxInstances: 10,
//
// If you have trouble getting all important capabilities together, check out the
// Sauce Labs platform configurator - a great tool to configure your capabilities:
// https://docs.saucelabs.com/reference/platforms-configurator
//
capabilities: [{
// maxInstances can get overwritten per capability. So if you have an in-house Selenium
// grid with only 5 firefox instances available you can make sure that not more than
// 5 instances get started at a time.
maxInstances: 5,
//
browserName: 'firefox'
}],
//
// ===================
// Test Configurations
// ===================
// Define all options that are relevant for the WebdriverIO instance here
//
// By default WebdriverIO commands are executed in a synchronous way using
// the wdio-sync package. If you still want to run your tests in an async way
// e.g. using promises you can set the sync option to false.
sync: true,
//
// Level of logging verbosity: silent | verbose | command | data | result | error
logLevel: '<%= answers.logLevel %>',
//
// Enables colors for log output.
coloredLogs: true,
//
// If you only want to run your tests until a specific amount of tests have failed use
// bail (default is 0 - don't bail, run all tests).
bail: 0,
//
// Saves a screenshot to a given path if a command fails.
screenshotPath: '<%= answers.screenshotPath %>',
//
// Set a base URL in order to shorten url command calls. If your url parameter starts
// with "/", then the base url gets prepended.
baseUrl: '<%= answers.baseUrl %>',
//
// Default timeout for all waitFor* commands.
waitforTimeout: 10000,
//
// Default timeout in milliseconds for request
// if Selenium Grid doesn't send response
connectionRetryTimeout: 90000,
//
// Default request retries count
connectionRetryCount: 3,
//
// Initialize the browser instance with a WebdriverIO plugin. The object should have the
// plugin name as key and the desired plugin options as properties. Make sure you have
// the plugin installed before running any tests. The following plugins are currently
// available:
// WebdriverCSS: https://github.com/webdriverio/webdrivercss
// WebdriverRTC: https://github.com/webdriverio/webdriverrtc
// Browserevent: https://github.com/webdriverio/browserevent
// plugins: {
// webdrivercss: {
// screenshotRoot: 'my-shots',
// failedComparisonsRoot: 'diffs',
// misMatchTolerance: 0.05,
// screenWidth: [320,480,640,1024]
// },
// webdriverrtc: {},
// browserevent: {}
// },
//
// Test runner services
// Services take over a specific job you don't want to take care of. They enhance
// your test setup with almost no effort. Unlike plugins, they don't add new
// commands. Instead, they hook themselves up into the test process.
<% if(answers.services.length) {
%>services: ['<%- answers.services.map(function(service) {
return service.slice(5, -8);
}).join("','") %>'],
<% } else {
%>// services: [],<% } %>//
// Framework you want to run your specs with.
// The following are supported: Mocha, Jasmine, and Cucumber
// see also: http://webdriver.io/guide/testrunner/frameworks.html
//
// Make sure you have the wdio adapter package for the specific framework installed
// before running any tests.
framework: '<%= answers.framework %>',
//
// Test reporter for stdout.
// The only one supported by default is 'dot'
// see also: http://webdriver.io/guide/testrunner/reporters.html
<% if(answers.reporters.length) {
%>reporters: ['<%- answers.reporters.map(function(reporter) {
return reporter.slice(5, -9);
}).join("','") %>'],
<% } else {
%>// reporters: ['dot'],<%
}
if(answers.outputDir) { %>//
// Some reporters require additional information which should get defined here
reporterOptions: {
//
// If you are using the "xunit" reporter you should define the directory where
// WebdriverIO should save all unit reports.
outputDir: './'
},
<% }
if(answers.framework === 'mocha') { %>
//
// Options to be passed to Mocha.
// See the full list at http://mochajs.org/
mochaOpts: {
ui: 'bdd'
},<% }
if(answers.framework === 'jasmine') { %>
//
// Options to be passed to Jasmine.
jasmineNodeOpts: {
//
// Jasmine default timeout
defaultTimeoutInterval: 10000,
//
// The Jasmine framework allows interception of each assertion in order to log the state of the application
// or website depending on the result. For example, it is pretty handy to take a screenshot every time
// an assertion fails.
expectationResultHandler: function(passed, assertion) {
// do something
}
},
<% }
if(answers.framework === 'cucumber') { %>//
// If you are using Cucumber you need to specify the location of your step definitions.
cucumberOpts: {
require: ['<%= answers.stepDefinitions %>'], // <string[]> (file/dir) require files before executing features
backtrace: false, // <boolean> show full backtrace for errors
compiler: [], // <string[]> ("extension:module") require files with the given EXTENSION after requiring MODULE (repeatable)
dryRun: false, // <boolean> invoke formatters without executing steps
failFast: false, // <boolean> abort the run on first failure
format: ['pretty'], // <string[]> (type[:path]) specify the output format, optionally supply PATH to redirect formatter output (repeatable)
colors: true, // <boolean> disable colors in formatter output
snippets: true, // <boolean> hide step definition snippets for pending steps
source: true, // <boolean> hide source uris
profile: [], // <string[]> (name) specify the profile to use
strict: false, // <boolean> fail if there are any undefined or pending steps
tags: [], // <string[]> (expression) only execute the features or scenarios with tags matching the expression
timeout: 20000, // <number> timeout for step definitions
ignoreUndefinedDefinitions: false, // <boolean> Enable this config to treat undefined definitions as warnings.
},
<% } %>
//
// =====
// Hooks
// =====
// WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance
// it and to build services around it. You can either apply a single function or an array of
// methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got
// resolved to continue.
/**
* Gets executed once before all workers get launched.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
*/
// onPrepare: function (config, capabilities) {
// },
/**
* Gets executed just before initialising the webdriver session and test framework. It allows you
* to manipulate configurations depending on the capability or spec.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that are to be run
*/
// beforeSession: function (config, capabilities, specs) {
// },
/**
* Gets executed before test execution begins. At this point you can access to all global
* variables like `browser`. It is the perfect place to define custom commands.
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that are to be run
*/
// before: function (capabilities, specs) {
// },
//
/**
* Hook that gets executed before the suite starts
* @param {Object} suite suite details
*/
// beforeSuite: function (suite) {
// },
/**
* Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling
* beforeEach in Mocha)
*/
// beforeHook: function () {
// },
/**
* Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling
* afterEach in Mocha)
*/
// afterHook: function () {
// },
/**
* Function to be executed before a test (in Mocha/Jasmine) or a step (in Cucumber) starts.
* @param {Object} test test details
*/
// beforeTest: function (test) {
// },
/**
* Runs before a WebdriverIO command gets executed.
* @param {String} commandName hook command name
* @param {Array} args arguments that command would receive
*/
// beforeCommand: function (commandName, args) {
// },
/**
* Runs after a WebdriverIO command gets executed
* @param {String} commandName hook command name
* @param {Array} args arguments that command would receive
* @param {Number} result 0 - command success, 1 - command error
* @param {Object} error error object if any
*/
// afterCommand: function (commandName, args, result, error) {
// },
/**
* Function to be executed after a test (in Mocha/Jasmine) or a step (in Cucumber) starts.
* @param {Object} test test details
*/
// afterTest: function (test) {
// },
/**
* Hook that gets executed after the suite has ended
* @param {Object} suite suite details
*/
// afterSuite: function (suite) {
// },
/**
* Gets executed after all tests are done. You still have access to all global variables from
* the test.
* @param {Number} result 0 - test pass, 1 - test fail
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that ran
*/
// after: function (result, capabilities, specs) {
// },
/**
* Gets executed right after terminating the webdriver session.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that ran
*/
// afterSession: function (config, capabilities, specs) {
// },
/**
* Gets executed after all workers got shut down and the process is about to exit. It is not
* possible to defer the end of the process using a promise.
* @param {Object} exitCode 0 - success, 1 - fail
*/
// onComplete: function(exitCode) {
// }
}

View File

@ -0,0 +1,775 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
var _regenerator = require('babel-runtime/regenerator');
var _regenerator2 = _interopRequireDefault(_regenerator);
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
var _assign = require('babel-runtime/core-js/object/assign');
var _assign2 = _interopRequireDefault(_assign);
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _child_process = require('child_process');
var _child_process2 = _interopRequireDefault(_child_process);
var _ConfigParser = require('./utils/ConfigParser');
var _ConfigParser2 = _interopRequireDefault(_ConfigParser);
var _BaseReporter = require('./utils/BaseReporter');
var _BaseReporter2 = _interopRequireDefault(_BaseReporter);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var Launcher = function () {
function Launcher(configFile, argv) {
(0, _classCallCheck3.default)(this, Launcher);
this.configParser = new _ConfigParser2.default();
this.configParser.addConfigFile(configFile);
this.configParser.merge(argv);
this.reporters = this.initReporters();
this.argv = argv;
this.configFile = configFile;
this.exitCode = 0;
this.hasTriggeredExitRoutine = false;
this.hasStartedAnyProcess = false;
this.processes = [];
this.schedule = [];
this.rid = [];
this.processesStarted = 0;
this.runnerFailed = 0;
}
/**
* check if multiremote or wdio test
*/
(0, _createClass3.default)(Launcher, [{
key: 'isMultiremote',
value: function isMultiremote() {
var caps = this.configParser.getCapabilities();
return !Array.isArray(caps);
}
/**
* initialise reporters
*/
}, {
key: 'initReporters',
value: function initReporters() {
var reporter = new _BaseReporter2.default();
var config = this.configParser.getConfig();
/**
* if no reporter is set or config property is in a wrong format
* just use the dot reporter
*/
if (!config.reporters || !Array.isArray(config.reporters) || !config.reporters.length) {
config.reporters = ['dot'];
}
var reporters = {};
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(config.reporters), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var reporterName = _step.value;
var Reporter = void 0;
if (typeof reporterName === 'function') {
Reporter = reporterName;
if (!Reporter.reporterName) {
throw new Error('Custom reporters must export a unique \'reporterName\' property');
}
reporters[Reporter.reporterName] = Reporter;
} else if (typeof reporterName === 'string') {
try {
Reporter = require('wdio-' + reporterName + '-reporter');
} catch (e) {
throw new Error('reporter "wdio-' + reporterName + '-reporter" is not installed. Error: ' + e.stack);
}
reporters[reporterName] = Reporter;
}
if (!Reporter) {
throw new Error('config.reporters must be an array of strings or functions, but got \'' + typeof reporterName + '\': ' + reporterName);
}
}
/**
* if no reporter options are set or property is in a wrong format default to
* empty object
*/
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
if (!config.reporterOptions || typeof config.reporterOptions !== 'object') {
config.reporterOptions = {};
}
for (var _reporterName in reporters) {
var Reporter = reporters[_reporterName];
var reporterOptions = {};
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = (0, _getIterator3.default)((0, _keys2.default)(config.reporterOptions)), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var option = _step2.value;
if (option === _reporterName && typeof config.reporterOptions[_reporterName] === 'object') {
// Copy over options specifically for this reporter type
reporterOptions = (0, _assign2.default)(reporterOptions, config.reporterOptions[_reporterName]);
} else if (reporters[option]) {
// Don't copy options for other reporters
continue;
} else {
// Copy over generic options
reporterOptions[option] = config.reporterOptions[option];
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
reporter.add(new Reporter(reporter, config, reporterOptions));
}
return reporter;
}
/**
* run sequence
* @return {Promise} that only gets resolves with either an exitCode or an error
*/
}, {
key: 'run',
value: function () {
var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee() {
var _this = this;
var config, caps, launcher, _exitCode, cid, _iteratorNormalCompletion3, _didIteratorError3, _iteratorError3, _iterator3, _step3, capabilities, exitCode;
return _regenerator2.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
config = this.configParser.getConfig();
caps = this.configParser.getCapabilities();
launcher = this.getLauncher(config);
this.reporters.handleEvent('start', {
isMultiremote: this.isMultiremote(),
capabilities: caps,
config: config
});
/**
* run onPrepare hook
*/
_context.next = 6;
return config.onPrepare(config, caps);
case 6:
_context.next = 8;
return this.runServiceHook(launcher, 'onPrepare', config, caps);
case 8:
if (!this.isMultiremote()) {
_context.next = 17;
break;
}
_context.next = 11;
return new _promise2.default(function (resolve) {
_this.resolve = resolve;
_this.startInstance(_this.configParser.getSpecs(), caps, 0);
});
case 11:
_exitCode = _context.sent;
_context.next = 14;
return this.runServiceHook(launcher, 'onComplete', _exitCode, config, caps);
case 14:
_context.next = 16;
return config.onComplete(_exitCode, config, caps);
case 16:
return _context.abrupt('return', _exitCode);
case 17:
/**
* schedule test runs
*/
cid = 0;
_iteratorNormalCompletion3 = true;
_didIteratorError3 = false;
_iteratorError3 = undefined;
_context.prev = 21;
for (_iterator3 = (0, _getIterator3.default)(caps); !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
capabilities = _step3.value;
this.schedule.push({
cid: cid++,
caps: capabilities,
specs: this.configParser.getSpecs(capabilities.specs, capabilities.exclude),
availableInstances: capabilities.maxInstances || config.maxInstancesPerCapability,
runningInstances: 0,
seleniumServer: { host: config.host, port: config.port, protocol: config.protocol }
});
}
/**
* catches ctrl+c event
*/
_context.next = 29;
break;
case 25:
_context.prev = 25;
_context.t0 = _context['catch'](21);
_didIteratorError3 = true;
_iteratorError3 = _context.t0;
case 29:
_context.prev = 29;
_context.prev = 30;
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
case 32:
_context.prev = 32;
if (!_didIteratorError3) {
_context.next = 35;
break;
}
throw _iteratorError3;
case 35:
return _context.finish(32);
case 36:
return _context.finish(29);
case 37:
process.on('SIGINT', this.exitHandler.bind(this));
/**
* make sure the program will not close instantly
*/
if (process.stdin.isPaused()) {
process.stdin.resume();
}
_context.next = 41;
return new _promise2.default(function (resolve) {
_this.resolve = resolve;
/**
* return immediatelly if no spec was run
*/
if (_this.runSpecs()) {
resolve(0);
}
});
case 41:
exitCode = _context.sent;
_context.next = 44;
return this.runServiceHook(launcher, 'onComplete', exitCode, config, caps);
case 44:
_context.next = 46;
return config.onComplete(exitCode, config, caps);
case 46:
return _context.abrupt('return', exitCode);
case 47:
case 'end':
return _context.stop();
}
}
}, _callee, this, [[21, 25, 29, 37], [30,, 32, 36]]);
}));
function run() {
return _ref.apply(this, arguments);
}
return run;
}()
/**
* run service launch sequences
*/
}, {
key: 'runServiceHook',
value: function () {
var _ref2 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee2(launcher, hookName) {
for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
return _regenerator2.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
_context2.prev = 0;
_context2.next = 3;
return _promise2.default.all(launcher.map(function (service) {
if (typeof service[hookName] === 'function') {
return service[hookName].apply(service, args);
}
}));
case 3:
return _context2.abrupt('return', _context2.sent);
case 6:
_context2.prev = 6;
_context2.t0 = _context2['catch'](0);
console.error('A service failed in the \'' + hookName + '\' hook\n' + _context2.t0.stack + '\n\nContinue...');
case 9:
case 'end':
return _context2.stop();
}
}
}, _callee2, this, [[0, 6]]);
}));
function runServiceHook(_x, _x2) {
return _ref2.apply(this, arguments);
}
return runServiceHook;
}()
/**
* run multiple single remote tests
* @return {Boolean} true if all specs have been run and all instances have finished
*/
}, {
key: 'runSpecs',
value: function runSpecs() {
var _this2 = this;
var config = this.configParser.getConfig();
/**
* stop spawning new processes when CTRL+C was triggered
*/
if (this.hasTriggeredExitRoutine) {
return true;
}
while (this.getNumberOfRunningInstances() < config.maxInstances) {
var schedulableCaps = this.schedule
/**
* bail if number of errors exceeds allowed
*/
.filter(function () {
var filter = typeof config.bail !== 'number' || config.bail < 1 || config.bail > _this2.runnerFailed;
/**
* clear number of specs when filter is false
*/
if (!filter) {
_this2.schedule.forEach(function (t) {
t.specs = [];
});
}
return filter;
})
/**
* make sure complete number of running instances is not higher than general maxInstances number
*/
.filter(function (a) {
return _this2.getNumberOfRunningInstances() < config.maxInstances;
})
/**
* make sure the capabiltiy has available capacities
*/
.filter(function (a) {
return a.availableInstances > 0;
})
/**
* make sure capabiltiy has still caps to run
*/
.filter(function (a) {
return a.specs.length > 0;
})
/**
* make sure we are running caps with less running instances first
*/
.sort(function (a, b) {
return a.runningInstances > b.runningInstances;
});
/**
* continue if no capabiltiy were schedulable
*/
if (schedulableCaps.length === 0) {
break;
}
this.startInstance([schedulableCaps[0].specs.shift()], schedulableCaps[0].caps, schedulableCaps[0].cid, schedulableCaps[0].seleniumServer);
schedulableCaps[0].availableInstances--;
schedulableCaps[0].runningInstances++;
}
return this.getNumberOfRunningInstances() === 0 && this.getNumberOfSpecsLeft() === 0;
}
/**
* gets number of all running instances
* @return {number} number of running instances
*/
}, {
key: 'getNumberOfRunningInstances',
value: function getNumberOfRunningInstances() {
return this.schedule.map(function (a) {
return a.runningInstances;
}).reduce(function (a, b) {
return a + b;
});
}
/**
* get number of total specs left to complete whole suites
* @return {number} specs left to complete suite
*/
}, {
key: 'getNumberOfSpecsLeft',
value: function getNumberOfSpecsLeft() {
return this.schedule.map(function (a) {
return a.specs.length;
}).reduce(function (a, b) {
return a + b;
});
}
/**
* Start instance in a child process.
* @param {Array} specs Specs to run
* @param {Number} cid Capabilities ID
*/
}, {
key: 'startInstance',
value: function startInstance(specs, caps, cid, server) {
var config = this.configParser.getConfig();
var debug = caps.debug || config.debug;
cid = this.getRunnerId(cid);
var processNumber = this.processesStarted + 1;
// process.debugPort defaults to 5858 and is set even when process
// is not being debugged.
var debugArgs = debug ? ['--debug=' + (process.debugPort + processNumber)] : [];
// if you would like to add --debug-brk, use a different port, etc...
var capExecArgs = [].concat((0, _toConsumableArray3.default)(config.execArgv || []), (0, _toConsumableArray3.default)(caps.execArgv || []));
// The default value for child.fork execArgs is process.execArgs,
// so continue to use this unless another value is specified in config.
var defaultArgs = capExecArgs.length ? process.execArgv : [];
// If an arg appears multiple times the last occurence is used
var execArgv = [].concat((0, _toConsumableArray3.default)(defaultArgs), debugArgs, (0, _toConsumableArray3.default)(capExecArgs));
var childProcess = _child_process2.default.fork(_path2.default.join(__dirname, '/runner.js'), process.argv.slice(2), {
cwd: process.cwd(),
execArgv: execArgv
});
this.processes.push(childProcess);
childProcess.on('message', this.messageHandler.bind(this, cid)).on('exit', this.endHandler.bind(this, cid));
childProcess.send({
cid: cid,
command: 'run',
configFile: this.configFile,
argv: this.argv,
caps: caps,
processNumber: processNumber,
specs: specs,
server: server,
isMultiremote: this.isMultiremote()
});
this.processesStarted++;
}
/**
* generates a runner id
* @param {Number} cid capability id (unique identifier for a capability)
* @return {String} runner id (combination of cid and test id e.g. 0a, 0b, 1a, 1b ...)
*/
}, {
key: 'getRunnerId',
value: function getRunnerId(cid) {
if (!this.rid[cid]) {
this.rid[cid] = 0;
}
return cid + '-' + this.rid[cid]++;
}
/**
* emit event from child process to reporter
* @param {String} cid
* @param {Object} m event object
*/
}, {
key: 'messageHandler',
value: function messageHandler(cid, m) {
this.hasStartedAnyProcess = true;
if (!m.cid) {
m.cid = cid;
}
if (m.event === 'runner:error') {
this.reporters.handleEvent('error', m);
}
this.reporters.handleEvent(m.event, m);
}
/**
* Close test runner process once all child processes have exited
* @param {Number} cid Capabilities ID
* @param {Number} childProcessExitCode exit code of child process
*/
}, {
key: 'endHandler',
value: function endHandler(cid, childProcessExitCode) {
this.exitCode = this.exitCode || childProcessExitCode;
this.runnerFailed += childProcessExitCode !== 0 ? 1 : 0;
// Update schedule now this process has ended
if (!this.isMultiremote()) {
// get cid (capability id) from rid (runner id)
cid = parseInt(cid, 10);
this.schedule[cid].availableInstances++;
this.schedule[cid].runningInstances--;
}
if (!this.isMultiremote() && !this.runSpecs()) {
return;
}
this.reporters.handleEvent('end', {
sigint: this.hasTriggeredExitRoutine,
exitCode: this.exitCode,
isMultiremote: this.isMultiremote(),
capabilities: this.configParser.getCapabilities(),
config: this.configParser.getConfig()
});
if (this.exitCode === 0) {
return this.resolve(this.exitCode);
}
/**
* finish with exit code 1
*/
return this.resolve(1);
}
/**
* Make sure all started selenium sessions get closed properly and prevent
* having dead driver processes. To do so let the runner end its Selenium
* session first before killing
*/
}, {
key: 'exitHandler',
value: function exitHandler() {
if (this.hasTriggeredExitRoutine || !this.hasStartedAnyProcess) {
console.log('\nKilling process, bye!');
// When spawned as a subprocess,
// SIGINT will not be forwarded to childs.
// Thus for the child to exit cleanly, we must force send SIGINT
if (!process.stdin.isTTY) {
this.processes.forEach(function (p) {
return p.kill('SIGINT');
});
}
/**
* finish with exit code 1
*/
return this.resolve(1);
}
// When spawned as a subprocess,
// SIGINT will not be forwarded to childs.
// Thus for the child to exit cleanly, we must force send SIGINT
if (!process.stdin.isTTY) {
this.processes.forEach(function (p) {
return p.kill('SIGINT');
});
}
console.log('\n\nEnd selenium sessions properly ...\n(press ctrl+c again to hard kill the runner)\n');
this.hasTriggeredExitRoutine = true;
}
/**
* loads launch services
*/
}, {
key: 'getLauncher',
value: function getLauncher(config) {
var launchServices = [];
if (!Array.isArray(config.services)) {
return launchServices;
}
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = (0, _getIterator3.default)(config.services), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var serviceName = _step4.value;
var service = void 0;
/**
* allow custom services
*/
if (typeof serviceName === 'object') {
launchServices.push(serviceName);
continue;
}
try {
service = require('wdio-' + serviceName + '-service/launcher');
} catch (e) {
if (!e.message.match('Cannot find module \'wdio-' + serviceName + '-service/launcher\'')) {
throw new Error('Couldn\'t initialise launcher from service "' + serviceName + '".\n' + e.stack);
}
}
if (service && typeof service.onPrepare === 'function') {
launchServices.push(service);
}
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
return launchServices;
}
}]);
return Launcher;
}();
exports.default = Launcher;
module.exports = exports['default'];

View File

@ -0,0 +1,281 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _q = require('q');
var _q2 = _interopRequireDefault(_q);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Multibrowser
*/
var Multibrowser = function () {
function Multibrowser() {
(0, _classCallCheck3.default)(this, Multibrowser);
this.instances = {};
this.promiseBucket = [];
var defer = _q2.default.defer();
this.qResolve = defer.resolve.toString();
}
/**
* add instance to multibrowser instance
*/
(0, _createClass3.default)(Multibrowser, [{
key: 'addInstance',
value: function addInstance(browserName, client) {
if (this.instances[browserName]) {
throw new Error('webdriver instance "' + browserName + '" is already defined');
}
this.instances[browserName] = client;
}
/**
* modifier for multibrowser instance
*/
}, {
key: 'getModifier',
value: function getModifier() {
return multiremoteModifier.bind(this);
}
/**
* flush bucket and return current pending promises
*/
}, {
key: 'flushPromiseBucket',
value: function flushPromiseBucket() {
var bucket = this.promiseBucket.filter(function (promise) {
return promise.inspect().state === 'pending';
});
this.promiseBucket = [];
return bucket;
}
/**
* modifier for single webdriverio instances
*/
}, {
key: 'getInstanceModifier',
value: function getInstanceModifier() {
return instanceModifier.bind(this);
}
}]);
return Multibrowser;
}();
function instanceModifier(client) {
var _next = client.next;
var multibrowser = this;
/**
* Overwrite next (bind) method to put each command into a bucket.
* This provides us useful information about all current running
* commands.
*/
client.next = function () {
multibrowser.promiseBucket.push(this.promise);
return _next.apply(this, arguments);
};
return client;
}
function multiremoteModifier(client) {
var multibrowser = this;
var browserNames = (0, _keys2.default)(multibrowser.instances);
client.getInstances = function () {
return browserNames;
};
client.next = function () {
var _this = this;
var promises = [];
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var fnName = args.pop();
/**
* no need for actual function here
*/
args.shift();
/**
* flush promise bucket
*/
multibrowser.promiseBucket = [];
var commandArgs = args.pop();
return this.lastPromise.done(function () {
browserNames.forEach(function (browserName) {
var instance = multibrowser.instances[browserName];
promises.push(instance[fnName].apply(instance, commandArgs).promise);
});
return _promise2.default.all(promises).then(function (result) {
/**
* custom event handling since multibrowser instance
* actually never executes any command
*/
var payload = {
fnName: fnName
};
for (var i = 0; i < browserNames.length; ++i) {
payload[browserNames[i]] = result[i];
}
if (fnName.match(/(init|end)/)) {
_this.emit(fnName, payload);
}
_this.emit('result', payload);
_this.defer.resolve(result);
}, function (err) {
_this.emit('error', err);
_this.defer.reject(err);
});
});
};
var _then = client.then;
client.then = function (onFulfilled, onRejected) {
/**
* curry arguments
* as multibrowser commands return with an array of results for each instance
* respectively (see q.all) we need to curry them (expand arguments) to help
* users to better work with the results
*
* uncurried original version:
* ```js
* matrix.getTitle().then(function (result) {
* expect(result[0]).to.be.equal('title of browser A')
* expect(result[1]).to.be.equal('title of browser B')
* })
* ```
*
* curried version:
* ```js
* matrix.getTitle().then(function (result) {
* expect(result.browserA).to.be.equal('title of browser A')
* expect(result.browserB).to.be.equal('title of browser B')
* })
* ```
*/
var curryArguments = function curryArguments(args) {
/**
* when returning with a promise within a multibrowser promise like
*
* ```js
* matrix.url(...).getTitle().then(function () {
* return matrix.getSource()
* })
* ```
*
* we will have problems as we are running through curryArguments twice.
* Therefor check if the onFulFilled handler is from the Q library and
* handle that promise as usual here.
* It's an ugly hack but the only way to get around with this and having
* nice curried arguments.
*
*/
if (onFulfilled.toString() === multibrowser.qResolve) {
return onFulfilled.apply(this, arguments);
}
if (arguments.length === 1 && !Array.isArray(args)) {
return onFulfilled.call(this, args);
}
if (arguments.length > 1) {
args = Array.prototype.slice.call(arguments);
}
var result = {};
for (var i = 0; i < browserNames.length; ++i) {
result[browserNames[i]] = args[i];
}
return onFulfilled.call(this, result);
};
if (!onFulfilled && !onRejected) {
return this;
}
if (onFulfilled && !onRejected) {
return _then.call(this, curryArguments);
}
if (onFulfilled && onRejected) {
return _then.call(this, curryArguments, onRejected);
}
return _then.call(this, undefined, onRejected);
};
client.select = function (browserName) {
var instance = multibrowser.instances[browserName];
if (!instance) {
throw new Error('browser name "' + browserName + '" was not defined');
}
instance.isMultibrowser = false;
return instance;
};
client.sync = function () {
var bucket = multibrowser.flushPromiseBucket();
return this.call(function () {
return _promise2.default.all(bucket);
});
};
var _addCommand = client.addCommand;
client.addCommand = function (fnName, fn, forceOverwrite) {
var _this2 = this;
var args = arguments;
_addCommand.apply(this, args);
(0, _keys2.default)(multibrowser.instances).forEach(function (browserName) {
var instance = multibrowser.instances[browserName];
instance.addCommand.apply(_this2, args);
});
return this;
};
return client;
}
exports.default = Multibrowser;
module.exports = exports['default'];

View File

@ -0,0 +1,49 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = alertAccept;
/**
*
* Accepts the currently displayed alert dialog. Usually, this is equivalent to
* clicking on the 'OK' button in the dialog.
*
* <example>
:alertAccept.js
it('demonstrate the alertAccept command', function () {
if (browser.alertText()) {
browser.alertAccept();
}
// ...
});
* </example>
*
* @throws {RuntimeError} If no alert is present. The seleniumStack.type parameter will equal 'NoAlertOpenError'.
*
* @see https://w3c.github.io/webdriver/webdriver-spec.html#accept-alert
* @type protocol
*
*/
function alertAccept() {
var _this = this;
var requestOptions = {
path: '/session/:sessionId/accept_alert',
method: 'POST'
};
return this.requestHandler.create(requestOptions).catch(function (err) {
/**
* jsonwire command not supported try webdriver endpoint
*/
if (err.message.match(/did not match a known command/)) {
requestOptions.path = '/session/:sessionId/alert/accept';
return _this.requestHandler.create(requestOptions);
}
throw err;
});
}
module.exports = exports['default'];

View File

@ -0,0 +1,50 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = alertDismiss;
/**
*
* Dismisses the currently displayed alert dialog. For confirm() and prompt()
* dialogs, this is equivalent to clicking the 'Cancel' button. For alert()
* dialogs, this is equivalent to clicking the 'OK' button.
*
* <example>
:alertAccept.js
it('demonstrate the alertDismiss command', function () {
if (browser.alertText()) {
browser.alertDismiss();
}
// ...
});
* </example>
*
* @throws {RuntimeError} If no alert is present. The seleniumStack.type parameter will equal 'NoAlertOpenError'.
*
* @see https://w3c.github.io/webdriver/webdriver-spec.html#dismiss-alert
* @type protocol
*
*/
function alertDismiss() {
var _this = this;
var requestOptions = {
path: '/session/:sessionId/dismiss_alert',
method: 'POST'
};
return this.requestHandler.create(requestOptions).catch(function (err) {
/**
* jsonwire command not supported try webdriver endpoint
*/
if (err.message.match(/did not match a known command/)) {
requestOptions.path = '/session/:sessionId/alert/dismiss';
return _this.requestHandler.create(requestOptions);
}
throw err;
});
}
module.exports = exports['default'];

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