From 8f99648d4f4ff2d0871e459e164a6d93b6ac7131 Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Fri, 29 Jul 2016 12:08:48 +0300 Subject: [PATCH 001/111] close tabs after test, close browser after all tests --- lib/helper/WebDriverIO.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 9b599bbbc..549c335bd 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -250,7 +250,16 @@ class WebDriverIO extends Helper { if (this.options.restart) return this.browser.end(); this.debugSection('Session', 'cleaning cookies and localStorage'); return this.browser.execute('localStorage.clear();').then(() => { - return this.browser.deleteCookie(); + return this.browser.deleteCookie().then(() => { + return this.browser.waitUntil(function () { + return this.browser.getTabIds().then(function (handles) { + for (var i = 0; i < handles.length - 1; i++) { + this.browser.close(handles[i]); + } + return this.browser.switchTab(handles[0]); + }); + }, 30000); + }); }); } @@ -263,6 +272,10 @@ class WebDriverIO extends Helper { return this.saveScreenshot(fileName); } + _afterAll() { + return this.browser.end(); + } + _withinBegin(locator) { withinStore.elFn = this.browser.element; withinStore.elsFn = this.browser.elements; From 13dc1c3e3813fad163f395eb56befcdd5299764d Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Fri, 29 Jul 2016 14:01:08 +0300 Subject: [PATCH 002/111] revert changes, fix afterAll --- lib/helper/WebDriverIO.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 549c335bd..2d68c35c6 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -250,16 +250,7 @@ class WebDriverIO extends Helper { if (this.options.restart) return this.browser.end(); this.debugSection('Session', 'cleaning cookies and localStorage'); return this.browser.execute('localStorage.clear();').then(() => { - return this.browser.deleteCookie().then(() => { - return this.browser.waitUntil(function () { - return this.browser.getTabIds().then(function (handles) { - for (var i = 0; i < handles.length - 1; i++) { - this.browser.close(handles[i]); - } - return this.browser.switchTab(handles[0]); - }); - }, 30000); - }); + return this.browser.deleteCookie(); }); } @@ -273,7 +264,7 @@ class WebDriverIO extends Helper { } _afterAll() { - return this.browser.end(); + if (!this.options.restart) return this.browser.end(); } _withinBegin(locator) { From 59cb8029d65625caf1b440b1a1712a310442b893 Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Fri, 29 Jul 2016 17:53:33 +0300 Subject: [PATCH 003/111] experemental --- lib/helper/WebDriverIO.js | 18 ++++++++++++++++-- package.json | 3 ++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 2d68c35c6..37cc366c8 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -12,6 +12,8 @@ const assert = require('assert'); const path = require('path'); const requireg = require('requireg'); +import series from 'async/series'; + let withinStore = {}; /** @@ -249,8 +251,20 @@ class WebDriverIO extends Helper { _after() { if (this.options.restart) return this.browser.end(); this.debugSection('Session', 'cleaning cookies and localStorage'); - return this.browser.execute('localStorage.clear();').then(() => { - return this.browser.deleteCookie(); + async.series({ + one: function(callback) { + setTimeout(function() { + callback(null, this.browser.execute('localStorage.clear();')); + }, 200); + }, + two: function(callback){ + setTimeout(function() { + callback(null, this.browser.deleteCookie()); + }, 100); + } + }, function(err, results) { + console.log(results); + // results is now equal to: {one: 1, two: 2} }); } diff --git a/package.json b/package.json index a2f903ea7..38aa4f1c2 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "glob": "^6.0.1", "inquirer": "^0.11.0", "mocha": "^2.4.2", - "requireg": "^0.1.5" + "requireg": "^0.1.5", + "async": "^2.0.1" }, "devDependencies": { "chai": "^3.4.1", From b446fb82108884930e219aefe3fb1b5be941ba9e Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Wed, 21 Sep 2016 19:19:23 +0300 Subject: [PATCH 004/111] Implement BeforeSuite AfterSuite --- lib/interfaces/bdd.js | 42 +++++++++++++++++++----------------------- lib/scenario.js | 25 +++++++++++++++++-------- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/lib/interfaces/bdd.js b/lib/interfaces/bdd.js index 82399c4ae..2dd3a23cd 100644 --- a/lib/interfaces/bdd.js +++ b/lib/interfaces/bdd.js @@ -6,7 +6,6 @@ var Suite = require('mocha/lib/suite'); var Test = require('mocha/lib/test'); var event = require('../event'); var scenario = require('../scenario'); -var recorder = require('../recorder'); var escapeRe = require('escape-string-regexp'); /** @@ -21,22 +20,9 @@ var escapeRe = require('escape-string-regexp'); * * @param {Suite} suite Root suite. */ -module.exports = function (suite) { +module.exports = function(suite) { var suites = [suite]; - suite.timeout(0); - - suite.beforeAll('codeceptjs.beforeSuite', function(done) { - recorder.start(); - event.dispatcher.emit(event.suite.before, suite); - recorder.add(() => done()); - }); - suite.afterAll('codeceptjs.afterSuite', function(done) { - recorder.start(); - event.dispatcher.emit(event.suite.after, suite); - recorder.add(() => done()); - }); - - suite.on('pre-require', function (context, file, mocha) { + suite.on('pre-require', function(context, file, mocha) { var common = require('mocha/lib/interfaces/common')(suites, context); // create dispatcher @@ -51,7 +37,7 @@ module.exports = function (suite) { * and/or tests. */ - context.Feature = function (title) { + context.Feature = function(title) { if (suites.length > 1) { suites.shift(); } @@ -62,14 +48,25 @@ module.exports = function (suite) { suite.beforeEach('codeceptjs.before', scenario.setup); suite.afterEach('finialize codeceptjs', scenario.teardown); + suite.beforeAll('codeceptjs.beforeSuite', scenario.suiteSetup); + suite.afterAll('codeceptjs.afterSuite', scenario.suiteTeardown); + return suite; }; - context.Background = context.Before = function (fn) { + context.BeforeSuite = function(fn) { + suites[0].beforeAll('BeforeSuite', scenario.injected(fn)); + }; + + context.AfterSuite = function(fn) { + suites[0].afterAll('AfterSuite', scenario.injected(fn)); + }; + + context.Background = context.Before = function(fn) { suites[0].beforeEach('Before', scenario.injected(fn)); }; - context.After = function (fn) { + context.After = function(fn) { suites[0].afterEach('After', scenario.injected(fn)); }; @@ -78,13 +75,12 @@ module.exports = function (suite) { * with the given `title` and callback `fn` * acting as a thunk. */ - context.Scenario = function (title, fn) { + context.Scenario = function(title, fn) { var suite = suites[0]; if (suite.pending) { fn = null; } var test = new Test(title, fn); - test.fullTitle = () => `${suite.title}: ${title}`; test.file = file; test.async = true; test.timeout(0); @@ -95,7 +91,7 @@ module.exports = function (suite) { /** * Exclusive test-case. */ - context.Scenario.only = function (title, fn) { + context.Scenario.only = function(title, fn) { var test = context.Scenario(title, fn); var reString = '^' + escapeRe(test.fullTitle()) + '$'; mocha.grep(new RegExp(reString)); @@ -104,7 +100,7 @@ module.exports = function (suite) { /** * Pending test case. */ - context.xScenario = context.Scenario.skip = function (title) { + context.xScenario = context.Scenario.skip = function(title) { context.Scenario(title); }; }); diff --git a/lib/scenario.js b/lib/scenario.js index ad83c08ea..97c2d7430 100644 --- a/lib/scenario.js +++ b/lib/scenario.js @@ -16,8 +16,8 @@ module.exports.test = (test) => { } test.steps = []; - test.fn = function (done) { - recorder.errHandler(function (err) { + test.fn = function(done) { + recorder.errHandler(function(err) { recorder.session.start('teardown'); event.emit(event.test.failed, test, err); recorder.add(() => done(err)); @@ -34,8 +34,8 @@ module.exports.test = (test) => { return test; } recorder.catch(); // catching possible errors in promises - let resumeTest = function () { - recorder.add('create new promises queue for generator',function (data) { + let resumeTest = function() { + recorder.add('create new promises queue for generator', function(data) { recorder.session.start('generator'); // creating a new promise chain try { let resume = res.next(data); @@ -67,8 +67,8 @@ module.exports.test = (test) => { /** * Injects arguments to function from controller */ -module.exports.injected = function (fn) { - return function () { +module.exports.injected = function(fn) { + return function() { try { fn.apply(this, getInjectedArguments(fn)); } catch (err) { @@ -81,15 +81,24 @@ module.exports.injected = function (fn) { /** * Starts promise chain, so helpers could enqueue their hooks */ -module.exports.setup = function () { +module.exports.setup = function() { recorder.start(); event.emit(event.test.before); }; -module.exports.teardown = function () { +module.exports.teardown = function() { event.emit(event.test.after); }; +module.exports.suiteSetup = function() { + recorder.start(); + event.emit(event.suite.before); +}; + +module.exports.suiteTeardown = function() { + event.emit(event.suite.after); +}; + function isGenerator(fn) { return fn.constructor.name === 'GeneratorFunction'; } From 6182693ea5eeaa569688b70dc2aa3aec9c8b95ba Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Wed, 21 Sep 2016 19:24:56 +0300 Subject: [PATCH 005/111] Remove temp changes --- lib/helper/WebDriverIO.js | 205 +++++++++++++++++++------------------- lib/interfaces/bdd.js | 14 +++ package.json | 3 +- 3 files changed, 120 insertions(+), 102 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index adbc8eb4e..b97adbf4e 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -12,7 +12,6 @@ const assert = require('assert'); const path = require('path'); const requireg = require('requireg'); -import series from 'async/series'; let withinStore = {}; @@ -201,25 +200,29 @@ class WebDriverIO extends Helper { } } - static _checkRequirements() - { + static _checkRequirements() { try { requireg("webdriverio"); - } catch(e) { + } catch (e) { return ["webdriverio"]; } } static _config() { - return [ - { name: 'url', message: "Base url of site to be tested", default: 'http://localhost' }, - { name: 'browser', message: 'Browser in which testing will be performed', default: 'firefox' } - ]; + return [{ + name: 'url', + message: "Base url of site to be tested", + default: 'http://localhost' + }, { + name: 'browser', + message: 'Browser in which testing will be performed', + default: 'firefox' + }]; } _beforeSuite() { if (!this.options.restart) { - this.debugSection('Session','Starting singleton browser session'); + this.debugSection('Session', 'Starting singleton browser session'); return this._startBrowser(); } } @@ -236,7 +239,10 @@ class WebDriverIO extends Helper { } if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0) { let dimensions = this.options.windowSize.split('x'); - this.browser.windowHandleSize({ width: dimensions[0], height: dimensions[1] }); + this.browser.windowHandleSize({ + width: dimensions[0], + height: dimensions[1] + }); } return this.browser; } @@ -251,20 +257,9 @@ class WebDriverIO extends Helper { _after() { if (this.options.restart) return this.browser.end(); this.debugSection('Session', 'cleaning cookies and localStorage'); - async.series({ - one: function(callback) { - setTimeout(function() { - callback(null, this.browser.execute('localStorage.clear();')); - }, 200); - }, - two: function(callback){ - setTimeout(function() { - callback(null, this.browser.deleteCookie()); - }, 100); - } - }, function(err, results) { - console.log(results); - // results is now equal to: {one: 1, two: 2} + return this.browser.execute('localStorage.clear();').then(() => { + - + return this.browser.deleteCookie(); }); } @@ -277,19 +272,15 @@ class WebDriverIO extends Helper { return this.saveScreenshot(fileName); } - _afterAll() { - if (!this.options.restart) return this.browser.end(); - } - _withinBegin(locator) { withinStore.elFn = this.browser.element; withinStore.elsFn = this.browser.elements; this.context = locator; return this.browser.element(withStrictLocator(locator)).then((res) => { - this.browser.element = function (l) { + this.browser.element = function(l) { return this.elementIdElement(res.value.ELEMENT, l); }; - this.browser.elements = function (l) { + this.browser.elements = function(l) { return this.elementIdElements(res.value.ELEMENT, l); }; }); @@ -321,7 +312,7 @@ class WebDriverIO extends Helper { * ``` */ _locateCheckable(locator) { - return findCheckable(this.browser, locator).then(function(res){ + return findCheckable(this.browser, locator).then(function(res) { return res.value; }) } @@ -334,7 +325,7 @@ class WebDriverIO extends Helper { * ``` */ _locateClickable(locator) { - return findClickable(this.browser, locator).then(function(res){ + return findClickable(this.browser, locator).then(function(res) { return res.value; }) } @@ -347,7 +338,7 @@ class WebDriverIO extends Helper { * ``` */ _locateFields(locator) { - return findFields(this.browser, locator).then(function(res){ + return findFields(this.browser, locator).then(function(res) { return res.value; }) } @@ -371,7 +362,7 @@ class WebDriverIO extends Helper { if (context) { client = client.element(context); } - return findClickable(client, locator).then(function (res) { + return findClickable(client, locator).then(function(res) { if (!res.value || res.value.length === 0) { if (typeof(locator) === "object") locator = JSON.stringify(locator); throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); @@ -389,7 +380,7 @@ class WebDriverIO extends Helper { if (context) { client = client.element(context); } - return findClickable(client, locator).then(function (res) { + return findClickable(client, locator).then(function(res) { if (!res.value || res.value.length === 0) { if (typeof(locator) === "object") locator = JSON.stringify(locator); throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); @@ -410,7 +401,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/fillField }} */ fillField(field, value) { - return findFields(this.browser, field).then(function (res) { + return findFields(this.browser, field).then(function(res) { if (!res.value || res.value.length === 0) { throw new Error(`Field ${field} not found by name|text|CSS|XPath`); } @@ -423,7 +414,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/appendField }} */ appendField(field, value) { - return findFields(this.browser, field).then(function (res) { + return findFields(this.browser, field).then(function(res) { if (!res.value || res.value.length === 0) { throw new Error(`Field ${field} not found by name|text|CSS|XPath`); } @@ -437,7 +428,7 @@ class WebDriverIO extends Helper { * */ selectOption(select, option) { - return findFields(this.browser, select).then(function (res) { + return findFields(this.browser, select).then(function(res) { if (!res.value || res.value.length === 0) { throw new Error(`Selectable field ${select} not found by name|text|CSS|XPath`); } @@ -455,7 +446,9 @@ class WebDriverIO extends Helper { byVisibleText = `./option${normalized}|./optgroup/option${normalized}`; commands.push(this.elementIdElements(elem.ELEMENT, byVisibleText)); }); - return this.unify(commands, { extractValue: true }).then((els) => { + return this.unify(commands, { + extractValue: true + }).then((els) => { commands = []; let clickOptionFn = (el) => { if (el[0]) el = el[0]; @@ -474,7 +467,9 @@ class WebDriverIO extends Helper { commands.push(this.elementIdElements(elem.ELEMENT, byValue)); }); // try by value - return this.unify(commands, { extractValue: true }).then((els) => { + return this.unify(commands, { + extractValue: true + }).then((els) => { if (els.length === 0) { throw new Error(`Option ${option} in ${select} was found neither by visible text not by value`); } @@ -518,7 +513,7 @@ class WebDriverIO extends Helper { throw new Error(`Checkable ${field} cant be located by name|text|CSS|XPath`); } let elem = res.value[0]; - return client.elementIdSelected(elem.ELEMENT).then(function (isSelected) { + return client.elementIdSelected(elem.ELEMENT).then(function(isSelected) { if (isSelected.value) return true; return this[clickMethod](elem.ELEMENT); }); @@ -529,7 +524,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/grabTextFrom }} */ grabTextFrom(locator) { - return this.browser.getText(withStrictLocator(locator)).then(function (text) { + return this.browser.getText(withStrictLocator(locator)).then(function(text) { return text; }); } @@ -543,16 +538,16 @@ class WebDriverIO extends Helper { * ``` */ grabHTMLFrom(locator) { - return this.browser.getHTML(withStrictLocator(locator)).then(function (html) { + return this.browser.getHTML(withStrictLocator(locator)).then(function(html) { return html; }); } /** * {{> ../webapi/grabValueFrom }} - */ + */ grabValueFrom(locator) { - return this.browser.getValue(withStrictLocator(locator)).then(function (text) { + return this.browser.getValue(withStrictLocator(locator)).then(function(text) { return text; }); } @@ -561,7 +556,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/grabAttributeFrom }} */ grabAttributeFrom(locator, attr) { - return this.browser.getAttribute(withStrictLocator(locator), attr).then(function (text) { + return this.browser.getAttribute(withStrictLocator(locator), attr).then(function(text) { return text; }); } @@ -640,8 +635,8 @@ class WebDriverIO extends Helper { * {{> ../webapi/seeElement }} */ seeElement(locator) { - return this.browser.isVisible(withStrictLocator(locator)).then(function (res) { - return truth(`elements of ${locator}`,'to be seen').assert(res); + return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { + return truth(`elements of ${locator}`, 'to be seen').assert(res); }); } @@ -649,7 +644,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/dontSeeElement}} */ dontSeeElement(locator) { - return this.browser.isVisible(withStrictLocator(locator)).then(function (res) { + return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { return truth(`elements of ${locator}`, 'to be seen').negate(res); }); } @@ -658,7 +653,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/seeElementInDOM }} */ seeElementInDOM(locator) { - return this.browser.elements(withStrictLocator(locator)).then(function (res) { + return this.browser.elements(withStrictLocator(locator)).then(function(res) { return empty('elements').negate(res.value); }); } @@ -667,7 +662,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/dontSeeElementInDOM }} */ dontSeeElementInDOM(locator) { - return this.browser.elements(withStrictLocator(locator)).then(function (res) { + return this.browser.elements(withStrictLocator(locator)).then(function(res) { return empty('elements').assert(res.value); }); } @@ -691,25 +686,25 @@ class WebDriverIO extends Helper { } /** - * asserts that an element appears a given number of times in the DOM - * Element is located by label or name or CSS or XPath. - * - * ```js - * I.seeNumberOfElements('#submitBtn', 1); - * ``` - */ + * asserts that an element appears a given number of times in the DOM + * Element is located by label or name or CSS or XPath. + * + * ```js + * I.seeNumberOfElements('#submitBtn', 1); + * ``` + */ seeNumberOfElements(selector, num) { return this.browser.elements(withStrictLocator(selector)) - .then(function (res) { - return assert.equal(res.value.length, num); - }); + .then(function(res) { + return assert.equal(res.value.length, num); + }); } /** * {{> ../webapi/seeInCurrentUrl }} */ seeInCurrentUrl(url) { - return this.browser.url().then(function (res) { + return this.browser.url().then(function(res) { return stringIncludes('url').assert(url, res.value); }); } @@ -718,7 +713,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/dontSeeInCurrentUrl }} */ dontSeeInCurrentUrl(url) { - return this.browser.url().then(function (res) { + return this.browser.url().then(function(res) { return stringIncludes('url').negate(url, res.value); }); } @@ -733,8 +728,8 @@ class WebDriverIO extends Helper { } /** - * {{> ../webapi/dontSeeCurrentUrlEquals }} - */ + * {{> ../webapi/dontSeeCurrentUrlEquals }} + */ dontSeeCurrentUrlEquals(url) { return this.browser.url().then((res) => { return urlEquals(this.options.url).negate(url, res.value); @@ -817,7 +812,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/seeCookie}} */ seeCookie(name) { - return this.browser.getCookie(name).then(function (res) { + return this.browser.getCookie(name).then(function(res) { return truth('cookie ' + name, 'to be set').assert(res); }); } @@ -826,7 +821,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/dontSeeCookie}} */ dontSeeCookie(name) { - return this.browser.getCookie(name).then(function (res) { + return this.browser.getCookie(name).then(function(res) { return truth('cookie ' + name, 'to be set').negate(res); }); } @@ -843,7 +838,7 @@ class WebDriverIO extends Helper { * Don't confuse popups with modal windows, as created by [various libraries](http://jster.net/category/windows-modals-popups). */ acceptPopup() { - return this.browser.alertText().then(function (res) { + return this.browser.alertText().then(function(res) { if (res !== null) { return this.alertAccept(); } @@ -854,7 +849,7 @@ class WebDriverIO extends Helper { * Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt. */ cancelPopup() { - return this.browser.alertText().then(function (res) { + return this.browser.alertText().then(function(res) { if (res !== null) { return this.alertDismiss(); } @@ -865,9 +860,9 @@ class WebDriverIO extends Helper { * Checks that the active JavaScript popup, as created by `window.alert|window.confirm|window.prompt`, contains the given string. */ seeInPopup(text) { - return this.browser.alertText().then(function (res) { + return this.browser.alertText().then(function(res) { if (res === null) { - throw new Error('Popup is not opened'); + throw new Error('Popup is not opened'); } stringIncludes('text in popup').assert(text, res); }); @@ -886,7 +881,7 @@ class WebDriverIO extends Helper { */ pressKey(key) { let modifier; - if (Array.isArray(key) && ~['Control','Command','Shift','Alt'].indexOf(key[0])) { + if (Array.isArray(key) && ~['Control', 'Command', 'Shift', 'Alt'].indexOf(key[0])) { modifier = key[0]; } return this.browser.keys(key).then(function() { @@ -902,16 +897,19 @@ class WebDriverIO extends Helper { if (width === 'maximize') { return this.browser.windowHandleMaximize(false); } - return this.browser.windowHandleSize({ width, height }); + return this.browser.windowHandleSize({ + width, + height + }); } - /** - * Drag an item to a destination element. - * - * ```js - * I.dragAndDrop('#dragHandle', '#container'); - * ``` - */ + /** + * Drag an item to a destination element. + * + * ```js + * I.dragAndDrop('#dragHandle', '#container'); + * ``` + */ dragAndDrop(srcElement, destElement) { return this.browser.dragAndDrop( withStrictLocator(srcElement), @@ -948,8 +946,8 @@ class WebDriverIO extends Helper { waitForText(text, sec, context) { sec = sec || this.options.waitForTimeout; context = context || 'body'; - return this.browser.waitUntil(function () { - return this.getText(context).then(function (source) { + return this.browser.waitUntil(function() { + return this.getText(context).then(function(source) { if (Array.isArray(source)) { return source.filter(part => part.indexOf(text) >= 0).length > 0; } @@ -1011,13 +1009,13 @@ function proceedSee(assertType, text, context) { } else { description = 'element ' + context; } - return this.browser.getText(withStrictLocator(context)).then(function (source) { + return this.browser.getText(withStrictLocator(context)).then(function(source) { return stringIncludes(description)[assertType](text, source); }); } function findClickable(client, locator) { - if (typeof (locator) === 'object') return client.elements(withStrictLocator(locator)); + if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); if (isCSSorXPathLocator(locator)) return client.elements(locator); let literal = xpathLocator.literal(locator); @@ -1028,7 +1026,7 @@ function findClickable(client, locator) { `.//a/img[normalize-space(@alt)=${literal}]/ancestor::a`, `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][normalize-space(@value)=${literal}]` ]); - return client.elements(narrowLocator).then(function (els) { + return client.elements(narrowLocator).then(function(els) { if (els.value.length) { return els; } @@ -1040,7 +1038,7 @@ function findClickable(client, locator) { `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][./@name = ${literal}]`, `.//button[./@name = ${literal}]` ]); - return client.elements(wideLocator).then(function (els) { + return client.elements(wideLocator).then(function(els) { if (els.value.length) { return els; } @@ -1050,7 +1048,7 @@ function findClickable(client, locator) { } function findFields(client, locator) { - if (typeof (locator) === 'object') return client.elements(withStrictLocator(locator)); + if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); if (isCSSorXPathLocator(locator)) return client.elements(locator); let literal = xpathLocator.literal(locator); @@ -1069,7 +1067,7 @@ function findFields(client, locator) { } function proceedSeeField(assertType, field, value) { - return findFields(this.browser, field).then(function (res) { + return findFields(this.browser, field).then(function(res) { if (!res.value || res.value.length === 0) { throw new Error(`Field ${field} not found by name|text|CSS|XPath`); } @@ -1083,7 +1081,9 @@ function proceedSeeField(assertType, field, value) { if (el.value === false) return; commands.push(this.elementIdAttribute(el.ELEMENT, 'value')); }); - this.unify(commands, { extractValue: true }).then((val) => { + this.unify(commands, { + extractValue: true + }).then((val) => { return stringIncludes('fields by ' + field)[assertType](value, val); }); }); @@ -1114,20 +1114,22 @@ function proceedSeeField(assertType, field, value) { } function proceedSeeCheckbox(assertType, field) { - return findFields(this.browser, field).then(function (res) { + return findFields(this.browser, field).then(function(res) { if (!res.value || res.value.length === 0) { throw new Error(`Field ${field} not found by name|text|CSS|XPath`); } let commands = []; res.value.forEach((el) => commands.push(this.elementIdSelected(el.ELEMENT))); - return this.unify(commands, { extractValue: true }).then((selected) => { + return this.unify(commands, { + extractValue: true + }).then((selected) => { return truth(`checkable field ${field}`, 'to be checked')[assertType](selected); }); }); } function findCheckable(client, locator) { - if (typeof (locator) === 'object') return client.elements(withStrictLocator(locator)); + if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); if (isCSSorXPathLocator(locator)) return client.elements(locator); let literal = xpathLocator.literal(locator); @@ -1135,10 +1137,10 @@ function findCheckable(client, locator) { `.//input[@type = 'checkbox' or @type = 'radio'][(@id = //label[contains(normalize-space(string(.)), ${literal})]/@for) or @placeholder = ${literal}]`, `.//label[contains(normalize-space(string(.)), ${literal})]//input[@type = 'radio' or @type = 'checkbox']` ]); - return client.elements(byText).then(function (els) { + return client.elements(byText).then(function(els) { if (els.value.length) return els; let byName = `.//input[@type = 'checkbox' or @type = 'radio'][@name = ${literal}]`; - return client.elements(byName).then(function (els) { + return client.elements(byName).then(function(els) { if (els.value.length) return els; return client.elements(locator); // by css or xpath }); @@ -1157,18 +1159,21 @@ function isCSSorXPathLocator(locator) { function withStrictLocator(locator) { if (!locator) return null; - if (typeof (locator) !== 'object') return locator; + if (typeof(locator) !== 'object') return locator; let key = Object.keys(locator)[0]; let value = locator[key]; locator.toString = () => `{${key}: '${value}'}`; switch (key) { - case 'by': - case 'xpath': - case 'css': return value; - case 'id': return '#' + value; - case 'name': return `[name="${value}"]`; + case 'by': + case 'xpath': + case 'css': + return value; + case 'id': + return '#' + value; + case 'name': + return `[name="${value}"]`; } } diff --git a/lib/interfaces/bdd.js b/lib/interfaces/bdd.js index 2dd3a23cd..22ad105d8 100644 --- a/lib/interfaces/bdd.js +++ b/lib/interfaces/bdd.js @@ -6,6 +6,7 @@ var Suite = require('mocha/lib/suite'); var Test = require('mocha/lib/test'); var event = require('../event'); var scenario = require('../scenario'); +var recorder = require('../recorder'); var escapeRe = require('escape-string-regexp'); /** @@ -22,6 +23,19 @@ var escapeRe = require('escape-string-regexp'); */ module.exports = function(suite) { var suites = [suite]; + suite.timeout(0); + + suite.beforeAll('codeceptjs.beforeSuite', function(done) { + recorder.start(); + event.dispatcher.emit(event.suite.before, suite); + recorder.add(() => done()); + }); + suite.afterAll('codeceptjs.afterSuite', function(done) { + recorder.start(); + event.dispatcher.emit(event.suite.after, suite); + recorder.add(() => done()); + }); + suite.on('pre-require', function(context, file, mocha) { var common = require('mocha/lib/interfaces/common')(suites, context); diff --git a/package.json b/package.json index 1b939d2f1..190f0e8a3 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,7 @@ "glob": "^6.0.1", "inquirer": "^0.11.0", "mocha": "^2.4.2", - "requireg": "^0.1.5", - "async": "^2.0.1" + "requireg": "^0.1.5" }, "devDependencies": { "chai": "^3.4.1", From 3ed77fc554f5dbc1f44e67961b4980d645d1143b Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Wed, 21 Sep 2016 19:28:50 +0300 Subject: [PATCH 006/111] remove formatting --- lib/helper/WebDriverIO.js | 184 +++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 103 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index b97adbf4e..820576615 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -200,29 +200,25 @@ class WebDriverIO extends Helper { } } - static _checkRequirements() { + static _checkRequirements() + { try { requireg("webdriverio"); - } catch (e) { + } catch(e) { return ["webdriverio"]; } } static _config() { - return [{ - name: 'url', - message: "Base url of site to be tested", - default: 'http://localhost' - }, { - name: 'browser', - message: 'Browser in which testing will be performed', - default: 'firefox' - }]; + return [ + { name: 'url', message: "Base url of site to be tested", default: 'http://localhost' }, + { name: 'browser', message: 'Browser in which testing will be performed', default: 'firefox' } + ]; } _beforeSuite() { if (!this.options.restart) { - this.debugSection('Session', 'Starting singleton browser session'); + this.debugSection('Session','Starting singleton browser session'); return this._startBrowser(); } } @@ -239,10 +235,7 @@ class WebDriverIO extends Helper { } if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0) { let dimensions = this.options.windowSize.split('x'); - this.browser.windowHandleSize({ - width: dimensions[0], - height: dimensions[1] - }); + this.browser.windowHandleSize({ width: dimensions[0], height: dimensions[1] }); } return this.browser; } @@ -258,7 +251,6 @@ class WebDriverIO extends Helper { if (this.options.restart) return this.browser.end(); this.debugSection('Session', 'cleaning cookies and localStorage'); return this.browser.execute('localStorage.clear();').then(() => { - - return this.browser.deleteCookie(); }); } @@ -277,10 +269,10 @@ class WebDriverIO extends Helper { withinStore.elsFn = this.browser.elements; this.context = locator; return this.browser.element(withStrictLocator(locator)).then((res) => { - this.browser.element = function(l) { + this.browser.element = function (l) { return this.elementIdElement(res.value.ELEMENT, l); }; - this.browser.elements = function(l) { + this.browser.elements = function (l) { return this.elementIdElements(res.value.ELEMENT, l); }; }); @@ -312,7 +304,7 @@ class WebDriverIO extends Helper { * ``` */ _locateCheckable(locator) { - return findCheckable(this.browser, locator).then(function(res) { + return findCheckable(this.browser, locator).then(function(res){ return res.value; }) } @@ -325,7 +317,7 @@ class WebDriverIO extends Helper { * ``` */ _locateClickable(locator) { - return findClickable(this.browser, locator).then(function(res) { + return findClickable(this.browser, locator).then(function(res){ return res.value; }) } @@ -338,7 +330,7 @@ class WebDriverIO extends Helper { * ``` */ _locateFields(locator) { - return findFields(this.browser, locator).then(function(res) { + return findFields(this.browser, locator).then(function(res){ return res.value; }) } @@ -362,7 +354,7 @@ class WebDriverIO extends Helper { if (context) { client = client.element(context); } - return findClickable(client, locator).then(function(res) { + return findClickable(client, locator).then(function (res) { if (!res.value || res.value.length === 0) { if (typeof(locator) === "object") locator = JSON.stringify(locator); throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); @@ -380,7 +372,7 @@ class WebDriverIO extends Helper { if (context) { client = client.element(context); } - return findClickable(client, locator).then(function(res) { + return findClickable(client, locator).then(function (res) { if (!res.value || res.value.length === 0) { if (typeof(locator) === "object") locator = JSON.stringify(locator); throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); @@ -401,7 +393,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/fillField }} */ fillField(field, value) { - return findFields(this.browser, field).then(function(res) { + return findFields(this.browser, field).then(function (res) { if (!res.value || res.value.length === 0) { throw new Error(`Field ${field} not found by name|text|CSS|XPath`); } @@ -414,7 +406,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/appendField }} */ appendField(field, value) { - return findFields(this.browser, field).then(function(res) { + return findFields(this.browser, field).then(function (res) { if (!res.value || res.value.length === 0) { throw new Error(`Field ${field} not found by name|text|CSS|XPath`); } @@ -428,7 +420,7 @@ class WebDriverIO extends Helper { * */ selectOption(select, option) { - return findFields(this.browser, select).then(function(res) { + return findFields(this.browser, select).then(function (res) { if (!res.value || res.value.length === 0) { throw new Error(`Selectable field ${select} not found by name|text|CSS|XPath`); } @@ -446,9 +438,7 @@ class WebDriverIO extends Helper { byVisibleText = `./option${normalized}|./optgroup/option${normalized}`; commands.push(this.elementIdElements(elem.ELEMENT, byVisibleText)); }); - return this.unify(commands, { - extractValue: true - }).then((els) => { + return this.unify(commands, { extractValue: true }).then((els) => { commands = []; let clickOptionFn = (el) => { if (el[0]) el = el[0]; @@ -467,9 +457,7 @@ class WebDriverIO extends Helper { commands.push(this.elementIdElements(elem.ELEMENT, byValue)); }); // try by value - return this.unify(commands, { - extractValue: true - }).then((els) => { + return this.unify(commands, { extractValue: true }).then((els) => { if (els.length === 0) { throw new Error(`Option ${option} in ${select} was found neither by visible text not by value`); } @@ -513,7 +501,7 @@ class WebDriverIO extends Helper { throw new Error(`Checkable ${field} cant be located by name|text|CSS|XPath`); } let elem = res.value[0]; - return client.elementIdSelected(elem.ELEMENT).then(function(isSelected) { + return client.elementIdSelected(elem.ELEMENT).then(function (isSelected) { if (isSelected.value) return true; return this[clickMethod](elem.ELEMENT); }); @@ -524,7 +512,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/grabTextFrom }} */ grabTextFrom(locator) { - return this.browser.getText(withStrictLocator(locator)).then(function(text) { + return this.browser.getText(withStrictLocator(locator)).then(function (text) { return text; }); } @@ -538,16 +526,16 @@ class WebDriverIO extends Helper { * ``` */ grabHTMLFrom(locator) { - return this.browser.getHTML(withStrictLocator(locator)).then(function(html) { + return this.browser.getHTML(withStrictLocator(locator)).then(function (html) { return html; }); } /** * {{> ../webapi/grabValueFrom }} - */ + */ grabValueFrom(locator) { - return this.browser.getValue(withStrictLocator(locator)).then(function(text) { + return this.browser.getValue(withStrictLocator(locator)).then(function (text) { return text; }); } @@ -556,7 +544,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/grabAttributeFrom }} */ grabAttributeFrom(locator, attr) { - return this.browser.getAttribute(withStrictLocator(locator), attr).then(function(text) { + return this.browser.getAttribute(withStrictLocator(locator), attr).then(function (text) { return text; }); } @@ -635,8 +623,8 @@ class WebDriverIO extends Helper { * {{> ../webapi/seeElement }} */ seeElement(locator) { - return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { - return truth(`elements of ${locator}`, 'to be seen').assert(res); + return this.browser.isVisible(withStrictLocator(locator)).then(function (res) { + return truth(`elements of ${locator}`,'to be seen').assert(res); }); } @@ -644,7 +632,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/dontSeeElement}} */ dontSeeElement(locator) { - return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { + return this.browser.isVisible(withStrictLocator(locator)).then(function (res) { return truth(`elements of ${locator}`, 'to be seen').negate(res); }); } @@ -653,7 +641,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/seeElementInDOM }} */ seeElementInDOM(locator) { - return this.browser.elements(withStrictLocator(locator)).then(function(res) { + return this.browser.elements(withStrictLocator(locator)).then(function (res) { return empty('elements').negate(res.value); }); } @@ -662,7 +650,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/dontSeeElementInDOM }} */ dontSeeElementInDOM(locator) { - return this.browser.elements(withStrictLocator(locator)).then(function(res) { + return this.browser.elements(withStrictLocator(locator)).then(function (res) { return empty('elements').assert(res.value); }); } @@ -686,25 +674,25 @@ class WebDriverIO extends Helper { } /** - * asserts that an element appears a given number of times in the DOM - * Element is located by label or name or CSS or XPath. - * - * ```js - * I.seeNumberOfElements('#submitBtn', 1); - * ``` - */ + * asserts that an element appears a given number of times in the DOM + * Element is located by label or name or CSS or XPath. + * + * ```js + * I.seeNumberOfElements('#submitBtn', 1); + * ``` + */ seeNumberOfElements(selector, num) { return this.browser.elements(withStrictLocator(selector)) - .then(function(res) { - return assert.equal(res.value.length, num); - }); + .then(function (res) { + return assert.equal(res.value.length, num); + }); } /** * {{> ../webapi/seeInCurrentUrl }} */ seeInCurrentUrl(url) { - return this.browser.url().then(function(res) { + return this.browser.url().then(function (res) { return stringIncludes('url').assert(url, res.value); }); } @@ -713,7 +701,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/dontSeeInCurrentUrl }} */ dontSeeInCurrentUrl(url) { - return this.browser.url().then(function(res) { + return this.browser.url().then(function (res) { return stringIncludes('url').negate(url, res.value); }); } @@ -728,8 +716,8 @@ class WebDriverIO extends Helper { } /** - * {{> ../webapi/dontSeeCurrentUrlEquals }} - */ + * {{> ../webapi/dontSeeCurrentUrlEquals }} + */ dontSeeCurrentUrlEquals(url) { return this.browser.url().then((res) => { return urlEquals(this.options.url).negate(url, res.value); @@ -812,7 +800,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/seeCookie}} */ seeCookie(name) { - return this.browser.getCookie(name).then(function(res) { + return this.browser.getCookie(name).then(function (res) { return truth('cookie ' + name, 'to be set').assert(res); }); } @@ -821,7 +809,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/dontSeeCookie}} */ dontSeeCookie(name) { - return this.browser.getCookie(name).then(function(res) { + return this.browser.getCookie(name).then(function (res) { return truth('cookie ' + name, 'to be set').negate(res); }); } @@ -838,7 +826,7 @@ class WebDriverIO extends Helper { * Don't confuse popups with modal windows, as created by [various libraries](http://jster.net/category/windows-modals-popups). */ acceptPopup() { - return this.browser.alertText().then(function(res) { + return this.browser.alertText().then(function (res) { if (res !== null) { return this.alertAccept(); } @@ -849,7 +837,7 @@ class WebDriverIO extends Helper { * Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt. */ cancelPopup() { - return this.browser.alertText().then(function(res) { + return this.browser.alertText().then(function (res) { if (res !== null) { return this.alertDismiss(); } @@ -860,9 +848,9 @@ class WebDriverIO extends Helper { * Checks that the active JavaScript popup, as created by `window.alert|window.confirm|window.prompt`, contains the given string. */ seeInPopup(text) { - return this.browser.alertText().then(function(res) { + return this.browser.alertText().then(function (res) { if (res === null) { - throw new Error('Popup is not opened'); + throw new Error('Popup is not opened'); } stringIncludes('text in popup').assert(text, res); }); @@ -881,7 +869,7 @@ class WebDriverIO extends Helper { */ pressKey(key) { let modifier; - if (Array.isArray(key) && ~['Control', 'Command', 'Shift', 'Alt'].indexOf(key[0])) { + if (Array.isArray(key) && ~['Control','Command','Shift','Alt'].indexOf(key[0])) { modifier = key[0]; } return this.browser.keys(key).then(function() { @@ -897,19 +885,16 @@ class WebDriverIO extends Helper { if (width === 'maximize') { return this.browser.windowHandleMaximize(false); } - return this.browser.windowHandleSize({ - width, - height - }); + return this.browser.windowHandleSize({ width, height }); } - /** - * Drag an item to a destination element. - * - * ```js - * I.dragAndDrop('#dragHandle', '#container'); - * ``` - */ + /** + * Drag an item to a destination element. + * + * ```js + * I.dragAndDrop('#dragHandle', '#container'); + * ``` + */ dragAndDrop(srcElement, destElement) { return this.browser.dragAndDrop( withStrictLocator(srcElement), @@ -946,8 +931,8 @@ class WebDriverIO extends Helper { waitForText(text, sec, context) { sec = sec || this.options.waitForTimeout; context = context || 'body'; - return this.browser.waitUntil(function() { - return this.getText(context).then(function(source) { + return this.browser.waitUntil(function () { + return this.getText(context).then(function (source) { if (Array.isArray(source)) { return source.filter(part => part.indexOf(text) >= 0).length > 0; } @@ -1009,13 +994,13 @@ function proceedSee(assertType, text, context) { } else { description = 'element ' + context; } - return this.browser.getText(withStrictLocator(context)).then(function(source) { + return this.browser.getText(withStrictLocator(context)).then(function (source) { return stringIncludes(description)[assertType](text, source); }); } function findClickable(client, locator) { - if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); + if (typeof (locator) === 'object') return client.elements(withStrictLocator(locator)); if (isCSSorXPathLocator(locator)) return client.elements(locator); let literal = xpathLocator.literal(locator); @@ -1026,7 +1011,7 @@ function findClickable(client, locator) { `.//a/img[normalize-space(@alt)=${literal}]/ancestor::a`, `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][normalize-space(@value)=${literal}]` ]); - return client.elements(narrowLocator).then(function(els) { + return client.elements(narrowLocator).then(function (els) { if (els.value.length) { return els; } @@ -1038,7 +1023,7 @@ function findClickable(client, locator) { `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][./@name = ${literal}]`, `.//button[./@name = ${literal}]` ]); - return client.elements(wideLocator).then(function(els) { + return client.elements(wideLocator).then(function (els) { if (els.value.length) { return els; } @@ -1048,7 +1033,7 @@ function findClickable(client, locator) { } function findFields(client, locator) { - if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); + if (typeof (locator) === 'object') return client.elements(withStrictLocator(locator)); if (isCSSorXPathLocator(locator)) return client.elements(locator); let literal = xpathLocator.literal(locator); @@ -1067,7 +1052,7 @@ function findFields(client, locator) { } function proceedSeeField(assertType, field, value) { - return findFields(this.browser, field).then(function(res) { + return findFields(this.browser, field).then(function (res) { if (!res.value || res.value.length === 0) { throw new Error(`Field ${field} not found by name|text|CSS|XPath`); } @@ -1081,9 +1066,7 @@ function proceedSeeField(assertType, field, value) { if (el.value === false) return; commands.push(this.elementIdAttribute(el.ELEMENT, 'value')); }); - this.unify(commands, { - extractValue: true - }).then((val) => { + this.unify(commands, { extractValue: true }).then((val) => { return stringIncludes('fields by ' + field)[assertType](value, val); }); }); @@ -1114,22 +1097,20 @@ function proceedSeeField(assertType, field, value) { } function proceedSeeCheckbox(assertType, field) { - return findFields(this.browser, field).then(function(res) { + return findFields(this.browser, field).then(function (res) { if (!res.value || res.value.length === 0) { throw new Error(`Field ${field} not found by name|text|CSS|XPath`); } let commands = []; res.value.forEach((el) => commands.push(this.elementIdSelected(el.ELEMENT))); - return this.unify(commands, { - extractValue: true - }).then((selected) => { + return this.unify(commands, { extractValue: true }).then((selected) => { return truth(`checkable field ${field}`, 'to be checked')[assertType](selected); }); }); } function findCheckable(client, locator) { - if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); + if (typeof (locator) === 'object') return client.elements(withStrictLocator(locator)); if (isCSSorXPathLocator(locator)) return client.elements(locator); let literal = xpathLocator.literal(locator); @@ -1137,10 +1118,10 @@ function findCheckable(client, locator) { `.//input[@type = 'checkbox' or @type = 'radio'][(@id = //label[contains(normalize-space(string(.)), ${literal})]/@for) or @placeholder = ${literal}]`, `.//label[contains(normalize-space(string(.)), ${literal})]//input[@type = 'radio' or @type = 'checkbox']` ]); - return client.elements(byText).then(function(els) { + return client.elements(byText).then(function (els) { if (els.value.length) return els; let byName = `.//input[@type = 'checkbox' or @type = 'radio'][@name = ${literal}]`; - return client.elements(byName).then(function(els) { + return client.elements(byName).then(function (els) { if (els.value.length) return els; return client.elements(locator); // by css or xpath }); @@ -1159,21 +1140,18 @@ function isCSSorXPathLocator(locator) { function withStrictLocator(locator) { if (!locator) return null; - if (typeof(locator) !== 'object') return locator; + if (typeof (locator) !== 'object') return locator; let key = Object.keys(locator)[0]; let value = locator[key]; locator.toString = () => `{${key}: '${value}'}`; switch (key) { - case 'by': - case 'xpath': - case 'css': - return value; - case 'id': - return '#' + value; - case 'name': - return `[name="${value}"]`; + case 'by': + case 'xpath': + case 'css': return value; + case 'id': return '#' + value; + case 'name': return `[name="${value}"]`; } } From d63bd3e070e60e696cfc07386d31206272285c7c Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Wed, 21 Sep 2016 19:29:51 +0300 Subject: [PATCH 007/111] remove formatting --- lib/helper/WebDriverIO.js | 1 - lib/interfaces/bdd.js | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 820576615..39d8a4028 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -12,7 +12,6 @@ const assert = require('assert'); const path = require('path'); const requireg = require('requireg'); - let withinStore = {}; /** diff --git a/lib/interfaces/bdd.js b/lib/interfaces/bdd.js index 22ad105d8..8d5acad66 100644 --- a/lib/interfaces/bdd.js +++ b/lib/interfaces/bdd.js @@ -35,7 +35,6 @@ module.exports = function(suite) { event.dispatcher.emit(event.suite.after, suite); recorder.add(() => done()); }); - suite.on('pre-require', function(context, file, mocha) { var common = require('mocha/lib/interfaces/common')(suites, context); From bfb7e336df4ef30a15a9c35da761e24040dbe571 Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Wed, 21 Sep 2016 19:32:57 +0300 Subject: [PATCH 008/111] remove Formatting --- lib/interfaces/bdd.js | 21 +++++++++++---------- lib/scenario.js | 16 ++++++++-------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/interfaces/bdd.js b/lib/interfaces/bdd.js index 8d5acad66..544337e9d 100644 --- a/lib/interfaces/bdd.js +++ b/lib/interfaces/bdd.js @@ -21,7 +21,7 @@ var escapeRe = require('escape-string-regexp'); * * @param {Suite} suite Root suite. */ -module.exports = function(suite) { +module.exports = function (suite) { var suites = [suite]; suite.timeout(0); @@ -35,7 +35,7 @@ module.exports = function(suite) { event.dispatcher.emit(event.suite.after, suite); recorder.add(() => done()); }); - suite.on('pre-require', function(context, file, mocha) { + suite.on('pre-require', function (context, file, mocha) { var common = require('mocha/lib/interfaces/common')(suites, context); // create dispatcher @@ -50,7 +50,7 @@ module.exports = function(suite) { * and/or tests. */ - context.Feature = function(title) { + context.Feature = function (title) { if (suites.length > 1) { suites.shift(); } @@ -67,19 +67,19 @@ module.exports = function(suite) { return suite; }; - context.BeforeSuite = function(fn) { + context.BeforeSuite = function (fn) { suites[0].beforeAll('BeforeSuite', scenario.injected(fn)); }; - context.AfterSuite = function(fn) { + context.AfterSuite = function (fn) { suites[0].afterAll('AfterSuite', scenario.injected(fn)); }; - context.Background = context.Before = function(fn) { + context.Background = context.Before = function (fn) { suites[0].beforeEach('Before', scenario.injected(fn)); }; - context.After = function(fn) { + context.After = function (fn) { suites[0].afterEach('After', scenario.injected(fn)); }; @@ -88,12 +88,13 @@ module.exports = function(suite) { * with the given `title` and callback `fn` * acting as a thunk. */ - context.Scenario = function(title, fn) { + context.Scenario = function (title, fn) { var suite = suites[0]; if (suite.pending) { fn = null; } var test = new Test(title, fn); + test.fullTitle = () => `${suite.title}: ${title}`; test.file = file; test.async = true; test.timeout(0); @@ -104,7 +105,7 @@ module.exports = function(suite) { /** * Exclusive test-case. */ - context.Scenario.only = function(title, fn) { + context.Scenario.only = function (title, fn) { var test = context.Scenario(title, fn); var reString = '^' + escapeRe(test.fullTitle()) + '$'; mocha.grep(new RegExp(reString)); @@ -113,7 +114,7 @@ module.exports = function(suite) { /** * Pending test case. */ - context.xScenario = context.Scenario.skip = function(title) { + context.xScenario = context.Scenario.skip = function (title) { context.Scenario(title); }; }); diff --git a/lib/scenario.js b/lib/scenario.js index 97c2d7430..14d36564d 100644 --- a/lib/scenario.js +++ b/lib/scenario.js @@ -16,8 +16,8 @@ module.exports.test = (test) => { } test.steps = []; - test.fn = function(done) { - recorder.errHandler(function(err) { + test.fn = function (done) { + recorder.errHandler(function (err) { recorder.session.start('teardown'); event.emit(event.test.failed, test, err); recorder.add(() => done(err)); @@ -34,7 +34,7 @@ module.exports.test = (test) => { return test; } recorder.catch(); // catching possible errors in promises - let resumeTest = function() { + let resumeTest = function () { recorder.add('create new promises queue for generator', function(data) { recorder.session.start('generator'); // creating a new promise chain try { @@ -67,7 +67,7 @@ module.exports.test = (test) => { /** * Injects arguments to function from controller */ -module.exports.injected = function(fn) { +module.exports.injected = function (fn) { return function() { try { fn.apply(this, getInjectedArguments(fn)); @@ -81,21 +81,21 @@ module.exports.injected = function(fn) { /** * Starts promise chain, so helpers could enqueue their hooks */ -module.exports.setup = function() { +module.exports.setup = function () { recorder.start(); event.emit(event.test.before); }; -module.exports.teardown = function() { +module.exports.teardown = function () { event.emit(event.test.after); }; -module.exports.suiteSetup = function() { +module.exports.suiteSetup = function () { recorder.start(); event.emit(event.suite.before); }; -module.exports.suiteTeardown = function() { +module.exports.suiteTeardown = function () { event.emit(event.suite.after); }; From d9d6f42876a9edffe2c0b7cbcbeb3617423b382c Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 22 Sep 2016 13:27:02 +0300 Subject: [PATCH 009/111] Fix AfterSuite Hook --- lib/scenario.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/scenario.js b/lib/scenario.js index 14d36564d..a99489327 100644 --- a/lib/scenario.js +++ b/lib/scenario.js @@ -35,7 +35,7 @@ module.exports.test = (test) => { } recorder.catch(); // catching possible errors in promises let resumeTest = function () { - recorder.add('create new promises queue for generator', function(data) { + recorder.add('create new promises queue for generator',function (data) { recorder.session.start('generator'); // creating a new promise chain try { let resume = res.next(data); @@ -96,6 +96,7 @@ module.exports.suiteSetup = function () { }; module.exports.suiteTeardown = function () { + recorder.start(); event.emit(event.suite.after); }; From 875e55bd04db60542eb637e10e8d7fa758511a9b Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 22 Sep 2016 13:28:26 +0300 Subject: [PATCH 010/111] Fix formatting --- lib/interfaces/bdd.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/interfaces/bdd.js b/lib/interfaces/bdd.js index 544337e9d..dc65e5382 100644 --- a/lib/interfaces/bdd.js +++ b/lib/interfaces/bdd.js @@ -35,6 +35,7 @@ module.exports = function (suite) { event.dispatcher.emit(event.suite.after, suite); recorder.add(() => done()); }); + suite.on('pre-require', function (context, file, mocha) { var common = require('mocha/lib/interfaces/common')(suites, context); From 8214a06904e03c328278b8003eab3415a3ed25b2 Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Mon, 3 Oct 2016 12:36:03 +0300 Subject: [PATCH 011/111] Update docs --- docs/basics.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/basics.md b/docs/basics.md index f7ee9e86d..0fa819209 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -91,8 +91,14 @@ Same as `Before` you can use `After` to run teardown for each scenario. ## BeforeSuite If you need to run complex setup before all tests and teardown this afterwards you can use `BeforeSuite` and `AfterSuite` -functions. Unlike `Before` and `After` hooks, `BeforeSuite` and `AfterSuite` doesn't have access to `I` object. -You can use them to execute plain JavaScript code, which is expected to return a promise. +functions. `BeforeSuite` and `AfterSuite` have access to `I` object, but `BeforeSuite/AfterSuite` don't have an access to the browser because it's not running at this moment. +You can use them to execute handlers that will setup your enviroment. BeforeSuite will work only for a file where it was declared (so you can declare different setups for files) + +```js +BeforeSuite((I) => { + I.syncDown('testfolder'); +}); +``` Here are some ideas where to use BeforeSuite. From 261e9815fc1143565133f07a949e5ee0aecd3fe0 Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Mon, 3 Oct 2016 12:37:29 +0300 Subject: [PATCH 012/111] Update docs 2 --- docs/basics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/basics.md b/docs/basics.md index 0fa819209..39f3438ef 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -92,7 +92,7 @@ Same as `Before` you can use `After` to run teardown for each scenario. If you need to run complex setup before all tests and teardown this afterwards you can use `BeforeSuite` and `AfterSuite` functions. `BeforeSuite` and `AfterSuite` have access to `I` object, but `BeforeSuite/AfterSuite` don't have an access to the browser because it's not running at this moment. -You can use them to execute handlers that will setup your enviroment. BeforeSuite will work only for a file where it was declared (so you can declare different setups for files) +You can use them to execute handlers that will setup your enviroment. BeforeSuite/AfterSuite will work only for a file where it was declared (so you can declare different setups for files) ```js BeforeSuite((I) => { From 6f4aaf7bb107cd2dd4a992998e174a4e4ab84dde Mon Sep 17 00:00:00 2001 From: Andrey Pshenkin Date: Mon, 3 Oct 2016 12:38:32 +0300 Subject: [PATCH 013/111] Update docs 3 --- docs/basics.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/basics.md b/docs/basics.md index 39f3438ef..f9f830b41 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -92,12 +92,17 @@ Same as `Before` you can use `After` to run teardown for each scenario. If you need to run complex setup before all tests and teardown this afterwards you can use `BeforeSuite` and `AfterSuite` functions. `BeforeSuite` and `AfterSuite` have access to `I` object, but `BeforeSuite/AfterSuite` don't have an access to the browser because it's not running at this moment. -You can use them to execute handlers that will setup your enviroment. BeforeSuite/AfterSuite will work only for a file where it was declared (so you can declare different setups for files) +You can use them to execute handlers that will setup your enviroment. `BeforeSuite/AfterSuite` will work only for a file where it was declared (so you can declare different setups for files) ```js BeforeSuite((I) => { I.syncDown('testfolder'); }); + +AfterSuite((I) => { + I.syncUp('testfolder'); + I.clearDir('testfolder'); +}) ``` Here are some ideas where to use BeforeSuite. From 4d107e910c50af90075cc4e98eaf6fbae1a4c665 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 3 Oct 2016 16:53:52 +0300 Subject: [PATCH 014/111] Fix "restart: false" --- lib/interfaces/bdd.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lib/interfaces/bdd.js b/lib/interfaces/bdd.js index dc65e5382..f96bc6b9c 100644 --- a/lib/interfaces/bdd.js +++ b/lib/interfaces/bdd.js @@ -24,17 +24,6 @@ var escapeRe = require('escape-string-regexp'); module.exports = function (suite) { var suites = [suite]; suite.timeout(0); - - suite.beforeAll('codeceptjs.beforeSuite', function(done) { - recorder.start(); - event.dispatcher.emit(event.suite.before, suite); - recorder.add(() => done()); - }); - suite.afterAll('codeceptjs.afterSuite', function(done) { - recorder.start(); - event.dispatcher.emit(event.suite.after, suite); - recorder.add(() => done()); - }); suite.on('pre-require', function (context, file, mocha) { var common = require('mocha/lib/interfaces/common')(suites, context); From 2025774a3bafd36d45466f8749f4702f036e9e1f Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 28 Oct 2016 11:29:09 +0300 Subject: [PATCH 015/111] Update basics.md --- docs/basics.md | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/docs/basics.md b/docs/basics.md index f9f830b41..a8a1631cb 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -92,26 +92,20 @@ Same as `Before` you can use `After` to run teardown for each scenario. If you need to run complex setup before all tests and teardown this afterwards you can use `BeforeSuite` and `AfterSuite` functions. `BeforeSuite` and `AfterSuite` have access to `I` object, but `BeforeSuite/AfterSuite` don't have an access to the browser because it's not running at this moment. -You can use them to execute handlers that will setup your enviroment. `BeforeSuite/AfterSuite` will work only for a file where it was declared (so you can declare different setups for files) +You can use them to execute handlers that will setup your environment. `BeforeSuite/AfterSuite` will work only for a file where it was declared (so you can declare different setups for files) ```js BeforeSuite((I) => { - I.syncDown('testfolder'); + I.syncDown('testfolder'); }); AfterSuite((I) => { - I.syncUp('testfolder'); - I.clearDir('testfolder'); + I.syncUp('testfolder'); + I.clearDir('testfolder'); }) ``` -Here are some ideas where to use BeforeSuite. - -> You can use these feature, If your web application has any integration with client application. E.g. web application connects to client application in my project using Websockets to send/get information about installed apps(games) and statuses about installation on PC. Also web application calls client app to launch the game. So in my web tests sometimes I need to install different versions of client app for tests (with different settings) to check that everything works well. But I don't need to reinstall app after each test or one time (on launching tests). Because of this BeforeSuite|AfterSuite are best for me. - -> During tests I use webdrivercss to test CSS https://github.com/webdriverio/webdrivercss . So for this I need reference screenshots. It's very expensive, to save all screenshots to repository, because the repository will be too large and also during tests run you don't need all screenshots. Because of this I use the remote server to save screenshots. Before tests I have to download them and during scenarios I check the CSS using these screenshots. And after test suite I upload difference to remote server. - -*[Via @APshenkin](https://github.com/Codeception/CodeceptJS/pull/231#issuecomment-249554933)* +[Here are some ideas](https://github.com/Codeception/CodeceptJS/pull/231#issuecomment-249554933) where to use BeforeSuite hooks. ## Within From 82b15b20d54287e507a770be71062e153ae09bbf Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 28 Oct 2016 11:34:52 +0300 Subject: [PATCH 016/111] Add support for string in restart --- lib/helper/WebDriverIO.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index b3b3b9803..2a669f72f 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -203,7 +203,10 @@ class WebDriverIO extends Helper { this.options.baseUrl = this.options.url || this.options.baseUrl; this.options.desiredCapabilities.browserName = this.options.browser || this.options.desiredCapabilities.browserName; this.options.waitForTimeout /= 1000; // convert to seconds - + + if (typeof this.options.restart = "string") { + this.options.restart = (this.options.restart == "true"); + } if (!this.options.url || !this.options.browser) { throw new Error(` From ec16ceda80a8701290e2c31d1bfec00b68035782 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 28 Oct 2016 11:43:13 +0300 Subject: [PATCH 017/111] Add to selenium-webdriver helper --- lib/helper/SeleniumWebdriver.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/helper/SeleniumWebdriver.js b/lib/helper/SeleniumWebdriver.js index 8b1223616..7b3fc952a 100644 --- a/lib/helper/SeleniumWebdriver.js +++ b/lib/helper/SeleniumWebdriver.js @@ -72,6 +72,11 @@ class SeleniumWebdriver extends Helper { }; this.options = Object.assign(this.options, config); this.options.waitforTimeout /= 1000; // convert to seconds + + if (typeof this.options.restart = "string") { + this.options.restart = (this.options.restart == "true"); + } + } _init() { From 1dafd56a617b8388f80bf54b4c42a5508ee11929 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 28 Oct 2016 11:43:59 +0300 Subject: [PATCH 018/111] Add to protractor --- lib/helper/Protractor.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/helper/Protractor.js b/lib/helper/Protractor.js index 4bf7092b5..39e1b6baf 100644 --- a/lib/helper/Protractor.js +++ b/lib/helper/Protractor.js @@ -73,6 +73,10 @@ class Protractor extends SeleniumWebdriver { this.options = Object.assign(this.options, config); if (this.options.proxy) this.options.capabilities.proxy = this.options.proxy; if (this.options.browser) this.options.capabilities.browserName = this.options.browser; + + if (typeof this.options.restart = "string") { + this.options.restart = (this.options.restart == "true"); + } } _init() { From 43f8ee7d24a468f15cbb7736bac3e0bae18c393b Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 28 Oct 2016 15:27:27 +0300 Subject: [PATCH 019/111] Fix restart config --- lib/helper/WebDriverIO.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 2a669f72f..c3c8ed2de 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -204,7 +204,7 @@ class WebDriverIO extends Helper { this.options.desiredCapabilities.browserName = this.options.browser || this.options.desiredCapabilities.browserName; this.options.waitForTimeout /= 1000; // convert to seconds - if (typeof this.options.restart = "string") { + if (typeof this.options.restart == "string") { this.options.restart = (this.options.restart == "true"); } From 55513b00cd01646764f29015d472f364963ba067 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 28 Oct 2016 15:27:59 +0300 Subject: [PATCH 020/111] Update SeleniumWebdriver.js --- lib/helper/SeleniumWebdriver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helper/SeleniumWebdriver.js b/lib/helper/SeleniumWebdriver.js index 7b3fc952a..226203869 100644 --- a/lib/helper/SeleniumWebdriver.js +++ b/lib/helper/SeleniumWebdriver.js @@ -73,7 +73,7 @@ class SeleniumWebdriver extends Helper { this.options = Object.assign(this.options, config); this.options.waitforTimeout /= 1000; // convert to seconds - if (typeof this.options.restart = "string") { + if (typeof this.options.restart == "string") { this.options.restart = (this.options.restart == "true"); } From a53b0bfa681e3273d595d2ec7279d1ced5d0ed30 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 28 Oct 2016 15:28:13 +0300 Subject: [PATCH 021/111] Fix restart config --- lib/helper/Protractor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helper/Protractor.js b/lib/helper/Protractor.js index 39e1b6baf..f0f564ada 100644 --- a/lib/helper/Protractor.js +++ b/lib/helper/Protractor.js @@ -74,7 +74,7 @@ class Protractor extends SeleniumWebdriver { if (this.options.proxy) this.options.capabilities.proxy = this.options.proxy; if (this.options.browser) this.options.capabilities.browserName = this.options.browser; - if (typeof this.options.restart = "string") { + if (typeof this.options.restart == "string") { this.options.restart = (this.options.restart == "true"); } } From 46deee1ea349e3f49c72b9f7c5ef0148eb74e639 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 23 Nov 2016 12:35:28 +0300 Subject: [PATCH 022/111] Revert changes --- lib/helper/Protractor.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/helper/Protractor.js b/lib/helper/Protractor.js index c13bff6d8..c8ba72aef 100644 --- a/lib/helper/Protractor.js +++ b/lib/helper/Protractor.js @@ -73,10 +73,6 @@ class Protractor extends SeleniumWebdriver { this.options = Object.assign(this.options, config); if (this.options.proxy) this.options.capabilities.proxy = this.options.proxy; if (this.options.browser) this.options.capabilities.browserName = this.options.browser; - - if (typeof this.options.restart == "string") { - this.options.restart = (this.options.restart == "true"); - } } _init() { From 5916153589c6a1cfc9b7f136fb4a7a14120aeb95 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 23 Nov 2016 12:36:08 +0300 Subject: [PATCH 023/111] Revert changes --- lib/helper/SeleniumWebdriver.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/helper/SeleniumWebdriver.js b/lib/helper/SeleniumWebdriver.js index 97ead235e..11b25d0d8 100644 --- a/lib/helper/SeleniumWebdriver.js +++ b/lib/helper/SeleniumWebdriver.js @@ -74,11 +74,6 @@ class SeleniumWebdriver extends Helper { }; this.options = Object.assign(this.options, config); this.options.waitforTimeout /= 1000; // convert to seconds - - if (typeof this.options.restart == "string") { - this.options.restart = (this.options.restart == "true"); - } - } _init() { From 3a49fd5e53ca15c6d428df92cee843641eb0ba61 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 23 Nov 2016 12:37:00 +0300 Subject: [PATCH 024/111] Revert changes --- lib/helper/WebDriverIO.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 23908685a..d1703d707 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -206,10 +206,6 @@ class WebDriverIO extends Helper { this.options.baseUrl = this.options.url || this.options.baseUrl; this.options.desiredCapabilities.browserName = this.options.browser || this.options.desiredCapabilities.browserName; this.options.waitForTimeout /= 1000; // convert to seconds - - if (typeof this.options.restart == "string") { - this.options.restart = (this.options.restart == "true"); - } if (!this.options.url || !this.options.browser) { throw new Error(` From 82e0fc16066dad512931df14bb04ff39398b5057 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 23 Nov 2016 12:43:20 +0300 Subject: [PATCH 025/111] add hashcodeOption --- lib/helper/WebDriverIO.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index d1703d707..5a95f9670 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -194,6 +194,7 @@ class WebDriverIO extends Helper { waitForTimeout: 1000, // ms desiredCapabilities: {}, restart: true, + hashCodeScrenshotsNames: false, manualStart: false, timeouts: { script: 1000 // ms @@ -287,7 +288,11 @@ class WebDriverIO extends Helper { } _failed(test) { - let fileName = test.title.replace(/ /g, '_') + '.failed.png'; + let fileName = ''; + if (this.options.hashCodeScreenshotsNames) + fileName = hashCode(test.title) + '-' + hashCode(test.file) + '.failed.png'; + else + fileName = test.title.replace(/ /g, '_') + '.failed.png'; return this.saveScreenshot(fileName); } @@ -1215,4 +1220,16 @@ function withStrictLocator(locator) { } } +function hashCode(str) { + var hash = 0; + var char; + if (str.length == 0) return hash; + for (var i = 0; i < str.length; i++) { + char = str.charCodeAt(i); + hash = ((hash << 5) - hash) + char; + hash = hash & hash; // Convert to 32bit integer + } + return hash; +} + module.exports = WebDriverIO; From 69a721d4851ad1fc4043d206b328c85fb50654f9 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 23 Nov 2016 12:56:33 +0300 Subject: [PATCH 026/111] fix --- lib/helper/WebDriverIO.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 5a95f9670..d1bc17c00 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -194,7 +194,7 @@ class WebDriverIO extends Helper { waitForTimeout: 1000, // ms desiredCapabilities: {}, restart: true, - hashCodeScrenshotsNames: false, + hashCodeScreenshotsNames: false, manualStart: false, timeouts: { script: 1000 // ms From 1876c1882b2d27f7587e49d0008588d3dc3a81a1 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 29 Nov 2016 11:41:59 +0300 Subject: [PATCH 027/111] Appium integration --- lib/helper/WebDriverIO.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 0a685fd15..b1f32257e 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -13,7 +13,7 @@ const path = require('path'); const requireg = require('requireg'); let withinStore = {}; - +// test /** * WebDriverIO helper which wraps [webdriverio](http://webdriver.io/) library to * manipulate browser using Selenium WebDriver or PhantomJS. From 6fa458a47d34832cb127acce2e277f1ed21484a7 Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 12:28:01 +0300 Subject: [PATCH 028/111] Init commit --- lib/helper/WebDriverIO.js | 230 ++++++++++++++++++++++++-------------- 1 file changed, 144 insertions(+), 86 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index b1f32257e..ccc14a9e7 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -13,7 +13,7 @@ const path = require('path'); const requireg = require('requireg'); let withinStore = {}; -// test + /** * WebDriverIO helper which wraps [webdriverio](http://webdriver.io/) library to * manipulate browser using Selenium WebDriver or PhantomJS. @@ -207,9 +207,9 @@ class WebDriverIO extends Helper { this.options.desiredCapabilities.browserName = this.options.browser || this.options.desiredCapabilities.browserName; this.options.waitForTimeout /= 1000; // convert to seconds - - if (!this.options.url || !this.options.browser) { - throw new Error(` + if (!this.options.desiredCapabilities.platformName) { + if (!this.options.url || !this.options.browser) { + throw new Error(` WebDriverIO requires at least these parameters Check your codeceptjs config file to ensure these are set properly { @@ -221,28 +221,69 @@ class WebDriverIO extends Helper { } } `); + } + } else { + if (this.options.desiredCapabilities.platformName = "iOS" && (!this.options.desiredCapabilities.app || !this.options.desiredCapabilities.deviceName || !this.options.desiredCapabilities.platformVersion))) { + throw new Error(` + WebDriverIO requires at least these parameters to run in Appium mode for iOS + Check your codeceptjs config file to ensure these are set properly + { + "helpers": { + "WebDriverIO": { + desiredCapabilities: { + platformName: "iOS", + app: "http://myapp.com/app.ipa", + deviceName: "iPhone Simulator", + platformVersion: "7.1" + } + } + } + } + `); + } else if ((this.options.desiredCapabilities.platformName = "Android") && (!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion)) { + throw new Error(` + WebDriverIO requires at least these parameters to run in Appium mode for Android + Check your codeceptjs config file to ensure these are set properly + { + "helpers": { + "WebDriverIO": { + desiredCapabilities: { + platformName: "Android", + appPackage: "com.aim.condition", + appActivity: "com.aim.condition.activities.MainActivity", + platformVersion: "6.0.1" + } + } + } + } + `); } + } - static _checkRequirements() - { + static _checkRequirements() { try { requireg("webdriverio"); - } catch(e) { + } catch (e) { return ["webdriverio"]; } } static _config() { - return [ - { name: 'url', message: "Base url of site to be tested", default: 'http://localhost' }, - { name: 'browser', message: 'Browser in which testing will be performed', default: 'firefox' } - ]; + return [{ + name: 'url', + message: "Base url of site to be tested", + default: 'http://localhost' + }, { + name: 'browser', + message: 'Browser in which testing will be performed', + default: 'firefox' + }]; } _beforeSuite() { if (!this.options.restart && !this.options.manualStart) { - this.debugSection('Session','Starting singleton browser session'); + this.debugSection('Session', 'Starting singleton browser session'); return this._startBrowser(); } } @@ -263,7 +304,10 @@ class WebDriverIO extends Helper { } if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0) { let dimensions = this.options.windowSize.split('x'); - this.browser.windowHandleSize({ width: dimensions[0], height: dimensions[1] }); + this.browser.windowHandleSize({ + width: dimensions[0], + height: dimensions[1] + }); } return this.browser; } @@ -297,10 +341,10 @@ class WebDriverIO extends Helper { withinStore.elsFn = this.browser.elements; this.context = locator; return this.browser.element(withStrictLocator(locator)).then((res) => { - this.browser.element = function (l) { + this.browser.element = function(l) { return this.elementIdElement(res.value.ELEMENT, l); }; - this.browser.elements = function (l) { + this.browser.elements = function(l) { return this.elementIdElements(res.value.ELEMENT, l); }; }); @@ -332,7 +376,7 @@ class WebDriverIO extends Helper { * ``` */ _locateCheckable(locator) { - return findCheckable(this.browser, locator).then(function(res){ + return findCheckable(this.browser, locator).then(function(res) { return res.value; }) } @@ -345,7 +389,7 @@ class WebDriverIO extends Helper { * ``` */ _locateClickable(locator) { - return findClickable(this.browser, locator).then(function(res){ + return findClickable(this.browser, locator).then(function(res) { return res.value; }) } @@ -358,7 +402,7 @@ class WebDriverIO extends Helper { * ``` */ _locateFields(locator) { - return findFields(this.browser, locator).then(function(res){ + return findFields(this.browser, locator).then(function(res) { return res.value; }) } @@ -403,7 +447,7 @@ class WebDriverIO extends Helper { if (context) { client = client.element(context); } - return findClickable(client, locator).then(function (res) { + return findClickable(client, locator).then(function(res) { if (!res.value || res.value.length === 0) { if (typeof(locator) === "object") locator = JSON.stringify(locator); throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); @@ -421,7 +465,7 @@ class WebDriverIO extends Helper { if (context) { client = client.element(context); } - return findClickable(client, locator).then(function (res) { + return findClickable(client, locator).then(function(res) { if (!res.value || res.value.length === 0) { if (typeof(locator) === "object") locator = JSON.stringify(locator); throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); @@ -442,7 +486,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/fillField }} */ fillField(field, value) { - return findFields(this.browser, field).then(function (res) { + return findFields(this.browser, field).then(function(res) { if (!res.value || res.value.length === 0) { throw new Error(`Field ${field} not found by name|text|CSS|XPath`); } @@ -455,7 +499,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/appendField }} */ appendField(field, value) { - return findFields(this.browser, field).then(function (res) { + return findFields(this.browser, field).then(function(res) { if (!res.value || res.value.length === 0) { throw new Error(`Field ${field} not found by name|text|CSS|XPath`); } @@ -469,7 +513,7 @@ class WebDriverIO extends Helper { * */ selectOption(select, option) { - return findFields(this.browser, select).then(function (res) { + return findFields(this.browser, select).then(function(res) { if (!res.value || res.value.length === 0) { throw new Error(`Selectable field ${select} not found by name|text|CSS|XPath`); } @@ -487,7 +531,9 @@ class WebDriverIO extends Helper { byVisibleText = `./option${normalized}|./optgroup/option${normalized}`; commands.push(this.elementIdElements(elem.ELEMENT, byVisibleText)); }); - return this.unify(commands, { extractValue: true }).then((els) => { + return this.unify(commands, { + extractValue: true + }).then((els) => { commands = []; let clickOptionFn = (el) => { if (el[0]) el = el[0]; @@ -506,7 +552,9 @@ class WebDriverIO extends Helper { commands.push(this.elementIdElements(elem.ELEMENT, byValue)); }); // try by value - return this.unify(commands, { extractValue: true }).then((els) => { + return this.unify(commands, { + extractValue: true + }).then((els) => { if (els.length === 0) { throw new Error(`Option ${option} in ${select} was found neither by visible text not by value`); } @@ -527,7 +575,7 @@ class WebDriverIO extends Helper { throw new Error(`File at ${file} can not be found on local system`); } return findFields(this.browser, locator).then((el) => { - this.debug("Uploading "+file); + this.debug("Uploading " + file); return this.browser.uploadFile(file).then((res) => { if (!el.value || el.value.length === 0) { throw new Error(`File field ${locator} not found by name|text|CSS|XPath`); @@ -551,7 +599,7 @@ class WebDriverIO extends Helper { throw new Error(`Checkable ${field} cant be located by name|text|CSS|XPath`); } let elem = res.value[0]; - return client.elementIdSelected(elem.ELEMENT).then(function (isSelected) { + return client.elementIdSelected(elem.ELEMENT).then(function(isSelected) { if (isSelected.value) return true; return this[clickMethod](elem.ELEMENT); }); @@ -562,7 +610,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/grabTextFrom }} */ grabTextFrom(locator) { - return this.browser.getText(withStrictLocator(locator)).then(function (text) { + return this.browser.getText(withStrictLocator(locator)).then(function(text) { return text; }); } @@ -576,16 +624,16 @@ class WebDriverIO extends Helper { * ``` */ grabHTMLFrom(locator) { - return this.browser.getHTML(withStrictLocator(locator)).then(function (html) { + return this.browser.getHTML(withStrictLocator(locator)).then(function(html) { return html; }); } /** * {{> ../webapi/grabValueFrom }} - */ + */ grabValueFrom(locator) { - return this.browser.getValue(withStrictLocator(locator)).then(function (text) { + return this.browser.getValue(withStrictLocator(locator)).then(function(text) { return text; }); } @@ -594,7 +642,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/grabAttributeFrom }} */ grabAttributeFrom(locator, attr) { - return this.browser.getAttribute(withStrictLocator(locator), attr).then(function (text) { + return this.browser.getAttribute(withStrictLocator(locator), attr).then(function(text) { return text; }); } @@ -673,8 +721,8 @@ class WebDriverIO extends Helper { * {{> ../webapi/seeElement }} */ seeElement(locator) { - return this.browser.isVisible(withStrictLocator(locator)).then(function (res) { - return truth(`elements of ${locator}`,'to be seen').assert(res); + return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { + return truth(`elements of ${locator}`, 'to be seen').assert(res); }); } @@ -682,7 +730,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/dontSeeElement}} */ dontSeeElement(locator) { - return this.browser.isVisible(withStrictLocator(locator)).then(function (res) { + return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { return truth(`elements of ${locator}`, 'to be seen').negate(res); }); } @@ -691,7 +739,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/seeElementInDOM }} */ seeElementInDOM(locator) { - return this.browser.elements(withStrictLocator(locator)).then(function (res) { + return this.browser.elements(withStrictLocator(locator)).then(function(res) { return empty('elements').negate(res.value); }); } @@ -700,7 +748,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/dontSeeElementInDOM }} */ dontSeeElementInDOM(locator) { - return this.browser.elements(withStrictLocator(locator)).then(function (res) { + return this.browser.elements(withStrictLocator(locator)).then(function(res) { return empty('elements').assert(res.value); }); } @@ -724,25 +772,25 @@ class WebDriverIO extends Helper { } /** - * asserts that an element appears a given number of times in the DOM - * Element is located by label or name or CSS or XPath. - * - * ```js - * I.seeNumberOfElements('#submitBtn', 1); - * ``` - */ + * asserts that an element appears a given number of times in the DOM + * Element is located by label or name or CSS or XPath. + * + * ```js + * I.seeNumberOfElements('#submitBtn', 1); + * ``` + */ seeNumberOfElements(selector, num) { return this.browser.elements(withStrictLocator(selector)) - .then(function (res) { - return assert.equal(res.value.length, num); - }); + .then(function(res) { + return assert.equal(res.value.length, num); + }); } /** * {{> ../webapi/seeInCurrentUrl }} */ seeInCurrentUrl(url) { - return this.browser.url().then(function (res) { + return this.browser.url().then(function(res) { return stringIncludes('url').assert(url, res.value); }); } @@ -751,7 +799,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/dontSeeInCurrentUrl }} */ dontSeeInCurrentUrl(url) { - return this.browser.url().then(function (res) { + return this.browser.url().then(function(res) { return stringIncludes('url').negate(url, res.value); }); } @@ -766,8 +814,8 @@ class WebDriverIO extends Helper { } /** - * {{> ../webapi/dontSeeCurrentUrlEquals }} - */ + * {{> ../webapi/dontSeeCurrentUrlEquals }} + */ dontSeeCurrentUrlEquals(url) { return this.browser.url().then((res) => { return urlEquals(this.options.url).negate(url, res.value); @@ -846,7 +894,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/seeCookie}} */ seeCookie(name) { - return this.browser.getCookie(name).then(function (res) { + return this.browser.getCookie(name).then(function(res) { return truth('cookie ' + name, 'to be set').assert(res); }); } @@ -855,7 +903,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/dontSeeCookie}} */ dontSeeCookie(name) { - return this.browser.getCookie(name).then(function (res) { + return this.browser.getCookie(name).then(function(res) { return truth('cookie ' + name, 'to be set').negate(res); }); } @@ -872,7 +920,7 @@ class WebDriverIO extends Helper { * Don't confuse popups with modal windows, as created by [various libraries](http://jster.net/category/windows-modals-popups). */ acceptPopup() { - return this.browser.alertText().then(function (res) { + return this.browser.alertText().then(function(res) { if (res !== null) { return this.alertAccept(); } @@ -883,7 +931,7 @@ class WebDriverIO extends Helper { * Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt. */ cancelPopup() { - return this.browser.alertText().then(function (res) { + return this.browser.alertText().then(function(res) { if (res !== null) { return this.alertDismiss(); } @@ -894,9 +942,9 @@ class WebDriverIO extends Helper { * Checks that the active JavaScript popup, as created by `window.alert|window.confirm|window.prompt`, contains the given string. */ seeInPopup(text) { - return this.browser.alertText().then(function (res) { + return this.browser.alertText().then(function(res) { if (res === null) { - throw new Error('Popup is not opened'); + throw new Error('Popup is not opened'); } stringIncludes('text in popup').assert(text, res); }); @@ -915,7 +963,7 @@ class WebDriverIO extends Helper { */ pressKey(key) { let modifier; - if (Array.isArray(key) && ~['Control','Command','Shift','Alt'].indexOf(key[0])) { + if (Array.isArray(key) && ~['Control', 'Command', 'Shift', 'Alt'].indexOf(key[0])) { modifier = key[0]; } return this.browser.keys(key).then(function() { @@ -931,16 +979,19 @@ class WebDriverIO extends Helper { if (width === 'maximize') { return this.browser.windowHandleMaximize(false); } - return this.browser.windowHandleSize({ width, height }); + return this.browser.windowHandleSize({ + width, + height + }); } - /** - * Drag an item to a destination element. - * - * ```js - * I.dragAndDrop('#dragHandle', '#container'); - * ``` - */ + /** + * Drag an item to a destination element. + * + * ```js + * I.dragAndDrop('#dragHandle', '#container'); + * ``` + */ dragAndDrop(srcElement, destElement) { return this.browser.dragAndDrop( withStrictLocator(srcElement), @@ -977,8 +1028,8 @@ class WebDriverIO extends Helper { waitForText(text, sec, context) { sec = sec || this.options.waitForTimeout; context = context || 'body'; - return this.browser.waitUntil(function () { - return this.getText(context).then(function (source) { + return this.browser.waitUntil(function() { + return this.getText(context).then(function(source) { if (Array.isArray(source)) { return source.filter(part => part.indexOf(text) >= 0).length > 0; } @@ -1055,13 +1106,13 @@ function proceedSee(assertType, text, context) { } else { description = 'element ' + context; } - return this.browser.getText(withStrictLocator(context)).then(function (source) { + return this.browser.getText(withStrictLocator(context)).then(function(source) { return stringIncludes(description)[assertType](text, source); }); } function findClickable(client, locator) { - if (typeof (locator) === 'object') return client.elements(withStrictLocator(locator)); + if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); if (isCSSorXPathLocator(locator)) return client.elements(locator); let literal = xpathLocator.literal(locator); @@ -1072,7 +1123,7 @@ function findClickable(client, locator) { `.//a/img[normalize-space(@alt)=${literal}]/ancestor::a`, `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][normalize-space(@value)=${literal}]` ]); - return client.elements(narrowLocator).then(function (els) { + return client.elements(narrowLocator).then(function(els) { if (els.value.length) { return els; } @@ -1084,7 +1135,7 @@ function findClickable(client, locator) { `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][./@name = ${literal}]`, `.//button[./@name = ${literal}]` ]); - return client.elements(wideLocator).then(function (els) { + return client.elements(wideLocator).then(function(els) { if (els.value.length) { return els; } @@ -1094,7 +1145,7 @@ function findClickable(client, locator) { } function findFields(client, locator) { - if (typeof (locator) === 'object') return client.elements(withStrictLocator(locator)); + if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); if (isCSSorXPathLocator(locator)) return client.elements(locator); let literal = xpathLocator.literal(locator); @@ -1113,7 +1164,7 @@ function findFields(client, locator) { } function proceedSeeField(assertType, field, value) { - return findFields(this.browser, field).then(function (res) { + return findFields(this.browser, field).then(function(res) { if (!res.value || res.value.length === 0) { throw new Error(`Field ${field} not found by name|text|CSS|XPath`); } @@ -1127,7 +1178,9 @@ function proceedSeeField(assertType, field, value) { if (el.value === false) return; commands.push(this.elementIdAttribute(el.ELEMENT, 'value')); }); - this.unify(commands, { extractValue: true }).then((val) => { + this.unify(commands, { + extractValue: true + }).then((val) => { return stringIncludes('fields by ' + field)[assertType](value, val); }); }); @@ -1158,20 +1211,22 @@ function proceedSeeField(assertType, field, value) { } function proceedSeeCheckbox(assertType, field) { - return findFields(this.browser, field).then(function (res) { + return findFields(this.browser, field).then(function(res) { if (!res.value || res.value.length === 0) { throw new Error(`Field ${field} not found by name|text|CSS|XPath`); } let commands = []; res.value.forEach((el) => commands.push(this.elementIdSelected(el.ELEMENT))); - return this.unify(commands, { extractValue: true }).then((selected) => { + return this.unify(commands, { + extractValue: true + }).then((selected) => { return truth(`checkable field ${field}`, 'to be checked')[assertType](selected); }); }); } function findCheckable(client, locator) { - if (typeof (locator) === 'object') return client.elements(withStrictLocator(locator)); + if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); if (isCSSorXPathLocator(locator)) return client.elements(locator); let literal = xpathLocator.literal(locator); @@ -1179,10 +1234,10 @@ function findCheckable(client, locator) { `.//input[@type = 'checkbox' or @type = 'radio'][(@id = //label[contains(normalize-space(string(.)), ${literal})]/@for) or @placeholder = ${literal}]`, `.//label[contains(normalize-space(string(.)), ${literal})]//input[@type = 'radio' or @type = 'checkbox']` ]); - return client.elements(byText).then(function (els) { + return client.elements(byText).then(function(els) { if (els.value.length) return els; let byName = `.//input[@type = 'checkbox' or @type = 'radio'][@name = ${literal}]`; - return client.elements(byName).then(function (els) { + return client.elements(byName).then(function(els) { if (els.value.length) return els; return client.elements(locator); // by css or xpath }); @@ -1201,18 +1256,21 @@ function isCSSorXPathLocator(locator) { function withStrictLocator(locator) { if (!locator) return null; - if (typeof (locator) !== 'object') return locator; + if (typeof(locator) !== 'object') return locator; let key = Object.keys(locator)[0]; let value = locator[key]; locator.toString = () => `{${key}: '${value}'}`; switch (key) { - case 'by': - case 'xpath': - case 'css': return value; - case 'id': return '#' + value; - case 'name': return `[name="${value}"]`; + case 'by': + case 'xpath': + case 'css': + return value; + case 'id': + return '#' + value; + case 'name': + return `[name="${value}"]`; } } From 7d55e7f44e3d1d5d2fec7e6d70f7f17b12d42c55 Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 12:30:46 +0300 Subject: [PATCH 029/111] fix --- lib/helper/WebDriverIO.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index ccc14a9e7..f8c9bb0c1 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -223,7 +223,7 @@ class WebDriverIO extends Helper { `); } } else { - if (this.options.desiredCapabilities.platformName = "iOS" && (!this.options.desiredCapabilities.app || !this.options.desiredCapabilities.deviceName || !this.options.desiredCapabilities.platformVersion))) { + if ((this.options.desiredCapabilities.platformName = "iOS") && (!this.options.desiredCapabilities.app || !this.options.desiredCapabilities.deviceName || !this.options.desiredCapabilities.platformVersion))) { throw new Error(` WebDriverIO requires at least these parameters to run in Appium mode for iOS Check your codeceptjs config file to ensure these are set properly From 5383068876d6ce1a99dd18338413d4870e0cff19 Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 12:33:41 +0300 Subject: [PATCH 030/111] fix --- lib/helper/WebDriverIO.js | 1456 ++++++++++++++++++------------------- 1 file changed, 728 insertions(+), 728 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index f8c9bb0c1..8a62616e0 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -258,840 +258,840 @@ class WebDriverIO extends Helper { } `); } - } +} - static _checkRequirements() { - try { - requireg("webdriverio"); - } catch (e) { - return ["webdriverio"]; - } +static _checkRequirements() { + try { + requireg("webdriverio"); + } catch (e) { + return ["webdriverio"]; } +} - static _config() { - return [{ - name: 'url', - message: "Base url of site to be tested", - default: 'http://localhost' - }, { - name: 'browser', - message: 'Browser in which testing will be performed', - default: 'firefox' - }]; - } +static _config() { + return [{ + name: 'url', + message: "Base url of site to be tested", + default: 'http://localhost' + }, { + name: 'browser', + message: 'Browser in which testing will be performed', + default: 'firefox' + }]; +} - _beforeSuite() { - if (!this.options.restart && !this.options.manualStart) { - this.debugSection('Session', 'Starting singleton browser session'); - return this._startBrowser(); - } +_beforeSuite() { + if (!this.options.restart && !this.options.manualStart) { + this.debugSection('Session', 'Starting singleton browser session'); + return this._startBrowser(); } +} - _startBrowser() { - if (this.options.multiremote) { - this.browser = webdriverio.multiremote(this.options.multiremote).init(); - } else { - this.browser = webdriverio.remote(this.options).init(); - } - - if (this.options.timeouts) { - this.defineTimeout(this.options.timeouts); - } - - if (this.options.windowSize === 'maximize') { - this.browser.windowHandleMaximize(false); - } - if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0) { - let dimensions = this.options.windowSize.split('x'); - this.browser.windowHandleSize({ - width: dimensions[0], - height: dimensions[1] - }); - } - return this.browser; +_startBrowser() { + if (this.options.multiremote) { + this.browser = webdriverio.multiremote(this.options.multiremote).init(); + } else { + this.browser = webdriverio.remote(this.options).init(); } - _before() { - if (this.options.restart && !this.options.manualStart) this._startBrowser(); - this.failedTestName = null; - this.context = 'body'; - return this.browser; + if (this.options.timeouts) { + this.defineTimeout(this.options.timeouts); } - _after() { - if (this.options.restart) return this.browser.end(); - this.debugSection('Session', 'cleaning cookies and localStorage'); - return this.browser.execute('localStorage.clear();').then(() => { - return this.browser.deleteCookie(); + if (this.options.windowSize === 'maximize') { + this.browser.windowHandleMaximize(false); + } + if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0) { + let dimensions = this.options.windowSize.split('x'); + this.browser.windowHandleSize({ + width: dimensions[0], + height: dimensions[1] }); } + return this.browser; +} - _afterSuite() { - if (!this.options.restart) return this.browser.end(); - } +_before() { + if (this.options.restart && !this.options.manualStart) this._startBrowser(); + this.failedTestName = null; + this.context = 'body'; + return this.browser; +} - _failed(test) { - let fileName = test.title.replace(/ /g, '_') + '.failed.png'; - return this.saveScreenshot(fileName); - } +_after() { + if (this.options.restart) return this.browser.end(); + this.debugSection('Session', 'cleaning cookies and localStorage'); + return this.browser.execute('localStorage.clear();').then(() => { + return this.browser.deleteCookie(); + }); +} - _withinBegin(locator) { - withinStore.elFn = this.browser.element; - withinStore.elsFn = this.browser.elements; - this.context = locator; - return this.browser.element(withStrictLocator(locator)).then((res) => { - this.browser.element = function(l) { - return this.elementIdElement(res.value.ELEMENT, l); - }; - this.browser.elements = function(l) { - return this.elementIdElements(res.value.ELEMENT, l); - }; - }); - } +_afterSuite() { + if (!this.options.restart) return this.browser.end(); +} - _withinEnd() { - this.context = 'body'; - this.browser.element = withinStore.elFn; - this.browser.elements = withinStore.elsFn; - } +_failed(test) { + let fileName = test.title.replace(/ /g, '_') + '.failed.png'; + return this.saveScreenshot(fileName); +} - /** - * Get elements by different locator types, including strict locator - * Should be used in custom helpers: - * - * ```js - * this.helpers['WebDriverIO']._locate({name: 'password'}).then //... - * ``` - */ - _locate(locator) { - return this.browser.elements(withStrictLocator(locator)); - } +_withinBegin(locator) { + withinStore.elFn = this.browser.element; + withinStore.elsFn = this.browser.elements; + this.context = locator; + return this.browser.element(withStrictLocator(locator)).then((res) => { + this.browser.element = function(l) { + return this.elementIdElement(res.value.ELEMENT, l); + }; + this.browser.elements = function(l) { + return this.elementIdElements(res.value.ELEMENT, l); + }; + }); +} - /** - * Find a checkbox by providing human readable text: - * - * ```js - * this.helpers['WebDriverIO']._locateCheckable('I agree with terms and conditions').then // ... - * ``` - */ - _locateCheckable(locator) { - return findCheckable(this.browser, locator).then(function(res) { - return res.value; - }) - } +_withinEnd() { + this.context = 'body'; + this.browser.element = withinStore.elFn; + this.browser.elements = withinStore.elsFn; +} - /** - * Find a clickable element by providing human readable text: - * - * ```js - * this.helpers['WebDriverIO']._locateClickable('Next page').then // ... - * ``` - */ - _locateClickable(locator) { - return findClickable(this.browser, locator).then(function(res) { - return res.value; - }) - } +/** + * Get elements by different locator types, including strict locator + * Should be used in custom helpers: + * + * ```js + * this.helpers['WebDriverIO']._locate({name: 'password'}).then //... + * ``` + */ +_locate(locator) { + return this.browser.elements(withStrictLocator(locator)); +} - /** - * Find field elements by providing human readable text: - * - * ```js - * this.helpers['WebDriverIO']._locateFields('Your email').then // ... - * ``` - */ - _locateFields(locator) { - return findFields(this.browser, locator).then(function(res) { - return res.value; - }) - } +/** + * Find a checkbox by providing human readable text: + * + * ```js + * this.helpers['WebDriverIO']._locateCheckable('I agree with terms and conditions').then // ... + * ``` + */ +_locateCheckable(locator) { + return findCheckable(this.browser, locator).then(function(res) { + return res.value; + }) +} - /** - * Set [WebDriverIO timeouts](http://webdriver.io/guide/testrunner/timeouts.html) in realtime. - * Timeouts are expected to be passed as object: - * - * ```js - * I.defineTimeout({ script: 5000 }); - * I.defineTimeout({ implicit: 10000, "page load": 10000, script: 5000 }); - * ``` - */ - defineTimeout(timeouts) { - if (timeouts.implicit) { - this.browser.timeouts('implicit', timeouts.implicit); - } - if (timeouts['page load']) { - this.browser.timeouts('page load', timeouts['page load']); - } - if (timeouts.script) { - this.browser.timeouts('script', timeouts.script); - } - } +/** + * Find a clickable element by providing human readable text: + * + * ```js + * this.helpers['WebDriverIO']._locateClickable('Next page').then // ... + * ``` + */ +_locateClickable(locator) { + return findClickable(this.browser, locator).then(function(res) { + return res.value; + }) +} - /** - * {{> ../webapi/amOnPage }} - */ - amOnPage(url) { - return this.browser.url(url).url((err, res) => { - if (err) throw err; - this.debugSection('Url', res.value); - }); +/** + * Find field elements by providing human readable text: + * + * ```js + * this.helpers['WebDriverIO']._locateFields('Your email').then // ... + * ``` + */ +_locateFields(locator) { + return findFields(this.browser, locator).then(function(res) { + return res.value; + }) +} + +/** + * Set [WebDriverIO timeouts](http://webdriver.io/guide/testrunner/timeouts.html) in realtime. + * Timeouts are expected to be passed as object: + * + * ```js + * I.defineTimeout({ script: 5000 }); + * I.defineTimeout({ implicit: 10000, "page load": 10000, script: 5000 }); + * ``` + */ +defineTimeout(timeouts) { + if (timeouts.implicit) { + this.browser.timeouts('implicit', timeouts.implicit); } + if (timeouts['page load']) { + this.browser.timeouts('page load', timeouts['page load']); + } + if (timeouts.script) { + this.browser.timeouts('script', timeouts.script); + } +} - /** - * {{> ../webapi/click }} - */ - click(locator, context) { - let client = this.browser; - let clickMethod = this.browser.isMobile ? 'touchClick' : 'elementIdClick'; - if (context) { - client = client.element(context); +/** + * {{> ../webapi/amOnPage }} + */ +amOnPage(url) { + return this.browser.url(url).url((err, res) => { + if (err) throw err; + this.debugSection('Url', res.value); + }); +} + +/** + * {{> ../webapi/click }} + */ +click(locator, context) { + let client = this.browser; + let clickMethod = this.browser.isMobile ? 'touchClick' : 'elementIdClick'; + if (context) { + client = client.element(context); + } + return findClickable(client, locator).then(function(res) { + if (!res.value || res.value.length === 0) { + if (typeof(locator) === "object") locator = JSON.stringify(locator); + throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); } - return findClickable(client, locator).then(function(res) { - if (!res.value || res.value.length === 0) { - if (typeof(locator) === "object") locator = JSON.stringify(locator); - throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); - } - let elem = res.value[0]; - return this[clickMethod](elem.ELEMENT); - }); + let elem = res.value[0]; + return this[clickMethod](elem.ELEMENT); + }); +} + +/** + * {{> ../webapi/doubleClick }} + */ +doubleClick(locator, context) { + let client = this.browser; + if (context) { + client = client.element(context); } + return findClickable(client, locator).then(function(res) { + if (!res.value || res.value.length === 0) { + if (typeof(locator) === "object") locator = JSON.stringify(locator); + throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); + } + let elem = res.value[0]; + return this.moveTo(elem.ELEMENT).doDoubleClick(); + }); +} - /** - * {{> ../webapi/doubleClick }} - */ - doubleClick(locator, context) { - let client = this.browser; - if (context) { - client = client.element(context); +/** + * Performs right click on an element matched by CSS or XPath. + */ +rightClick(locator) { + return this.browser.rightClick(withStrictLocator(locator)); +} + +/** + * {{> ../webapi/fillField }} + */ +fillField(field, value) { + return findFields(this.browser, field).then(function(res) { + if (!res.value || res.value.length === 0) { + throw new Error(`Field ${field} not found by name|text|CSS|XPath`); } - return findClickable(client, locator).then(function(res) { - if (!res.value || res.value.length === 0) { - if (typeof(locator) === "object") locator = JSON.stringify(locator); - throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); - } - let elem = res.value[0]; - return this.moveTo(elem.ELEMENT).doDoubleClick(); - }); - } + let elem = res.value[0]; + return this.elementIdClear(elem.ELEMENT).elementIdValue(elem.ELEMENT, value); + }); +} - /** - * Performs right click on an element matched by CSS or XPath. - */ - rightClick(locator) { - return this.browser.rightClick(withStrictLocator(locator)); - } +/** + * {{> ../webapi/appendField }} + */ +appendField(field, value) { + return findFields(this.browser, field).then(function(res) { + if (!res.value || res.value.length === 0) { + throw new Error(`Field ${field} not found by name|text|CSS|XPath`); + } + let elem = res.value[0]; + return this.elementIdValue(elem.ELEMENT, value); + }); +} - /** - * {{> ../webapi/fillField }} - */ - fillField(field, value) { - return findFields(this.browser, field).then(function(res) { - if (!res.value || res.value.length === 0) { - throw new Error(`Field ${field} not found by name|text|CSS|XPath`); - } - let elem = res.value[0]; - return this.elementIdClear(elem.ELEMENT).elementIdValue(elem.ELEMENT, value); - }); - } +/** + * {{> ../webapi/selectOption}} + * + */ +selectOption(select, option) { + return findFields(this.browser, select).then(function(res) { + if (!res.value || res.value.length === 0) { + throw new Error(`Selectable field ${select} not found by name|text|CSS|XPath`); + } + let elem = res.value[0]; - /** - * {{> ../webapi/appendField }} - */ - appendField(field, value) { - return findFields(this.browser, field).then(function(res) { - if (!res.value || res.value.length === 0) { - throw new Error(`Field ${field} not found by name|text|CSS|XPath`); - } - let elem = res.value[0]; - return this.elementIdValue(elem.ELEMENT, value); - }); - } + let normalized, byVisibleText; + let commands = []; - /** - * {{> ../webapi/selectOption}} - * - */ - selectOption(select, option) { - return findFields(this.browser, select).then(function(res) { - if (!res.value || res.value.length === 0) { - throw new Error(`Selectable field ${select} not found by name|text|CSS|XPath`); - } - let elem = res.value[0]; + if (!Array.isArray(option)) { + option = [option]; + } - let normalized, byVisibleText; - let commands = []; + option.forEach((opt) => { + normalized = `[normalize-space(.) = "${opt.trim() }"]`; + byVisibleText = `./option${normalized}|./optgroup/option${normalized}`; + commands.push(this.elementIdElements(elem.ELEMENT, byVisibleText)); + }); + return this.unify(commands, { + extractValue: true + }).then((els) => { + commands = []; + let clickOptionFn = (el) => { + if (el[0]) el = el[0]; + if (el && el.ELEMENT) commands.push(this.elementIdClick(el.ELEMENT)); + }; - if (!Array.isArray(option)) { - option = [option]; + if (els.length) { + els.forEach(clickOptionFn); + return this.unify(commands); } + let normalized, byValue; option.forEach((opt) => { - normalized = `[normalize-space(.) = "${opt.trim() }"]`; - byVisibleText = `./option${normalized}|./optgroup/option${normalized}`; - commands.push(this.elementIdElements(elem.ELEMENT, byVisibleText)); + normalized = `[normalize-space(@value) = "${opt.trim() }"]`; + byValue = `./option${normalized}|./optgroup/option${normalized}`; + commands.push(this.elementIdElements(elem.ELEMENT, byValue)); }); + // try by value return this.unify(commands, { extractValue: true }).then((els) => { - commands = []; - let clickOptionFn = (el) => { - if (el[0]) el = el[0]; - if (el && el.ELEMENT) commands.push(this.elementIdClick(el.ELEMENT)); - }; - - if (els.length) { - els.forEach(clickOptionFn); - return this.unify(commands); - } - let normalized, byValue; - - option.forEach((opt) => { - normalized = `[normalize-space(@value) = "${opt.trim() }"]`; - byValue = `./option${normalized}|./optgroup/option${normalized}`; - commands.push(this.elementIdElements(elem.ELEMENT, byValue)); - }); - // try by value - return this.unify(commands, { - extractValue: true - }).then((els) => { - if (els.length === 0) { - throw new Error(`Option ${option} in ${select} was found neither by visible text not by value`); - } - commands = []; - els.forEach(clickOptionFn); - return this.unify(commands); - }); - }); - }); - } - - /** - * {{> ../webapi/attachFile }} - */ - attachFile(locator, pathToFile) { - let file = path.join(global.codecept_dir, pathToFile); - if (!fileExists(file)) { - throw new Error(`File at ${file} can not be found on local system`); - } - return findFields(this.browser, locator).then((el) => { - this.debug("Uploading " + file); - return this.browser.uploadFile(file).then((res) => { - if (!el.value || el.value.length === 0) { - throw new Error(`File field ${locator} not found by name|text|CSS|XPath`); + if (els.length === 0) { + throw new Error(`Option ${option} in ${select} was found neither by visible text not by value`); } - return this.browser.elementIdValue(el.value[0].ELEMENT, res.value); + commands = []; + els.forEach(clickOptionFn); + return this.unify(commands); }); }); - } + }); +} - /** - * {{> ../webapi/checkOption }} - */ - checkOption(field, context) { - let client = this.browser; - let clickMethod = this.browser.isMobile ? 'touchClick' : 'elementIdClick'; - if (context) { - client = client.element(withStrictLocator(context)); - } - return findCheckable(client, field).then((res) => { - if (!res.value || res.value.length === 0) { - throw new Error(`Checkable ${field} cant be located by name|text|CSS|XPath`); +/** + * {{> ../webapi/attachFile }} + */ +attachFile(locator, pathToFile) { + let file = path.join(global.codecept_dir, pathToFile); + if (!fileExists(file)) { + throw new Error(`File at ${file} can not be found on local system`); + } + return findFields(this.browser, locator).then((el) => { + this.debug("Uploading " + file); + return this.browser.uploadFile(file).then((res) => { + if (!el.value || el.value.length === 0) { + throw new Error(`File field ${locator} not found by name|text|CSS|XPath`); } - let elem = res.value[0]; - return client.elementIdSelected(elem.ELEMENT).then(function(isSelected) { - if (isSelected.value) return true; - return this[clickMethod](elem.ELEMENT); - }); + return this.browser.elementIdValue(el.value[0].ELEMENT, res.value); }); - } + }); +} - /** - * {{> ../webapi/grabTextFrom }} - */ - grabTextFrom(locator) { - return this.browser.getText(withStrictLocator(locator)).then(function(text) { - return text; - }); +/** + * {{> ../webapi/checkOption }} + */ +checkOption(field, context) { + let client = this.browser; + let clickMethod = this.browser.isMobile ? 'touchClick' : 'elementIdClick'; + if (context) { + client = client.element(withStrictLocator(context)); } - - /** - * Retrieves the innerHTML from an element located by CSS or XPath and returns it to test. - * Resumes test execution, so **should be used inside a generator with `yield`** operator. - * - * ```js - * let postHTML = yield I.grabHTMLFrom('#post'); - * ``` - */ - grabHTMLFrom(locator) { - return this.browser.getHTML(withStrictLocator(locator)).then(function(html) { - return html; + return findCheckable(client, field).then((res) => { + if (!res.value || res.value.length === 0) { + throw new Error(`Checkable ${field} cant be located by name|text|CSS|XPath`); + } + let elem = res.value[0]; + return client.elementIdSelected(elem.ELEMENT).then(function(isSelected) { + if (isSelected.value) return true; + return this[clickMethod](elem.ELEMENT); }); - } + }); +} - /** - * {{> ../webapi/grabValueFrom }} - */ - grabValueFrom(locator) { - return this.browser.getValue(withStrictLocator(locator)).then(function(text) { - return text; - }); - } +/** + * {{> ../webapi/grabTextFrom }} + */ +grabTextFrom(locator) { + return this.browser.getText(withStrictLocator(locator)).then(function(text) { + return text; + }); +} - /** - * {{> ../webapi/grabAttributeFrom }} - */ - grabAttributeFrom(locator, attr) { - return this.browser.getAttribute(withStrictLocator(locator), attr).then(function(text) { - return text; - }); - } +/** + * Retrieves the innerHTML from an element located by CSS or XPath and returns it to test. + * Resumes test execution, so **should be used inside a generator with `yield`** operator. + * + * ```js + * let postHTML = yield I.grabHTMLFrom('#post'); + * ``` + */ +grabHTMLFrom(locator) { + return this.browser.getHTML(withStrictLocator(locator)).then(function(html) { + return html; + }); +} - /** - * {{> ../webapi/seeInTitle }} - */ - seeInTitle(text) { - return this.browser.getTitle().then((title) => { - return stringIncludes('web page title').assert(text, title); - }); - } +/** + * {{> ../webapi/grabValueFrom }} + */ +grabValueFrom(locator) { + return this.browser.getValue(withStrictLocator(locator)).then(function(text) { + return text; + }); +} - /** - * {{> ../webapi/dontSeeInTitle }} - */ - dontSeeInTitle(text) { - return this.browser.getTitle().then((title) => { - return stringIncludes('web page title').negate(text, title); - }); - } +/** + * {{> ../webapi/grabAttributeFrom }} + */ +grabAttributeFrom(locator, attr) { + return this.browser.getAttribute(withStrictLocator(locator), attr).then(function(text) { + return text; + }); +} - /** - * {{> ../webapi/grabTitle }} - */ - grabTitle() { - return this.browser.getTitle().then((title) => { - this.debugSection('Title', title); - return title; - }); - } +/** + * {{> ../webapi/seeInTitle }} + */ +seeInTitle(text) { + return this.browser.getTitle().then((title) => { + return stringIncludes('web page title').assert(text, title); + }); +} - /** - * {{> ../webapi/see }} - */ - see(text, context) { - return proceedSee.call(this, 'assert', text, context); - } +/** + * {{> ../webapi/dontSeeInTitle }} + */ +dontSeeInTitle(text) { + return this.browser.getTitle().then((title) => { + return stringIncludes('web page title').negate(text, title); + }); +} - /** - * {{> ../webapi/dontSee }} - */ - dontSee(text, context) { - return proceedSee.call(this, 'negate', text, context); - } +/** + * {{> ../webapi/grabTitle }} + */ +grabTitle() { + return this.browser.getTitle().then((title) => { + this.debugSection('Title', title); + return title; + }); +} - /** - * {{> ../webapi/seeInField }} - */ - seeInField(field, value) { - return proceedSeeField.call(this, 'assert', field, value); - } +/** + * {{> ../webapi/see }} + */ +see(text, context) { + return proceedSee.call(this, 'assert', text, context); +} - /** - * {{> ../webapi/dontSeeInField }} - */ - dontSeeInField(field, value) { - return proceedSeeField.call(this, 'negate', field, value); - } +/** + * {{> ../webapi/dontSee }} + */ +dontSee(text, context) { + return proceedSee.call(this, 'negate', text, context); +} - /** - * {{> ../webapi/seeCheckboxIsChecked }} - */ - seeCheckboxIsChecked(field) { - return proceedSeeCheckbox.call(this, 'assert', field); - } +/** + * {{> ../webapi/seeInField }} + */ +seeInField(field, value) { + return proceedSeeField.call(this, 'assert', field, value); +} - /** - * {{> ../webapi/dontSeeCheckboxIsChecked }} - */ - dontSeeCheckboxIsChecked(field) { - return proceedSeeCheckbox.call(this, 'negate', field); - } +/** + * {{> ../webapi/dontSeeInField }} + */ +dontSeeInField(field, value) { + return proceedSeeField.call(this, 'negate', field, value); +} - /** - * {{> ../webapi/seeElement }} - */ - seeElement(locator) { - return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { - return truth(`elements of ${locator}`, 'to be seen').assert(res); - }); - } +/** + * {{> ../webapi/seeCheckboxIsChecked }} + */ +seeCheckboxIsChecked(field) { + return proceedSeeCheckbox.call(this, 'assert', field); +} - /** - * {{> ../webapi/dontSeeElement}} - */ - dontSeeElement(locator) { - return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { - return truth(`elements of ${locator}`, 'to be seen').negate(res); - }); - } +/** + * {{> ../webapi/dontSeeCheckboxIsChecked }} + */ +dontSeeCheckboxIsChecked(field) { + return proceedSeeCheckbox.call(this, 'negate', field); +} - /** - * {{> ../webapi/seeElementInDOM }} - */ - seeElementInDOM(locator) { - return this.browser.elements(withStrictLocator(locator)).then(function(res) { - return empty('elements').negate(res.value); - }); - } +/** + * {{> ../webapi/seeElement }} + */ +seeElement(locator) { + return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { + return truth(`elements of ${locator}`, 'to be seen').assert(res); + }); +} - /** - * {{> ../webapi/dontSeeElementInDOM }} - */ - dontSeeElementInDOM(locator) { - return this.browser.elements(withStrictLocator(locator)).then(function(res) { - return empty('elements').assert(res.value); - }); - } +/** + * {{> ../webapi/dontSeeElement}} + */ +dontSeeElement(locator) { + return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { + return truth(`elements of ${locator}`, 'to be seen').negate(res); + }); +} - /** - * {{> ../webapi/seeInSource }} - */ - seeInSource(text) { - return this.browser.getSource().then((source) => { - return stringIncludes('HTML source of a page').assert(text, source); - }); - } +/** + * {{> ../webapi/seeElementInDOM }} + */ +seeElementInDOM(locator) { + return this.browser.elements(withStrictLocator(locator)).then(function(res) { + return empty('elements').negate(res.value); + }); +} - /** - * {{> ../webapi/dontSeeInSource }} - */ - dontSeeInSource(text) { - return this.browser.getSource().then((source) => { - return stringIncludes('HTML source of a page').negate(text, source); - }); - } +/** + * {{> ../webapi/dontSeeElementInDOM }} + */ +dontSeeElementInDOM(locator) { + return this.browser.elements(withStrictLocator(locator)).then(function(res) { + return empty('elements').assert(res.value); + }); +} - /** - * asserts that an element appears a given number of times in the DOM - * Element is located by label or name or CSS or XPath. - * - * ```js - * I.seeNumberOfElements('#submitBtn', 1); - * ``` - */ - seeNumberOfElements(selector, num) { - return this.browser.elements(withStrictLocator(selector)) - .then(function(res) { - return assert.equal(res.value.length, num); - }); - } +/** + * {{> ../webapi/seeInSource }} + */ +seeInSource(text) { + return this.browser.getSource().then((source) => { + return stringIncludes('HTML source of a page').assert(text, source); + }); +} - /** - * {{> ../webapi/seeInCurrentUrl }} - */ - seeInCurrentUrl(url) { - return this.browser.url().then(function(res) { - return stringIncludes('url').assert(url, res.value); - }); - } +/** + * {{> ../webapi/dontSeeInSource }} + */ +dontSeeInSource(text) { + return this.browser.getSource().then((source) => { + return stringIncludes('HTML source of a page').negate(text, source); + }); +} - /** - * {{> ../webapi/dontSeeInCurrentUrl }} - */ - dontSeeInCurrentUrl(url) { - return this.browser.url().then(function(res) { - return stringIncludes('url').negate(url, res.value); +/** + * asserts that an element appears a given number of times in the DOM + * Element is located by label or name or CSS or XPath. + * + * ```js + * I.seeNumberOfElements('#submitBtn', 1); + * ``` + */ +seeNumberOfElements(selector, num) { + return this.browser.elements(withStrictLocator(selector)) + .then(function(res) { + return assert.equal(res.value.length, num); }); - } +} - /** - * {{> ../webapi/seeCurrentUrlEquals }} - */ - seeCurrentUrlEquals(url) { - return this.browser.url().then((res) => { - return urlEquals(this.options.url).assert(url, res.value); - }); - } +/** + * {{> ../webapi/seeInCurrentUrl }} + */ +seeInCurrentUrl(url) { + return this.browser.url().then(function(res) { + return stringIncludes('url').assert(url, res.value); + }); +} - /** - * {{> ../webapi/dontSeeCurrentUrlEquals }} - */ - dontSeeCurrentUrlEquals(url) { - return this.browser.url().then((res) => { - return urlEquals(this.options.url).negate(url, res.value); - }); - } +/** + * {{> ../webapi/dontSeeInCurrentUrl }} + */ +dontSeeInCurrentUrl(url) { + return this.browser.url().then(function(res) { + return stringIncludes('url').negate(url, res.value); + }); +} - /** - * {{> ../webapi/executeScript }} - * - * Wraps [execute](http://webdriver.io/api/protocol/execute.html) command. - */ - executeScript(fn) { - return this.browser.execute.apply(this.browser, arguments).then((res) => res.value); - } +/** + * {{> ../webapi/seeCurrentUrlEquals }} + */ +seeCurrentUrlEquals(url) { + return this.browser.url().then((res) => { + return urlEquals(this.options.url).assert(url, res.value); + }); +} - /** - * {{> ../webapi/executeAsyncScript }} - */ - executeAsyncScript(fn) { - return this.browser.executeAsync.apply(this.browser, arguments).then((res) => res.value); - } +/** + * {{> ../webapi/dontSeeCurrentUrlEquals }} + */ +dontSeeCurrentUrlEquals(url) { + return this.browser.url().then((res) => { + return urlEquals(this.options.url).negate(url, res.value); + }); +} - /** - * Scrolls to element matched by locator. - * Extra shift can be set with offsetX and offsetY options - * - * ```js - * I.scrollTo('footer'); - * I.scrollTo('#submit', 5,5); - * ``` - */ - scrollTo(locator, offsetX, offsetY) { - return this.browser.scroll(withStrictLocator(locator), offsetX, offsetY); - } +/** + * {{> ../webapi/executeScript }} + * + * Wraps [execute](http://webdriver.io/api/protocol/execute.html) command. + */ +executeScript(fn) { + return this.browser.execute.apply(this.browser, arguments).then((res) => res.value); +} - /** - * {{> ../webapi/moveCursorTo}} - */ - moveCursorTo(locator, offsetX, offsetY) { - return this.browser.moveToObject(withStrictLocator(locator), offsetX, offsetY); - } +/** + * {{> ../webapi/executeAsyncScript }} + */ +executeAsyncScript(fn) { + return this.browser.executeAsync.apply(this.browser, arguments).then((res) => res.value); +} - /** - * {{> ../webapi/saveScreenshot}} - */ - saveScreenshot(fileName) { - let outputFile = path.join(global.output_dir, fileName); - this.debug('Screenshot has been saved to ' + outputFile); - return this.browser.saveScreenshot(outputFile); - } +/** + * Scrolls to element matched by locator. + * Extra shift can be set with offsetX and offsetY options + * + * ```js + * I.scrollTo('footer'); + * I.scrollTo('#submit', 5,5); + * ``` + */ +scrollTo(locator, offsetX, offsetY) { + return this.browser.scroll(withStrictLocator(locator), offsetX, offsetY); +} - /** - * {{> ../webapi/setCookie}} - * - * Uses Selenium's JSON [cookie format](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object). - */ - setCookie(cookie) { - return this.browser.setCookie(cookie); - } +/** + * {{> ../webapi/moveCursorTo}} + */ +moveCursorTo(locator, offsetX, offsetY) { + return this.browser.moveToObject(withStrictLocator(locator), offsetX, offsetY); +} - /** - * {{> ../webapi/clearCookie}} - */ - clearCookie(cookie) { - return this.browser.deleteCookie(cookie); - } +/** + * {{> ../webapi/saveScreenshot}} + */ +saveScreenshot(fileName) { + let outputFile = path.join(global.output_dir, fileName); + this.debug('Screenshot has been saved to ' + outputFile); + return this.browser.saveScreenshot(outputFile); +} - /** - * {{> ../webapi/clearField}} - */ - clearField(locator) { - return this.browser.clearElement(withStrictLocator(locator)); - } +/** + * {{> ../webapi/setCookie}} + * + * Uses Selenium's JSON [cookie format](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object). + */ +setCookie(cookie) { + return this.browser.setCookie(cookie); +} - /** - * {{> ../webapi/seeCookie}} - */ - seeCookie(name) { - return this.browser.getCookie(name).then(function(res) { - return truth('cookie ' + name, 'to be set').assert(res); - }); - } +/** + * {{> ../webapi/clearCookie}} + */ +clearCookie(cookie) { + return this.browser.deleteCookie(cookie); +} - /** - * {{> ../webapi/dontSeeCookie}} - */ - dontSeeCookie(name) { - return this.browser.getCookie(name).then(function(res) { - return truth('cookie ' + name, 'to be set').negate(res); - }); - } +/** + * {{> ../webapi/clearField}} + */ +clearField(locator) { + return this.browser.clearElement(withStrictLocator(locator)); +} - /** - * {{> ../webapi/grabCookie}} - */ - grabCookie(name) { - return this.browser.getCookie(name); - } +/** + * {{> ../webapi/seeCookie}} + */ +seeCookie(name) { + return this.browser.getCookie(name).then(function(res) { + return truth('cookie ' + name, 'to be set').assert(res); + }); +} - /** - * Accepts the active JavaScript native popup window, as created by window.alert|window.confirm|window.prompt. - * Don't confuse popups with modal windows, as created by [various libraries](http://jster.net/category/windows-modals-popups). - */ - acceptPopup() { - return this.browser.alertText().then(function(res) { - if (res !== null) { - return this.alertAccept(); - } - }); - } +/** + * {{> ../webapi/dontSeeCookie}} + */ +dontSeeCookie(name) { + return this.browser.getCookie(name).then(function(res) { + return truth('cookie ' + name, 'to be set').negate(res); + }); +} - /** - * Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt. - */ - cancelPopup() { - return this.browser.alertText().then(function(res) { - if (res !== null) { - return this.alertDismiss(); - } - }); - } +/** + * {{> ../webapi/grabCookie}} + */ +grabCookie(name) { + return this.browser.getCookie(name); +} - /** - * Checks that the active JavaScript popup, as created by `window.alert|window.confirm|window.prompt`, contains the given string. - */ - seeInPopup(text) { - return this.browser.alertText().then(function(res) { - if (res === null) { - throw new Error('Popup is not opened'); - } - stringIncludes('text in popup').assert(text, res); - }); - } +/** + * Accepts the active JavaScript native popup window, as created by window.alert|window.confirm|window.prompt. + * Don't confuse popups with modal windows, as created by [various libraries](http://jster.net/category/windows-modals-popups). + */ +acceptPopup() { + return this.browser.alertText().then(function(res) { + if (res !== null) { + return this.alertAccept(); + } + }); +} - /** - * {{> ../webapi/pressKey }} - * - * To make combinations with modifier and mouse clicks (like Ctrl+Click) press a modifier, click, then release it. - * - * ```js - * I.pressKey('Control'); - * I.click('#someelement'); - * I.pressKey('Control'); - * ``` - */ - pressKey(key) { - let modifier; - if (Array.isArray(key) && ~['Control', 'Command', 'Shift', 'Alt'].indexOf(key[0])) { - modifier = key[0]; +/** + * Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt. + */ +cancelPopup() { + return this.browser.alertText().then(function(res) { + if (res !== null) { + return this.alertDismiss(); } - return this.browser.keys(key).then(function() { - if (!modifier) return true; - return this.keys(modifier); // release modifeier - }); - } + }); +} - /** - * {{> ../webapi/resizeWindow }} - */ - resizeWindow(width, height) { - if (width === 'maximize') { - return this.browser.windowHandleMaximize(false); +/** + * Checks that the active JavaScript popup, as created by `window.alert|window.confirm|window.prompt`, contains the given string. + */ +seeInPopup(text) { + return this.browser.alertText().then(function(res) { + if (res === null) { + throw new Error('Popup is not opened'); } - return this.browser.windowHandleSize({ - width, - height - }); - } + stringIncludes('text in popup').assert(text, res); + }); +} - /** - * Drag an item to a destination element. - * - * ```js - * I.dragAndDrop('#dragHandle', '#container'); - * ``` - */ - dragAndDrop(srcElement, destElement) { - return this.browser.dragAndDrop( - withStrictLocator(srcElement), - withStrictLocator(destElement) - ); - } +/** + * {{> ../webapi/pressKey }} + * + * To make combinations with modifier and mouse clicks (like Ctrl+Click) press a modifier, click, then release it. + * + * ```js + * I.pressKey('Control'); + * I.click('#someelement'); + * I.pressKey('Control'); + * ``` + */ +pressKey(key) { + let modifier; + if (Array.isArray(key) && ~['Control', 'Command', 'Shift', 'Alt'].indexOf(key[0])) { + modifier = key[0]; + } + return this.browser.keys(key).then(function() { + if (!modifier) return true; + return this.keys(modifier); // release modifeier + }); +} - /** - * {{> ../webapi/wait }} - */ - wait(sec) { - return this.browser.pause(sec * 1000); +/** + * {{> ../webapi/resizeWindow }} + */ +resizeWindow(width, height) { + if (width === 'maximize') { + return this.browser.windowHandleMaximize(false); } + return this.browser.windowHandleSize({ + width, + height + }); +} - /** - * {{> ../webapi/waitForEnabled }} - */ - waitForEnabled(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForEnabled(withStrictLocator(locator), sec * 1000); - } +/** + * Drag an item to a destination element. + * + * ```js + * I.dragAndDrop('#dragHandle', '#container'); + * ``` + */ +dragAndDrop(srcElement, destElement) { + return this.browser.dragAndDrop( + withStrictLocator(srcElement), + withStrictLocator(destElement) + ); +} - /** - * {{> ../webapi/waitForElement }} - */ - waitForElement(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForExist(withStrictLocator(locator), sec * 1000); - } +/** + * {{> ../webapi/wait }} + */ +wait(sec) { + return this.browser.pause(sec * 1000); +} - /** - * {{> ../webapi/waitForText }} - */ - waitForText(text, sec, context) { - sec = sec || this.options.waitForTimeout; - context = context || 'body'; - return this.browser.waitUntil(function() { - return this.getText(context).then(function(source) { - if (Array.isArray(source)) { - return source.filter(part => part.indexOf(text) >= 0).length > 0; - } - return source.indexOf(text) >= 0; - }); - }, sec * 1000) - .catch((e) => { - if (e.type === 'WaitUntilTimeoutError') { - return proceedSee.call(this, 'assert', text, context); - } else { - throw e; +/** + * {{> ../webapi/waitForEnabled }} + */ +waitForEnabled(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForEnabled(withStrictLocator(locator), sec * 1000); +} + +/** + * {{> ../webapi/waitForElement }} + */ +waitForElement(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForExist(withStrictLocator(locator), sec * 1000); +} + +/** + * {{> ../webapi/waitForText }} + */ +waitForText(text, sec, context) { + sec = sec || this.options.waitForTimeout; + context = context || 'body'; + return this.browser.waitUntil(function() { + return this.getText(context).then(function(source) { + if (Array.isArray(source)) { + return source.filter(part => part.indexOf(text) >= 0).length > 0; } + return source.indexOf(text) >= 0; }); - } + }, sec * 1000) + .catch((e) => { + if (e.type === 'WaitUntilTimeoutError') { + return proceedSee.call(this, 'assert', text, context); + } else { + throw e; + } + }); +} - /** - * {{> ../webapi/waitForVisible }} - */ - waitForVisible(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForVisible(withStrictLocator(locator), sec * 1000); - } +/** + * {{> ../webapi/waitForVisible }} + */ +waitForVisible(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForVisible(withStrictLocator(locator), sec * 1000); +} - /** - * {{> ../webapi/waitForInvisible }} - */ - waitForInvisible(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForVisible(withStrictLocator(locator), sec * 1000, true); - } +/** + * {{> ../webapi/waitForInvisible }} + */ +waitForInvisible(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForVisible(withStrictLocator(locator), sec * 1000, true); +} - /** - * Waits for an element to become invisible on a page (by default waits for 1sec). - * Element can be located by CSS or XPath. - */ - waitToHide(locator, sec) { - return this.waitForInvisible(locator, sec); - } +/** + * Waits for an element to become invisible on a page (by default waits for 1sec). + * Element can be located by CSS or XPath. + */ +waitToHide(locator, sec) { + return this.waitForInvisible(locator, sec); +} - /** - * {{> ../webapi/waitForStalenessOf }} - */ - waitForStalenessOf(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForExist(withStrictLocator(locator), sec * 1000, true); - } +/** + * {{> ../webapi/waitForStalenessOf }} + */ +waitForStalenessOf(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForExist(withStrictLocator(locator), sec * 1000, true); +} - /** - * Waits for a function to return true (waits for 1sec by default). - */ - waitUntil(fn, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitUntil(fn, sec); - } +/** + * Waits for a function to return true (waits for 1sec by default). + */ +waitUntil(fn, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitUntil(fn, sec); +} - /** - * Switches frame or in case of null locator reverts to parent. - */ - switchTo(locator) { - locator = locator || null; - return this.browser.frame(locator); - } +/** + * Switches frame or in case of null locator reverts to parent. + */ +switchTo(locator) { + locator = locator || null; + return this.browser.frame(locator); +} } function proceedSee(assertType, text, context) { From cbf3c4c5fc87218c851fa09db117c4fad256668c Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 12:37:31 +0300 Subject: [PATCH 031/111] fix --- lib/helper/WebDriverIO.js | 1815 ++++++++++++++++++------------------- 1 file changed, 907 insertions(+), 908 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 8a62616e0..ed39073dd 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -223,8 +223,8 @@ class WebDriverIO extends Helper { `); } } else { - if ((this.options.desiredCapabilities.platformName = "iOS") && (!this.options.desiredCapabilities.app || !this.options.desiredCapabilities.deviceName || !this.options.desiredCapabilities.platformVersion))) { - throw new Error(` + if ((this.options.desiredCapabilities.platformName = "iOS") && (!this.options.desiredCapabilities.app || !this.options.desiredCapabilities.deviceName || !this.options.desiredCapabilities.platformVersion)) { + throw new Error(` WebDriverIO requires at least these parameters to run in Appium mode for iOS Check your codeceptjs config file to ensure these are set properly { @@ -240,8 +240,8 @@ class WebDriverIO extends Helper { } } `); - } else if ((this.options.desiredCapabilities.platformName = "Android") && (!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion)) { - throw new Error(` + } else if ((this.options.desiredCapabilities.platformName = "Android") && (!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion)) { + throw new Error(` WebDriverIO requires at least these parameters to run in Appium mode for Android Check your codeceptjs config file to ensure these are set properly { @@ -257,1021 +257,1020 @@ class WebDriverIO extends Helper { } } `); + } } - } -} - -static _checkRequirements() { - try { - requireg("webdriverio"); - } catch (e) { - return ["webdriverio"]; - } -} - -static _config() { - return [{ - name: 'url', - message: "Base url of site to be tested", - default: 'http://localhost' - }, { - name: 'browser', - message: 'Browser in which testing will be performed', - default: 'firefox' - }]; -} - -_beforeSuite() { - if (!this.options.restart && !this.options.manualStart) { - this.debugSection('Session', 'Starting singleton browser session'); - return this._startBrowser(); - } -} -_startBrowser() { - if (this.options.multiremote) { - this.browser = webdriverio.multiremote(this.options.multiremote).init(); - } else { - this.browser = webdriverio.remote(this.options).init(); - } - - if (this.options.timeouts) { - this.defineTimeout(this.options.timeouts); - } - - if (this.options.windowSize === 'maximize') { - this.browser.windowHandleMaximize(false); - } - if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0) { - let dimensions = this.options.windowSize.split('x'); - this.browser.windowHandleSize({ - width: dimensions[0], - height: dimensions[1] - }); - } - return this.browser; -} - -_before() { - if (this.options.restart && !this.options.manualStart) this._startBrowser(); - this.failedTestName = null; - this.context = 'body'; - return this.browser; -} - -_after() { - if (this.options.restart) return this.browser.end(); - this.debugSection('Session', 'cleaning cookies and localStorage'); - return this.browser.execute('localStorage.clear();').then(() => { - return this.browser.deleteCookie(); - }); -} - -_afterSuite() { - if (!this.options.restart) return this.browser.end(); -} - -_failed(test) { - let fileName = test.title.replace(/ /g, '_') + '.failed.png'; - return this.saveScreenshot(fileName); -} - -_withinBegin(locator) { - withinStore.elFn = this.browser.element; - withinStore.elsFn = this.browser.elements; - this.context = locator; - return this.browser.element(withStrictLocator(locator)).then((res) => { - this.browser.element = function(l) { - return this.elementIdElement(res.value.ELEMENT, l); - }; - this.browser.elements = function(l) { - return this.elementIdElements(res.value.ELEMENT, l); - }; - }); -} + static _checkRequirements() { + try { + requireg("webdriverio"); + } catch (e) { + return ["webdriverio"]; + } + } -_withinEnd() { - this.context = 'body'; - this.browser.element = withinStore.elFn; - this.browser.elements = withinStore.elsFn; -} + static _config() { + return [{ + name: 'url', + message: "Base url of site to be tested", + default: 'http://localhost' + }, { + name: 'browser', + message: 'Browser in which testing will be performed', + default: 'firefox' + }]; + } -/** - * Get elements by different locator types, including strict locator - * Should be used in custom helpers: - * - * ```js - * this.helpers['WebDriverIO']._locate({name: 'password'}).then //... - * ``` - */ -_locate(locator) { - return this.browser.elements(withStrictLocator(locator)); -} + _beforeSuite() { + if (!this.options.restart && !this.options.manualStart) { + this.debugSection('Session', 'Starting singleton browser session'); + return this._startBrowser(); + } + } -/** - * Find a checkbox by providing human readable text: - * - * ```js - * this.helpers['WebDriverIO']._locateCheckable('I agree with terms and conditions').then // ... - * ``` - */ -_locateCheckable(locator) { - return findCheckable(this.browser, locator).then(function(res) { - return res.value; - }) -} + _startBrowser() { + if (this.options.multiremote) { + this.browser = webdriverio.multiremote(this.options.multiremote).init(); + } else { + this.browser = webdriverio.remote(this.options).init(); + } -/** - * Find a clickable element by providing human readable text: - * - * ```js - * this.helpers['WebDriverIO']._locateClickable('Next page').then // ... - * ``` - */ -_locateClickable(locator) { - return findClickable(this.browser, locator).then(function(res) { - return res.value; - }) -} + if (this.options.timeouts) { + this.defineTimeout(this.options.timeouts); + } -/** - * Find field elements by providing human readable text: - * - * ```js - * this.helpers['WebDriverIO']._locateFields('Your email').then // ... - * ``` - */ -_locateFields(locator) { - return findFields(this.browser, locator).then(function(res) { - return res.value; - }) -} + if (this.options.windowSize === 'maximize') { + this.browser.windowHandleMaximize(false); + } + if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0) { + let dimensions = this.options.windowSize.split('x'); + this.browser.windowHandleSize({ + width: dimensions[0], + height: dimensions[1] + }); + } + return this.browser; + } -/** - * Set [WebDriverIO timeouts](http://webdriver.io/guide/testrunner/timeouts.html) in realtime. - * Timeouts are expected to be passed as object: - * - * ```js - * I.defineTimeout({ script: 5000 }); - * I.defineTimeout({ implicit: 10000, "page load": 10000, script: 5000 }); - * ``` - */ -defineTimeout(timeouts) { - if (timeouts.implicit) { - this.browser.timeouts('implicit', timeouts.implicit); - } - if (timeouts['page load']) { - this.browser.timeouts('page load', timeouts['page load']); - } - if (timeouts.script) { - this.browser.timeouts('script', timeouts.script); - } -} + _before() { + if (this.options.restart && !this.options.manualStart) this._startBrowser(); + this.failedTestName = null; + this.context = 'body'; + return this.browser; + } -/** - * {{> ../webapi/amOnPage }} - */ -amOnPage(url) { - return this.browser.url(url).url((err, res) => { - if (err) throw err; - this.debugSection('Url', res.value); - }); -} + _after() { + if (this.options.restart) return this.browser.end(); + this.debugSection('Session', 'cleaning cookies and localStorage'); + return this.browser.execute('localStorage.clear();').then(() => { + return this.browser.deleteCookie(); + }); + } -/** - * {{> ../webapi/click }} - */ -click(locator, context) { - let client = this.browser; - let clickMethod = this.browser.isMobile ? 'touchClick' : 'elementIdClick'; - if (context) { - client = client.element(context); - } - return findClickable(client, locator).then(function(res) { - if (!res.value || res.value.length === 0) { - if (typeof(locator) === "object") locator = JSON.stringify(locator); - throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); + _afterSuite() { + if (!this.options.restart) return this.browser.end(); } - let elem = res.value[0]; - return this[clickMethod](elem.ELEMENT); - }); -} -/** - * {{> ../webapi/doubleClick }} - */ -doubleClick(locator, context) { - let client = this.browser; - if (context) { - client = client.element(context); - } - return findClickable(client, locator).then(function(res) { - if (!res.value || res.value.length === 0) { - if (typeof(locator) === "object") locator = JSON.stringify(locator); - throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); + _failed(test) { + let fileName = test.title.replace(/ /g, '_') + '.failed.png'; + return this.saveScreenshot(fileName); } - let elem = res.value[0]; - return this.moveTo(elem.ELEMENT).doDoubleClick(); - }); -} -/** - * Performs right click on an element matched by CSS or XPath. - */ -rightClick(locator) { - return this.browser.rightClick(withStrictLocator(locator)); -} + _withinBegin(locator) { + withinStore.elFn = this.browser.element; + withinStore.elsFn = this.browser.elements; + this.context = locator; + return this.browser.element(withStrictLocator(locator)).then((res) => { + this.browser.element = function(l) { + return this.elementIdElement(res.value.ELEMENT, l); + }; + this.browser.elements = function(l) { + return this.elementIdElements(res.value.ELEMENT, l); + }; + }); + } -/** - * {{> ../webapi/fillField }} - */ -fillField(field, value) { - return findFields(this.browser, field).then(function(res) { - if (!res.value || res.value.length === 0) { - throw new Error(`Field ${field} not found by name|text|CSS|XPath`); + _withinEnd() { + this.context = 'body'; + this.browser.element = withinStore.elFn; + this.browser.elements = withinStore.elsFn; } - let elem = res.value[0]; - return this.elementIdClear(elem.ELEMENT).elementIdValue(elem.ELEMENT, value); - }); -} -/** - * {{> ../webapi/appendField }} - */ -appendField(field, value) { - return findFields(this.browser, field).then(function(res) { - if (!res.value || res.value.length === 0) { - throw new Error(`Field ${field} not found by name|text|CSS|XPath`); + /** + * Get elements by different locator types, including strict locator + * Should be used in custom helpers: + * + * ```js + * this.helpers['WebDriverIO']._locate({name: 'password'}).then //... + * ``` + */ + _locate(locator) { + return this.browser.elements(withStrictLocator(locator)); } - let elem = res.value[0]; - return this.elementIdValue(elem.ELEMENT, value); - }); -} -/** - * {{> ../webapi/selectOption}} - * - */ -selectOption(select, option) { - return findFields(this.browser, select).then(function(res) { - if (!res.value || res.value.length === 0) { - throw new Error(`Selectable field ${select} not found by name|text|CSS|XPath`); + /** + * Find a checkbox by providing human readable text: + * + * ```js + * this.helpers['WebDriverIO']._locateCheckable('I agree with terms and conditions').then // ... + * ``` + */ + _locateCheckable(locator) { + return findCheckable(this.browser, locator).then(function(res) { + return res.value; + }) } - let elem = res.value[0]; - let normalized, byVisibleText; - let commands = []; + /** + * Find a clickable element by providing human readable text: + * + * ```js + * this.helpers['WebDriverIO']._locateClickable('Next page').then // ... + * ``` + */ + _locateClickable(locator) { + return findClickable(this.browser, locator).then(function(res) { + return res.value; + }) + } - if (!Array.isArray(option)) { - option = [option]; + /** + * Find field elements by providing human readable text: + * + * ```js + * this.helpers['WebDriverIO']._locateFields('Your email').then // ... + * ``` + */ + _locateFields(locator) { + return findFields(this.browser, locator).then(function(res) { + return res.value; + }) } - option.forEach((opt) => { - normalized = `[normalize-space(.) = "${opt.trim() }"]`; - byVisibleText = `./option${normalized}|./optgroup/option${normalized}`; - commands.push(this.elementIdElements(elem.ELEMENT, byVisibleText)); - }); - return this.unify(commands, { - extractValue: true - }).then((els) => { - commands = []; - let clickOptionFn = (el) => { - if (el[0]) el = el[0]; - if (el && el.ELEMENT) commands.push(this.elementIdClick(el.ELEMENT)); - }; - - if (els.length) { - els.forEach(clickOptionFn); - return this.unify(commands); + /** + * Set [WebDriverIO timeouts](http://webdriver.io/guide/testrunner/timeouts.html) in realtime. + * Timeouts are expected to be passed as object: + * + * ```js + * I.defineTimeout({ script: 5000 }); + * I.defineTimeout({ implicit: 10000, "page load": 10000, script: 5000 }); + * ``` + */ + defineTimeout(timeouts) { + if (timeouts.implicit) { + this.browser.timeouts('implicit', timeouts.implicit); } - let normalized, byValue; + if (timeouts['page load']) { + this.browser.timeouts('page load', timeouts['page load']); + } + if (timeouts.script) { + this.browser.timeouts('script', timeouts.script); + } + } - option.forEach((opt) => { - normalized = `[normalize-space(@value) = "${opt.trim() }"]`; - byValue = `./option${normalized}|./optgroup/option${normalized}`; - commands.push(this.elementIdElements(elem.ELEMENT, byValue)); + /** + * {{> ../webapi/amOnPage }} + */ + amOnPage(url) { + return this.browser.url(url).url((err, res) => { + if (err) throw err; + this.debugSection('Url', res.value); }); - // try by value - return this.unify(commands, { - extractValue: true - }).then((els) => { - if (els.length === 0) { - throw new Error(`Option ${option} in ${select} was found neither by visible text not by value`); + } + + /** + * {{> ../webapi/click }} + */ + click(locator, context) { + let client = this.browser; + let clickMethod = this.browser.isMobile ? 'touchClick' : 'elementIdClick'; + if (context) { + client = client.element(context); + } + return findClickable(client, locator).then(function(res) { + if (!res.value || res.value.length === 0) { + if (typeof(locator) === "object") locator = JSON.stringify(locator); + throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); } - commands = []; - els.forEach(clickOptionFn); - return this.unify(commands); + let elem = res.value[0]; + return this[clickMethod](elem.ELEMENT); }); - }); - }); -} + } -/** - * {{> ../webapi/attachFile }} - */ -attachFile(locator, pathToFile) { - let file = path.join(global.codecept_dir, pathToFile); - if (!fileExists(file)) { - throw new Error(`File at ${file} can not be found on local system`); - } - return findFields(this.browser, locator).then((el) => { - this.debug("Uploading " + file); - return this.browser.uploadFile(file).then((res) => { - if (!el.value || el.value.length === 0) { - throw new Error(`File field ${locator} not found by name|text|CSS|XPath`); + /** + * {{> ../webapi/doubleClick }} + */ + doubleClick(locator, context) { + let client = this.browser; + if (context) { + client = client.element(context); } - return this.browser.elementIdValue(el.value[0].ELEMENT, res.value); - }); - }); -} + return findClickable(client, locator).then(function(res) { + if (!res.value || res.value.length === 0) { + if (typeof(locator) === "object") locator = JSON.stringify(locator); + throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); + } + let elem = res.value[0]; + return this.moveTo(elem.ELEMENT).doDoubleClick(); + }); + } -/** - * {{> ../webapi/checkOption }} - */ -checkOption(field, context) { - let client = this.browser; - let clickMethod = this.browser.isMobile ? 'touchClick' : 'elementIdClick'; - if (context) { - client = client.element(withStrictLocator(context)); - } - return findCheckable(client, field).then((res) => { - if (!res.value || res.value.length === 0) { - throw new Error(`Checkable ${field} cant be located by name|text|CSS|XPath`); - } - let elem = res.value[0]; - return client.elementIdSelected(elem.ELEMENT).then(function(isSelected) { - if (isSelected.value) return true; - return this[clickMethod](elem.ELEMENT); - }); - }); -} + /** + * Performs right click on an element matched by CSS or XPath. + */ + rightClick(locator) { + return this.browser.rightClick(withStrictLocator(locator)); + } -/** - * {{> ../webapi/grabTextFrom }} - */ -grabTextFrom(locator) { - return this.browser.getText(withStrictLocator(locator)).then(function(text) { - return text; - }); -} + /** + * {{> ../webapi/fillField }} + */ + fillField(field, value) { + return findFields(this.browser, field).then(function(res) { + if (!res.value || res.value.length === 0) { + throw new Error(`Field ${field} not found by name|text|CSS|XPath`); + } + let elem = res.value[0]; + return this.elementIdClear(elem.ELEMENT).elementIdValue(elem.ELEMENT, value); + }); + } -/** - * Retrieves the innerHTML from an element located by CSS or XPath and returns it to test. - * Resumes test execution, so **should be used inside a generator with `yield`** operator. - * - * ```js - * let postHTML = yield I.grabHTMLFrom('#post'); - * ``` - */ -grabHTMLFrom(locator) { - return this.browser.getHTML(withStrictLocator(locator)).then(function(html) { - return html; - }); -} + /** + * {{> ../webapi/appendField }} + */ + appendField(field, value) { + return findFields(this.browser, field).then(function(res) { + if (!res.value || res.value.length === 0) { + throw new Error(`Field ${field} not found by name|text|CSS|XPath`); + } + let elem = res.value[0]; + return this.elementIdValue(elem.ELEMENT, value); + }); + } -/** - * {{> ../webapi/grabValueFrom }} - */ -grabValueFrom(locator) { - return this.browser.getValue(withStrictLocator(locator)).then(function(text) { - return text; - }); -} + /** + * {{> ../webapi/selectOption}} + * + */ + selectOption(select, option) { + return findFields(this.browser, select).then(function(res) { + if (!res.value || res.value.length === 0) { + throw new Error(`Selectable field ${select} not found by name|text|CSS|XPath`); + } + let elem = res.value[0]; -/** - * {{> ../webapi/grabAttributeFrom }} - */ -grabAttributeFrom(locator, attr) { - return this.browser.getAttribute(withStrictLocator(locator), attr).then(function(text) { - return text; - }); -} + let normalized, byVisibleText; + let commands = []; -/** - * {{> ../webapi/seeInTitle }} - */ -seeInTitle(text) { - return this.browser.getTitle().then((title) => { - return stringIncludes('web page title').assert(text, title); - }); -} + if (!Array.isArray(option)) { + option = [option]; + } -/** - * {{> ../webapi/dontSeeInTitle }} - */ -dontSeeInTitle(text) { - return this.browser.getTitle().then((title) => { - return stringIncludes('web page title').negate(text, title); - }); -} + option.forEach((opt) => { + normalized = `[normalize-space(.) = "${opt.trim() }"]`; + byVisibleText = `./option${normalized}|./optgroup/option${normalized}`; + commands.push(this.elementIdElements(elem.ELEMENT, byVisibleText)); + }); + return this.unify(commands, { + extractValue: true + }).then((els) => { + commands = []; + let clickOptionFn = (el) => { + if (el[0]) el = el[0]; + if (el && el.ELEMENT) commands.push(this.elementIdClick(el.ELEMENT)); + }; + + if (els.length) { + els.forEach(clickOptionFn); + return this.unify(commands); + } + let normalized, byValue; + + option.forEach((opt) => { + normalized = `[normalize-space(@value) = "${opt.trim() }"]`; + byValue = `./option${normalized}|./optgroup/option${normalized}`; + commands.push(this.elementIdElements(elem.ELEMENT, byValue)); + }); + // try by value + return this.unify(commands, { + extractValue: true + }).then((els) => { + if (els.length === 0) { + throw new Error(`Option ${option} in ${select} was found neither by visible text not by value`); + } + commands = []; + els.forEach(clickOptionFn); + return this.unify(commands); + }); + }); + }); + } -/** - * {{> ../webapi/grabTitle }} - */ -grabTitle() { - return this.browser.getTitle().then((title) => { - this.debugSection('Title', title); - return title; - }); -} + /** + * {{> ../webapi/attachFile }} + */ + attachFile(locator, pathToFile) { + let file = path.join(global.codecept_dir, pathToFile); + if (!fileExists(file)) { + throw new Error(`File at ${file} can not be found on local system`); + } + return findFields(this.browser, locator).then((el) => { + this.debug("Uploading " + file); + return this.browser.uploadFile(file).then((res) => { + if (!el.value || el.value.length === 0) { + throw new Error(`File field ${locator} not found by name|text|CSS|XPath`); + } + return this.browser.elementIdValue(el.value[0].ELEMENT, res.value); + }); + }); + } -/** - * {{> ../webapi/see }} - */ -see(text, context) { - return proceedSee.call(this, 'assert', text, context); -} + /** + * {{> ../webapi/checkOption }} + */ + checkOption(field, context) { + let client = this.browser; + let clickMethod = this.browser.isMobile ? 'touchClick' : 'elementIdClick'; + if (context) { + client = client.element(withStrictLocator(context)); + } + return findCheckable(client, field).then((res) => { + if (!res.value || res.value.length === 0) { + throw new Error(`Checkable ${field} cant be located by name|text|CSS|XPath`); + } + let elem = res.value[0]; + return client.elementIdSelected(elem.ELEMENT).then(function(isSelected) { + if (isSelected.value) return true; + return this[clickMethod](elem.ELEMENT); + }); + }); + } -/** - * {{> ../webapi/dontSee }} - */ -dontSee(text, context) { - return proceedSee.call(this, 'negate', text, context); -} + /** + * {{> ../webapi/grabTextFrom }} + */ + grabTextFrom(locator) { + return this.browser.getText(withStrictLocator(locator)).then(function(text) { + return text; + }); + } -/** - * {{> ../webapi/seeInField }} - */ -seeInField(field, value) { - return proceedSeeField.call(this, 'assert', field, value); -} + /** + * Retrieves the innerHTML from an element located by CSS or XPath and returns it to test. + * Resumes test execution, so **should be used inside a generator with `yield`** operator. + * + * ```js + * let postHTML = yield I.grabHTMLFrom('#post'); + * ``` + */ + grabHTMLFrom(locator) { + return this.browser.getHTML(withStrictLocator(locator)).then(function(html) { + return html; + }); + } -/** - * {{> ../webapi/dontSeeInField }} - */ -dontSeeInField(field, value) { - return proceedSeeField.call(this, 'negate', field, value); -} + /** + * {{> ../webapi/grabValueFrom }} + */ + grabValueFrom(locator) { + return this.browser.getValue(withStrictLocator(locator)).then(function(text) { + return text; + }); + } -/** - * {{> ../webapi/seeCheckboxIsChecked }} - */ -seeCheckboxIsChecked(field) { - return proceedSeeCheckbox.call(this, 'assert', field); -} + /** + * {{> ../webapi/grabAttributeFrom }} + */ + grabAttributeFrom(locator, attr) { + return this.browser.getAttribute(withStrictLocator(locator), attr).then(function(text) { + return text; + }); + } -/** - * {{> ../webapi/dontSeeCheckboxIsChecked }} - */ -dontSeeCheckboxIsChecked(field) { - return proceedSeeCheckbox.call(this, 'negate', field); -} + /** + * {{> ../webapi/seeInTitle }} + */ + seeInTitle(text) { + return this.browser.getTitle().then((title) => { + return stringIncludes('web page title').assert(text, title); + }); + } -/** - * {{> ../webapi/seeElement }} - */ -seeElement(locator) { - return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { - return truth(`elements of ${locator}`, 'to be seen').assert(res); - }); -} + /** + * {{> ../webapi/dontSeeInTitle }} + */ + dontSeeInTitle(text) { + return this.browser.getTitle().then((title) => { + return stringIncludes('web page title').negate(text, title); + }); + } -/** - * {{> ../webapi/dontSeeElement}} - */ -dontSeeElement(locator) { - return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { - return truth(`elements of ${locator}`, 'to be seen').negate(res); - }); -} + /** + * {{> ../webapi/grabTitle }} + */ + grabTitle() { + return this.browser.getTitle().then((title) => { + this.debugSection('Title', title); + return title; + }); + } -/** - * {{> ../webapi/seeElementInDOM }} - */ -seeElementInDOM(locator) { - return this.browser.elements(withStrictLocator(locator)).then(function(res) { - return empty('elements').negate(res.value); - }); -} + /** + * {{> ../webapi/see }} + */ + see(text, context) { + return proceedSee.call(this, 'assert', text, context); + } -/** - * {{> ../webapi/dontSeeElementInDOM }} - */ -dontSeeElementInDOM(locator) { - return this.browser.elements(withStrictLocator(locator)).then(function(res) { - return empty('elements').assert(res.value); - }); -} + /** + * {{> ../webapi/dontSee }} + */ + dontSee(text, context) { + return proceedSee.call(this, 'negate', text, context); + } -/** - * {{> ../webapi/seeInSource }} - */ -seeInSource(text) { - return this.browser.getSource().then((source) => { - return stringIncludes('HTML source of a page').assert(text, source); - }); -} + /** + * {{> ../webapi/seeInField }} + */ + seeInField(field, value) { + return proceedSeeField.call(this, 'assert', field, value); + } -/** - * {{> ../webapi/dontSeeInSource }} - */ -dontSeeInSource(text) { - return this.browser.getSource().then((source) => { - return stringIncludes('HTML source of a page').negate(text, source); - }); -} + /** + * {{> ../webapi/dontSeeInField }} + */ + dontSeeInField(field, value) { + return proceedSeeField.call(this, 'negate', field, value); + } -/** - * asserts that an element appears a given number of times in the DOM - * Element is located by label or name or CSS or XPath. - * - * ```js - * I.seeNumberOfElements('#submitBtn', 1); - * ``` - */ -seeNumberOfElements(selector, num) { - return this.browser.elements(withStrictLocator(selector)) - .then(function(res) { - return assert.equal(res.value.length, num); - }); -} + /** + * {{> ../webapi/seeCheckboxIsChecked }} + */ + seeCheckboxIsChecked(field) { + return proceedSeeCheckbox.call(this, 'assert', field); + } -/** - * {{> ../webapi/seeInCurrentUrl }} - */ -seeInCurrentUrl(url) { - return this.browser.url().then(function(res) { - return stringIncludes('url').assert(url, res.value); - }); -} + /** + * {{> ../webapi/dontSeeCheckboxIsChecked }} + */ + dontSeeCheckboxIsChecked(field) { + return proceedSeeCheckbox.call(this, 'negate', field); + } -/** - * {{> ../webapi/dontSeeInCurrentUrl }} - */ -dontSeeInCurrentUrl(url) { - return this.browser.url().then(function(res) { - return stringIncludes('url').negate(url, res.value); - }); -} + /** + * {{> ../webapi/seeElement }} + */ + seeElement(locator) { + return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { + return truth(`elements of ${locator}`, 'to be seen').assert(res); + }); + } -/** - * {{> ../webapi/seeCurrentUrlEquals }} - */ -seeCurrentUrlEquals(url) { - return this.browser.url().then((res) => { - return urlEquals(this.options.url).assert(url, res.value); - }); -} + /** + * {{> ../webapi/dontSeeElement}} + */ + dontSeeElement(locator) { + return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { + return truth(`elements of ${locator}`, 'to be seen').negate(res); + }); + } -/** - * {{> ../webapi/dontSeeCurrentUrlEquals }} - */ -dontSeeCurrentUrlEquals(url) { - return this.browser.url().then((res) => { - return urlEquals(this.options.url).negate(url, res.value); - }); -} + /** + * {{> ../webapi/seeElementInDOM }} + */ + seeElementInDOM(locator) { + return this.browser.elements(withStrictLocator(locator)).then(function(res) { + return empty('elements').negate(res.value); + }); + } -/** - * {{> ../webapi/executeScript }} - * - * Wraps [execute](http://webdriver.io/api/protocol/execute.html) command. - */ -executeScript(fn) { - return this.browser.execute.apply(this.browser, arguments).then((res) => res.value); -} + /** + * {{> ../webapi/dontSeeElementInDOM }} + */ + dontSeeElementInDOM(locator) { + return this.browser.elements(withStrictLocator(locator)).then(function(res) { + return empty('elements').assert(res.value); + }); + } -/** - * {{> ../webapi/executeAsyncScript }} - */ -executeAsyncScript(fn) { - return this.browser.executeAsync.apply(this.browser, arguments).then((res) => res.value); -} + /** + * {{> ../webapi/seeInSource }} + */ + seeInSource(text) { + return this.browser.getSource().then((source) => { + return stringIncludes('HTML source of a page').assert(text, source); + }); + } -/** - * Scrolls to element matched by locator. - * Extra shift can be set with offsetX and offsetY options - * - * ```js - * I.scrollTo('footer'); - * I.scrollTo('#submit', 5,5); - * ``` - */ -scrollTo(locator, offsetX, offsetY) { - return this.browser.scroll(withStrictLocator(locator), offsetX, offsetY); -} + /** + * {{> ../webapi/dontSeeInSource }} + */ + dontSeeInSource(text) { + return this.browser.getSource().then((source) => { + return stringIncludes('HTML source of a page').negate(text, source); + }); + } -/** - * {{> ../webapi/moveCursorTo}} - */ -moveCursorTo(locator, offsetX, offsetY) { - return this.browser.moveToObject(withStrictLocator(locator), offsetX, offsetY); -} + /** + * asserts that an element appears a given number of times in the DOM + * Element is located by label or name or CSS or XPath. + * + * ```js + * I.seeNumberOfElements('#submitBtn', 1); + * ``` + */ + seeNumberOfElements(selector, num) { + return this.browser.elements(withStrictLocator(selector)) + .then(function(res) { + return assert.equal(res.value.length, num); + }); + } -/** - * {{> ../webapi/saveScreenshot}} - */ -saveScreenshot(fileName) { - let outputFile = path.join(global.output_dir, fileName); - this.debug('Screenshot has been saved to ' + outputFile); - return this.browser.saveScreenshot(outputFile); -} + /** + * {{> ../webapi/seeInCurrentUrl }} + */ + seeInCurrentUrl(url) { + return this.browser.url().then(function(res) { + return stringIncludes('url').assert(url, res.value); + }); + } -/** - * {{> ../webapi/setCookie}} - * - * Uses Selenium's JSON [cookie format](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object). - */ -setCookie(cookie) { - return this.browser.setCookie(cookie); -} + /** + * {{> ../webapi/dontSeeInCurrentUrl }} + */ + dontSeeInCurrentUrl(url) { + return this.browser.url().then(function(res) { + return stringIncludes('url').negate(url, res.value); + }); + } -/** - * {{> ../webapi/clearCookie}} - */ -clearCookie(cookie) { - return this.browser.deleteCookie(cookie); -} + /** + * {{> ../webapi/seeCurrentUrlEquals }} + */ + seeCurrentUrlEquals(url) { + return this.browser.url().then((res) => { + return urlEquals(this.options.url).assert(url, res.value); + }); + } -/** - * {{> ../webapi/clearField}} - */ -clearField(locator) { - return this.browser.clearElement(withStrictLocator(locator)); -} + /** + * {{> ../webapi/dontSeeCurrentUrlEquals }} + */ + dontSeeCurrentUrlEquals(url) { + return this.browser.url().then((res) => { + return urlEquals(this.options.url).negate(url, res.value); + }); + } -/** - * {{> ../webapi/seeCookie}} - */ -seeCookie(name) { - return this.browser.getCookie(name).then(function(res) { - return truth('cookie ' + name, 'to be set').assert(res); - }); -} + /** + * {{> ../webapi/executeScript }} + * + * Wraps [execute](http://webdriver.io/api/protocol/execute.html) command. + */ + executeScript(fn) { + return this.browser.execute.apply(this.browser, arguments).then((res) => res.value); + } -/** - * {{> ../webapi/dontSeeCookie}} - */ -dontSeeCookie(name) { - return this.browser.getCookie(name).then(function(res) { - return truth('cookie ' + name, 'to be set').negate(res); - }); -} + /** + * {{> ../webapi/executeAsyncScript }} + */ + executeAsyncScript(fn) { + return this.browser.executeAsync.apply(this.browser, arguments).then((res) => res.value); + } -/** - * {{> ../webapi/grabCookie}} - */ -grabCookie(name) { - return this.browser.getCookie(name); -} + /** + * Scrolls to element matched by locator. + * Extra shift can be set with offsetX and offsetY options + * + * ```js + * I.scrollTo('footer'); + * I.scrollTo('#submit', 5,5); + * ``` + */ + scrollTo(locator, offsetX, offsetY) { + return this.browser.scroll(withStrictLocator(locator), offsetX, offsetY); + } -/** - * Accepts the active JavaScript native popup window, as created by window.alert|window.confirm|window.prompt. - * Don't confuse popups with modal windows, as created by [various libraries](http://jster.net/category/windows-modals-popups). - */ -acceptPopup() { - return this.browser.alertText().then(function(res) { - if (res !== null) { - return this.alertAccept(); + /** + * {{> ../webapi/moveCursorTo}} + */ + moveCursorTo(locator, offsetX, offsetY) { + return this.browser.moveToObject(withStrictLocator(locator), offsetX, offsetY); } - }); -} -/** - * Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt. - */ -cancelPopup() { - return this.browser.alertText().then(function(res) { - if (res !== null) { - return this.alertDismiss(); + /** + * {{> ../webapi/saveScreenshot}} + */ + saveScreenshot(fileName) { + let outputFile = path.join(global.output_dir, fileName); + this.debug('Screenshot has been saved to ' + outputFile); + return this.browser.saveScreenshot(outputFile); } - }); -} -/** - * Checks that the active JavaScript popup, as created by `window.alert|window.confirm|window.prompt`, contains the given string. - */ -seeInPopup(text) { - return this.browser.alertText().then(function(res) { - if (res === null) { - throw new Error('Popup is not opened'); + /** + * {{> ../webapi/setCookie}} + * + * Uses Selenium's JSON [cookie format](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object). + */ + setCookie(cookie) { + return this.browser.setCookie(cookie); } - stringIncludes('text in popup').assert(text, res); - }); -} -/** - * {{> ../webapi/pressKey }} - * - * To make combinations with modifier and mouse clicks (like Ctrl+Click) press a modifier, click, then release it. - * - * ```js - * I.pressKey('Control'); - * I.click('#someelement'); - * I.pressKey('Control'); - * ``` - */ -pressKey(key) { - let modifier; - if (Array.isArray(key) && ~['Control', 'Command', 'Shift', 'Alt'].indexOf(key[0])) { - modifier = key[0]; - } - return this.browser.keys(key).then(function() { - if (!modifier) return true; - return this.keys(modifier); // release modifeier - }); -} + /** + * {{> ../webapi/clearCookie}} + */ + clearCookie(cookie) { + return this.browser.deleteCookie(cookie); + } -/** - * {{> ../webapi/resizeWindow }} - */ -resizeWindow(width, height) { - if (width === 'maximize') { - return this.browser.windowHandleMaximize(false); - } - return this.browser.windowHandleSize({ - width, - height - }); -} + /** + * {{> ../webapi/clearField}} + */ + clearField(locator) { + return this.browser.clearElement(withStrictLocator(locator)); + } -/** - * Drag an item to a destination element. - * - * ```js - * I.dragAndDrop('#dragHandle', '#container'); - * ``` - */ -dragAndDrop(srcElement, destElement) { - return this.browser.dragAndDrop( - withStrictLocator(srcElement), - withStrictLocator(destElement) - ); -} + /** + * {{> ../webapi/seeCookie}} + */ + seeCookie(name) { + return this.browser.getCookie(name).then(function(res) { + return truth('cookie ' + name, 'to be set').assert(res); + }); + } -/** - * {{> ../webapi/wait }} - */ -wait(sec) { - return this.browser.pause(sec * 1000); -} + /** + * {{> ../webapi/dontSeeCookie}} + */ + dontSeeCookie(name) { + return this.browser.getCookie(name).then(function(res) { + return truth('cookie ' + name, 'to be set').negate(res); + }); + } -/** - * {{> ../webapi/waitForEnabled }} - */ -waitForEnabled(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForEnabled(withStrictLocator(locator), sec * 1000); -} + /** + * {{> ../webapi/grabCookie}} + */ + grabCookie(name) { + return this.browser.getCookie(name); + } -/** - * {{> ../webapi/waitForElement }} - */ -waitForElement(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForExist(withStrictLocator(locator), sec * 1000); -} + /** + * Accepts the active JavaScript native popup window, as created by window.alert|window.confirm|window.prompt. + * Don't confuse popups with modal windows, as created by [various libraries](http://jster.net/category/windows-modals-popups). + */ + acceptPopup() { + return this.browser.alertText().then(function(res) { + if (res !== null) { + return this.alertAccept(); + } + }); + } -/** - * {{> ../webapi/waitForText }} - */ -waitForText(text, sec, context) { - sec = sec || this.options.waitForTimeout; - context = context || 'body'; - return this.browser.waitUntil(function() { - return this.getText(context).then(function(source) { - if (Array.isArray(source)) { - return source.filter(part => part.indexOf(text) >= 0).length > 0; + /** + * Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt. + */ + cancelPopup() { + return this.browser.alertText().then(function(res) { + if (res !== null) { + return this.alertDismiss(); } - return source.indexOf(text) >= 0; }); - }, sec * 1000) - .catch((e) => { - if (e.type === 'WaitUntilTimeoutError') { - return proceedSee.call(this, 'assert', text, context); - } else { - throw e; + } + + /** + * Checks that the active JavaScript popup, as created by `window.alert|window.confirm|window.prompt`, contains the given string. + */ + seeInPopup(text) { + return this.browser.alertText().then(function(res) { + if (res === null) { + throw new Error('Popup is not opened'); + } + stringIncludes('text in popup').assert(text, res); + }); + } + + /** + * {{> ../webapi/pressKey }} + * + * To make combinations with modifier and mouse clicks (like Ctrl+Click) press a modifier, click, then release it. + * + * ```js + * I.pressKey('Control'); + * I.click('#someelement'); + * I.pressKey('Control'); + * ``` + */ + pressKey(key) { + let modifier; + if (Array.isArray(key) && ~['Control', 'Command', 'Shift', 'Alt'].indexOf(key[0])) { + modifier = key[0]; } - }); -} + return this.browser.keys(key).then(function() { + if (!modifier) return true; + return this.keys(modifier); // release modifeier + }); + } -/** - * {{> ../webapi/waitForVisible }} - */ -waitForVisible(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForVisible(withStrictLocator(locator), sec * 1000); -} + /** + * {{> ../webapi/resizeWindow }} + */ + resizeWindow(width, height) { + if (width === 'maximize') { + return this.browser.windowHandleMaximize(false); + } + return this.browser.windowHandleSize({ + width, + height + }); + } -/** - * {{> ../webapi/waitForInvisible }} - */ -waitForInvisible(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForVisible(withStrictLocator(locator), sec * 1000, true); -} + /** + * Drag an item to a destination element. + * + * ```js + * I.dragAndDrop('#dragHandle', '#container'); + * ``` + */ + dragAndDrop(srcElement, destElement) { + return this.browser.dragAndDrop( + withStrictLocator(srcElement), + withStrictLocator(destElement) + ); + } -/** - * Waits for an element to become invisible on a page (by default waits for 1sec). - * Element can be located by CSS or XPath. - */ -waitToHide(locator, sec) { - return this.waitForInvisible(locator, sec); -} + /** + * {{> ../webapi/wait }} + */ + wait(sec) { + return this.browser.pause(sec * 1000); + } -/** - * {{> ../webapi/waitForStalenessOf }} - */ -waitForStalenessOf(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForExist(withStrictLocator(locator), sec * 1000, true); -} + /** + * {{> ../webapi/waitForEnabled }} + */ + waitForEnabled(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForEnabled(withStrictLocator(locator), sec * 1000); + } -/** - * Waits for a function to return true (waits for 1sec by default). - */ -waitUntil(fn, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitUntil(fn, sec); -} + /** + * {{> ../webapi/waitForElement }} + */ + waitForElement(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForExist(withStrictLocator(locator), sec * 1000); + } -/** - * Switches frame or in case of null locator reverts to parent. - */ -switchTo(locator) { - locator = locator || null; - return this.browser.frame(locator); -} -} - -function proceedSee(assertType, text, context) { - let description; - if (!context) { - context = this.context; - if (this.context === 'body') { - description = 'web page'; + /** + * {{> ../webapi/waitForText }} + */ + waitForText(text, sec, context) { + sec = sec || this.options.waitForTimeout; + context = context || 'body'; + return this.browser.waitUntil(function() { + return this.getText(context).then(function(source) { + if (Array.isArray(source)) { + return source.filter(part => part.indexOf(text) >= 0).length > 0; + } + return source.indexOf(text) >= 0; + }); + }, sec * 1000) + .catch((e) => { + if (e.type === 'WaitUntilTimeoutError') { + return proceedSee.call(this, 'assert', text, context); + } else { + throw e; + } + }); + } + + /** + * {{> ../webapi/waitForVisible }} + */ + waitForVisible(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForVisible(withStrictLocator(locator), sec * 1000); + } + + /** + * {{> ../webapi/waitForInvisible }} + */ + waitForInvisible(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForVisible(withStrictLocator(locator), sec * 1000, true); + } + + /** + * Waits for an element to become invisible on a page (by default waits for 1sec). + * Element can be located by CSS or XPath. + */ + waitToHide(locator, sec) { + return this.waitForInvisible(locator, sec); + } + + /** + * {{> ../webapi/waitForStalenessOf }} + */ + waitForStalenessOf(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForExist(withStrictLocator(locator), sec * 1000, true); + } + + /** + * Waits for a function to return true (waits for 1sec by default). + */ + waitUntil(fn, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitUntil(fn, sec); + } + + /** + * Switches frame or in case of null locator reverts to parent. + */ + switchTo(locator) { + locator = locator || null; + return this.browser.frame(locator); + } + } + + function proceedSee(assertType, text, context) { + let description; + if (!context) { + context = this.context; + if (this.context === 'body') { + description = 'web page'; + } else { + description = 'current context ' + this.context; + } } else { - description = 'current context ' + this.context; + description = 'element ' + context; } - } else { - description = 'element ' + context; + return this.browser.getText(withStrictLocator(context)).then(function(source) { + return stringIncludes(description)[assertType](text, source); + }); } - return this.browser.getText(withStrictLocator(context)).then(function(source) { - return stringIncludes(description)[assertType](text, source); - }); -} - -function findClickable(client, locator) { - if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); - if (isCSSorXPathLocator(locator)) return client.elements(locator); - - let literal = xpathLocator.literal(locator); - - let narrowLocator = xpathLocator.combine([ - `.//a[normalize-space(.)=${literal}]`, - `.//button[normalize-space(.)=${literal}]`, - `.//a/img[normalize-space(@alt)=${literal}]/ancestor::a`, - `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][normalize-space(@value)=${literal}]` - ]); - return client.elements(narrowLocator).then(function(els) { - if (els.value.length) { - return els; - } - let wideLocator = xpathLocator.combine([ - `.//a[./@href][((contains(normalize-space(string(.)), ${literal})) or .//img[contains(./@alt, ${literal})])]`, - `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][contains(./@value, ${literal})]`, - `.//input[./@type = 'image'][contains(./@alt, ${literal})]`, - `.//button[contains(normalize-space(string(.)), ${literal})]`, - `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][./@name = ${literal}]`, - `.//button[./@name = ${literal}]` + + function findClickable(client, locator) { + if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); + if (isCSSorXPathLocator(locator)) return client.elements(locator); + + let literal = xpathLocator.literal(locator); + + let narrowLocator = xpathLocator.combine([ + `.//a[normalize-space(.)=${literal}]`, + `.//button[normalize-space(.)=${literal}]`, + `.//a/img[normalize-space(@alt)=${literal}]/ancestor::a`, + `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][normalize-space(@value)=${literal}]` ]); - return client.elements(wideLocator).then(function(els) { + return client.elements(narrowLocator).then(function(els) { if (els.value.length) { return els; } - return client.elements(locator); // by css or xpath - }); - }); -} - -function findFields(client, locator) { - if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); - if (isCSSorXPathLocator(locator)) return client.elements(locator); - - let literal = xpathLocator.literal(locator); - let byText = xpathLocator.combine([ - `.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][(((./@name = ${literal}) or ./@id = //label[contains(normalize-space(string(.)), ${literal})]/@for) or ./@placeholder = ${literal})]`, - `.//label[contains(normalize-space(string(.)), ${literal})]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]` - ]); - return client.elements(byText).then((els) => { - if (els.value.length) return els; - let byName = `.//*[self::input | self::textarea | self::select][@name = ${literal}]`; - return client.elements(byName).then((els) => { - if (els.value.length) return els; - return client.elements(locator); // by css or xpath + let wideLocator = xpathLocator.combine([ + `.//a[./@href][((contains(normalize-space(string(.)), ${literal})) or .//img[contains(./@alt, ${literal})])]`, + `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][contains(./@value, ${literal})]`, + `.//input[./@type = 'image'][contains(./@alt, ${literal})]`, + `.//button[contains(normalize-space(string(.)), ${literal})]`, + `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][./@name = ${literal}]`, + `.//button[./@name = ${literal}]` + ]); + return client.elements(wideLocator).then(function(els) { + if (els.value.length) { + return els; + } + return client.elements(locator); // by css or xpath + }); }); - }); -} + } -function proceedSeeField(assertType, field, value) { - return findFields(this.browser, field).then(function(res) { - if (!res.value || res.value.length === 0) { - throw new Error(`Field ${field} not found by name|text|CSS|XPath`); - } + function findFields(client, locator) { + if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); + if (isCSSorXPathLocator(locator)) return client.elements(locator); - var proceedMultiple = (fields) => { - let commands = []; - fields.forEach((el) => commands.push(this.elementIdSelected(el.ELEMENT))); - this.unify(commands).then((res) => { - commands = []; - fields.forEach((el) => { - if (el.value === false) return; - commands.push(this.elementIdAttribute(el.ELEMENT, 'value')); - }); - this.unify(commands, { - extractValue: true - }).then((val) => { - return stringIncludes('fields by ' + field)[assertType](value, val); - }); + let literal = xpathLocator.literal(locator); + let byText = xpathLocator.combine([ + `.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][(((./@name = ${literal}) or ./@id = //label[contains(normalize-space(string(.)), ${literal})]/@for) or ./@placeholder = ${literal})]`, + `.//label[contains(normalize-space(string(.)), ${literal})]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]` + ]); + return client.elements(byText).then((els) => { + if (els.value.length) return els; + let byName = `.//*[self::input | self::textarea | self::select][@name = ${literal}]`; + return client.elements(byName).then((els) => { + if (els.value.length) return els; + return client.elements(locator); // by css or xpath }); - } + }); + } - var proceedSingle = (el) => { - return this.elementIdAttribute(el.ELEMENT, 'value').then((res) => { - return stringIncludes('fields by ' + field)[assertType](value, res.value); - }); - } + function proceedSeeField(assertType, field, value) { + return findFields(this.browser, field).then(function(res) { + if (!res.value || res.value.length === 0) { + throw new Error(`Field ${field} not found by name|text|CSS|XPath`); + } - return this.elementIdName(res.value[0].ELEMENT).then((tag) => { - if (tag.value == 'select') { - return proceedMultiple(res.value); + var proceedMultiple = (fields) => { + let commands = []; + fields.forEach((el) => commands.push(this.elementIdSelected(el.ELEMENT))); + this.unify(commands).then((res) => { + commands = []; + fields.forEach((el) => { + if (el.value === false) return; + commands.push(this.elementIdAttribute(el.ELEMENT, 'value')); + }); + this.unify(commands, { + extractValue: true + }).then((val) => { + return stringIncludes('fields by ' + field)[assertType](value, val); + }); + }); } - if (tag.value == 'input') { - return this.elementIdAttribute(res.value[0].ELEMENT, 'type').then((type) => { - if (type.value == 'checkbox' || type.value == 'radio') { - return proceedMultiple(res.value); - } - return proceedSingle(res.value[0]); + var proceedSingle = (el) => { + return this.elementIdAttribute(el.ELEMENT, 'value').then((res) => { + return stringIncludes('fields by ' + field)[assertType](value, res.value); }); } - return proceedSingle(res.value[0]); + + return this.elementIdName(res.value[0].ELEMENT).then((tag) => { + if (tag.value == 'select') { + return proceedMultiple(res.value); + } + + if (tag.value == 'input') { + return this.elementIdAttribute(res.value[0].ELEMENT, 'type').then((type) => { + if (type.value == 'checkbox' || type.value == 'radio') { + return proceedMultiple(res.value); + } + return proceedSingle(res.value[0]); + }); + } + return proceedSingle(res.value[0]); + }); }); - }); -} - -function proceedSeeCheckbox(assertType, field) { - return findFields(this.browser, field).then(function(res) { - if (!res.value || res.value.length === 0) { - throw new Error(`Field ${field} not found by name|text|CSS|XPath`); - } - let commands = []; - res.value.forEach((el) => commands.push(this.elementIdSelected(el.ELEMENT))); - return this.unify(commands, { - extractValue: true - }).then((selected) => { - return truth(`checkable field ${field}`, 'to be checked')[assertType](selected); + } + + function proceedSeeCheckbox(assertType, field) { + return findFields(this.browser, field).then(function(res) { + if (!res.value || res.value.length === 0) { + throw new Error(`Field ${field} not found by name|text|CSS|XPath`); + } + let commands = []; + res.value.forEach((el) => commands.push(this.elementIdSelected(el.ELEMENT))); + return this.unify(commands, { + extractValue: true + }).then((selected) => { + return truth(`checkable field ${field}`, 'to be checked')[assertType](selected); + }); }); - }); -} - -function findCheckable(client, locator) { - if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); - if (isCSSorXPathLocator(locator)) return client.elements(locator); - - let literal = xpathLocator.literal(locator); - let byText = xpathLocator.combine([ - `.//input[@type = 'checkbox' or @type = 'radio'][(@id = //label[contains(normalize-space(string(.)), ${literal})]/@for) or @placeholder = ${literal}]`, - `.//label[contains(normalize-space(string(.)), ${literal})]//input[@type = 'radio' or @type = 'checkbox']` - ]); - return client.elements(byText).then(function(els) { - if (els.value.length) return els; - let byName = `.//input[@type = 'checkbox' or @type = 'radio'][@name = ${literal}]`; - return client.elements(byName).then(function(els) { + } + + function findCheckable(client, locator) { + if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); + if (isCSSorXPathLocator(locator)) return client.elements(locator); + + let literal = xpathLocator.literal(locator); + let byText = xpathLocator.combine([ + `.//input[@type = 'checkbox' or @type = 'radio'][(@id = //label[contains(normalize-space(string(.)), ${literal})]/@for) or @placeholder = ${literal}]`, + `.//label[contains(normalize-space(string(.)), ${literal})]//input[@type = 'radio' or @type = 'checkbox']` + ]); + return client.elements(byText).then(function(els) { if (els.value.length) return els; - return client.elements(locator); // by css or xpath + let byName = `.//input[@type = 'checkbox' or @type = 'radio'][@name = ${literal}]`; + return client.elements(byName).then(function(els) { + if (els.value.length) return els; + return client.elements(locator); // by css or xpath + }); }); - }); -} - -function isCSSorXPathLocator(locator) { - if (locator[0] === '#' || locator[0] === '.') { - return true; } - if (locator.substr(0, 2) === '//') { - return true; + + function isCSSorXPathLocator(locator) { + if (locator[0] === '#' || locator[0] === '.') { + return true; + } + if (locator.substr(0, 2) === '//') { + return true; + } + return false; } - return false; -} - -function withStrictLocator(locator) { - if (!locator) return null; - if (typeof(locator) !== 'object') return locator; - let key = Object.keys(locator)[0]; - let value = locator[key]; - - locator.toString = () => `{${key}: '${value}'}`; - - switch (key) { - case 'by': - case 'xpath': - case 'css': - return value; - case 'id': - return '#' + value; - case 'name': - return `[name="${value}"]`; + + function withStrictLocator(locator) { + if (!locator) return null; + if (typeof(locator) !== 'object') return locator; + let key = Object.keys(locator)[0]; + let value = locator[key]; + + locator.toString = () => `{${key}: '${value}'}`; + + switch (key) { + case 'by': + case 'xpath': + case 'css': + return value; + case 'id': + return '#' + value; + case 'name': + return `[name="${value}"]`; + } } -} -module.exports = WebDriverIO; + module.exports = WebDriverIO; From a6165d2a0c967bbc0877c746017f4d5aca8aff61 Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 12:39:49 +0300 Subject: [PATCH 032/111] one more fix --- lib/helper/WebDriverIO.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index ed39073dd..080821d47 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -223,7 +223,7 @@ class WebDriverIO extends Helper { `); } } else { - if ((this.options.desiredCapabilities.platformName = "iOS") && (!this.options.desiredCapabilities.app || !this.options.desiredCapabilities.deviceName || !this.options.desiredCapabilities.platformVersion)) { + if ((this.options.desiredCapabilities.platformName == "iOS") && (!this.options.desiredCapabilities.app || !this.options.desiredCapabilities.deviceName || !this.options.desiredCapabilities.platformVersion)) { throw new Error(` WebDriverIO requires at least these parameters to run in Appium mode for iOS Check your codeceptjs config file to ensure these are set properly @@ -240,7 +240,7 @@ class WebDriverIO extends Helper { } } `); - } else if ((this.options.desiredCapabilities.platformName = "Android") && (!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion)) { + } else if ((this.options.desiredCapabilities.platformName == "Android") && (!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion)) { throw new Error(` WebDriverIO requires at least these parameters to run in Appium mode for Android Check your codeceptjs config file to ensure these are set properly From b6c97349e2fa790a86e4308c53faebec2add0b92 Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 12:43:09 +0300 Subject: [PATCH 033/111] experimental --- lib/helper/WebDriverIO.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 080821d47..adb116c5e 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -223,7 +223,7 @@ class WebDriverIO extends Helper { `); } } else { - if ((this.options.desiredCapabilities.platformName == "iOS") && (!this.options.desiredCapabilities.app || !this.options.desiredCapabilities.deviceName || !this.options.desiredCapabilities.platformVersion)) { + if (!this.options.desiredCapabilities.app || !this.options.desiredCapabilities.deviceName || !this.options.desiredCapabilities.platformVersion) { throw new Error(` WebDriverIO requires at least these parameters to run in Appium mode for iOS Check your codeceptjs config file to ensure these are set properly @@ -240,7 +240,7 @@ class WebDriverIO extends Helper { } } `); - } else if ((this.options.desiredCapabilities.platformName == "Android") && (!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion)) { + } else if (!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion) { throw new Error(` WebDriverIO requires at least these parameters to run in Appium mode for Android Check your codeceptjs config file to ensure these are set properly From b5dc8ce081a7ebdb549667d1b33cf90698b3ff17 Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 12:47:25 +0300 Subject: [PATCH 034/111] experimental2 --- lib/helper/WebDriverIO.js | 1776 +++++++++++++++++++------------------ 1 file changed, 889 insertions(+), 887 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index adb116c5e..e4f620109 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -223,7 +223,7 @@ class WebDriverIO extends Helper { `); } } else { - if (!this.options.desiredCapabilities.app || !this.options.desiredCapabilities.deviceName || !this.options.desiredCapabilities.platformVersion) { + if ((this.options.desiredCapabilities.platformName == "iOS") && (!this.options.desiredCapabilities.app || !this.options.desiredCapabilities.deviceName || !this.options.desiredCapabilities.platformVersion)) { throw new Error(` WebDriverIO requires at least these parameters to run in Appium mode for iOS Check your codeceptjs config file to ensure these are set properly @@ -240,7 +240,7 @@ class WebDriverIO extends Helper { } } `); - } else if (!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion) { + } else if ((this.options.desiredCapabilities.platformName == "Android") && (!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion)) { throw new Error(` WebDriverIO requires at least these parameters to run in Appium mode for Android Check your codeceptjs config file to ensure these are set properly @@ -260,1017 +260,1019 @@ class WebDriverIO extends Helper { } } - static _checkRequirements() { - try { - requireg("webdriverio"); - } catch (e) { - return ["webdriverio"]; - } - } - - static _config() { - return [{ - name: 'url', - message: "Base url of site to be tested", - default: 'http://localhost' - }, { - name: 'browser', - message: 'Browser in which testing will be performed', - default: 'firefox' - }]; - } + } - _beforeSuite() { - if (!this.options.restart && !this.options.manualStart) { - this.debugSection('Session', 'Starting singleton browser session'); - return this._startBrowser(); - } + static _checkRequirements() { + try { + requireg("webdriverio"); + } catch (e) { + return ["webdriverio"]; } + } - _startBrowser() { - if (this.options.multiremote) { - this.browser = webdriverio.multiremote(this.options.multiremote).init(); - } else { - this.browser = webdriverio.remote(this.options).init(); - } + static _config() { + return [{ + name: 'url', + message: "Base url of site to be tested", + default: 'http://localhost' + }, { + name: 'browser', + message: 'Browser in which testing will be performed', + default: 'firefox' + }]; + } - if (this.options.timeouts) { - this.defineTimeout(this.options.timeouts); - } + _beforeSuite() { + if (!this.options.restart && !this.options.manualStart) { + this.debugSection('Session', 'Starting singleton browser session'); + return this._startBrowser(); + } + } - if (this.options.windowSize === 'maximize') { - this.browser.windowHandleMaximize(false); - } - if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0) { - let dimensions = this.options.windowSize.split('x'); - this.browser.windowHandleSize({ - width: dimensions[0], - height: dimensions[1] - }); - } - return this.browser; + _startBrowser() { + if (this.options.multiremote) { + this.browser = webdriverio.multiremote(this.options.multiremote).init(); + } else { + this.browser = webdriverio.remote(this.options).init(); } - _before() { - if (this.options.restart && !this.options.manualStart) this._startBrowser(); - this.failedTestName = null; - this.context = 'body'; - return this.browser; + if (this.options.timeouts) { + this.defineTimeout(this.options.timeouts); } - _after() { - if (this.options.restart) return this.browser.end(); - this.debugSection('Session', 'cleaning cookies and localStorage'); - return this.browser.execute('localStorage.clear();').then(() => { - return this.browser.deleteCookie(); + if (this.options.windowSize === 'maximize') { + this.browser.windowHandleMaximize(false); + } + if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0) { + let dimensions = this.options.windowSize.split('x'); + this.browser.windowHandleSize({ + width: dimensions[0], + height: dimensions[1] }); } + return this.browser; + } - _afterSuite() { - if (!this.options.restart) return this.browser.end(); - } + _before() { + if (this.options.restart && !this.options.manualStart) this._startBrowser(); + this.failedTestName = null; + this.context = 'body'; + return this.browser; + } - _failed(test) { - let fileName = test.title.replace(/ /g, '_') + '.failed.png'; - return this.saveScreenshot(fileName); - } + _after() { + if (this.options.restart) return this.browser.end(); + this.debugSection('Session', 'cleaning cookies and localStorage'); + return this.browser.execute('localStorage.clear();').then(() => { + return this.browser.deleteCookie(); + }); + } - _withinBegin(locator) { - withinStore.elFn = this.browser.element; - withinStore.elsFn = this.browser.elements; - this.context = locator; - return this.browser.element(withStrictLocator(locator)).then((res) => { - this.browser.element = function(l) { - return this.elementIdElement(res.value.ELEMENT, l); - }; - this.browser.elements = function(l) { - return this.elementIdElements(res.value.ELEMENT, l); - }; - }); - } + _afterSuite() { + if (!this.options.restart) return this.browser.end(); + } - _withinEnd() { - this.context = 'body'; - this.browser.element = withinStore.elFn; - this.browser.elements = withinStore.elsFn; - } + _failed(test) { + let fileName = test.title.replace(/ /g, '_') + '.failed.png'; + return this.saveScreenshot(fileName); + } - /** - * Get elements by different locator types, including strict locator - * Should be used in custom helpers: - * - * ```js - * this.helpers['WebDriverIO']._locate({name: 'password'}).then //... - * ``` - */ - _locate(locator) { - return this.browser.elements(withStrictLocator(locator)); - } + _withinBegin(locator) { + withinStore.elFn = this.browser.element; + withinStore.elsFn = this.browser.elements; + this.context = locator; + return this.browser.element(withStrictLocator(locator)).then((res) => { + this.browser.element = function(l) { + return this.elementIdElement(res.value.ELEMENT, l); + }; + this.browser.elements = function(l) { + return this.elementIdElements(res.value.ELEMENT, l); + }; + }); + } - /** - * Find a checkbox by providing human readable text: - * - * ```js - * this.helpers['WebDriverIO']._locateCheckable('I agree with terms and conditions').then // ... - * ``` - */ - _locateCheckable(locator) { - return findCheckable(this.browser, locator).then(function(res) { - return res.value; - }) - } + _withinEnd() { + this.context = 'body'; + this.browser.element = withinStore.elFn; + this.browser.elements = withinStore.elsFn; + } - /** - * Find a clickable element by providing human readable text: - * - * ```js - * this.helpers['WebDriverIO']._locateClickable('Next page').then // ... - * ``` - */ - _locateClickable(locator) { - return findClickable(this.browser, locator).then(function(res) { - return res.value; - }) - } + /** + * Get elements by different locator types, including strict locator + * Should be used in custom helpers: + * + * ```js + * this.helpers['WebDriverIO']._locate({name: 'password'}).then //... + * ``` + */ + _locate(locator) { + return this.browser.elements(withStrictLocator(locator)); + } - /** - * Find field elements by providing human readable text: - * - * ```js - * this.helpers['WebDriverIO']._locateFields('Your email').then // ... - * ``` - */ - _locateFields(locator) { - return findFields(this.browser, locator).then(function(res) { - return res.value; - }) - } + /** + * Find a checkbox by providing human readable text: + * + * ```js + * this.helpers['WebDriverIO']._locateCheckable('I agree with terms and conditions').then // ... + * ``` + */ + _locateCheckable(locator) { + return findCheckable(this.browser, locator).then(function(res) { + return res.value; + }) + } - /** - * Set [WebDriverIO timeouts](http://webdriver.io/guide/testrunner/timeouts.html) in realtime. - * Timeouts are expected to be passed as object: - * - * ```js - * I.defineTimeout({ script: 5000 }); - * I.defineTimeout({ implicit: 10000, "page load": 10000, script: 5000 }); - * ``` - */ - defineTimeout(timeouts) { - if (timeouts.implicit) { - this.browser.timeouts('implicit', timeouts.implicit); - } - if (timeouts['page load']) { - this.browser.timeouts('page load', timeouts['page load']); - } - if (timeouts.script) { - this.browser.timeouts('script', timeouts.script); - } - } + /** + * Find a clickable element by providing human readable text: + * + * ```js + * this.helpers['WebDriverIO']._locateClickable('Next page').then // ... + * ``` + */ + _locateClickable(locator) { + return findClickable(this.browser, locator).then(function(res) { + return res.value; + }) + } - /** - * {{> ../webapi/amOnPage }} - */ - amOnPage(url) { - return this.browser.url(url).url((err, res) => { - if (err) throw err; - this.debugSection('Url', res.value); - }); + /** + * Find field elements by providing human readable text: + * + * ```js + * this.helpers['WebDriverIO']._locateFields('Your email').then // ... + * ``` + */ + _locateFields(locator) { + return findFields(this.browser, locator).then(function(res) { + return res.value; + }) + } + + /** + * Set [WebDriverIO timeouts](http://webdriver.io/guide/testrunner/timeouts.html) in realtime. + * Timeouts are expected to be passed as object: + * + * ```js + * I.defineTimeout({ script: 5000 }); + * I.defineTimeout({ implicit: 10000, "page load": 10000, script: 5000 }); + * ``` + */ + defineTimeout(timeouts) { + if (timeouts.implicit) { + this.browser.timeouts('implicit', timeouts.implicit); + } + if (timeouts['page load']) { + this.browser.timeouts('page load', timeouts['page load']); + } + if (timeouts.script) { + this.browser.timeouts('script', timeouts.script); } + } - /** - * {{> ../webapi/click }} - */ - click(locator, context) { - let client = this.browser; - let clickMethod = this.browser.isMobile ? 'touchClick' : 'elementIdClick'; - if (context) { - client = client.element(context); + /** + * {{> ../webapi/amOnPage }} + */ + amOnPage(url) { + return this.browser.url(url).url((err, res) => { + if (err) throw err; + this.debugSection('Url', res.value); + }); + } + + /** + * {{> ../webapi/click }} + */ + click(locator, context) { + let client = this.browser; + let clickMethod = this.browser.isMobile ? 'touchClick' : 'elementIdClick'; + if (context) { + client = client.element(context); + } + return findClickable(client, locator).then(function(res) { + if (!res.value || res.value.length === 0) { + if (typeof(locator) === "object") locator = JSON.stringify(locator); + throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); } - return findClickable(client, locator).then(function(res) { - if (!res.value || res.value.length === 0) { - if (typeof(locator) === "object") locator = JSON.stringify(locator); - throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); - } - let elem = res.value[0]; - return this[clickMethod](elem.ELEMENT); - }); + let elem = res.value[0]; + return this[clickMethod](elem.ELEMENT); + }); + } + + /** + * {{> ../webapi/doubleClick }} + */ + doubleClick(locator, context) { + let client = this.browser; + if (context) { + client = client.element(context); } + return findClickable(client, locator).then(function(res) { + if (!res.value || res.value.length === 0) { + if (typeof(locator) === "object") locator = JSON.stringify(locator); + throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); + } + let elem = res.value[0]; + return this.moveTo(elem.ELEMENT).doDoubleClick(); + }); + } - /** - * {{> ../webapi/doubleClick }} - */ - doubleClick(locator, context) { - let client = this.browser; - if (context) { - client = client.element(context); + /** + * Performs right click on an element matched by CSS or XPath. + */ + rightClick(locator) { + return this.browser.rightClick(withStrictLocator(locator)); + } + + /** + * {{> ../webapi/fillField }} + */ + fillField(field, value) { + return findFields(this.browser, field).then(function(res) { + if (!res.value || res.value.length === 0) { + throw new Error(`Field ${field} not found by name|text|CSS|XPath`); } - return findClickable(client, locator).then(function(res) { - if (!res.value || res.value.length === 0) { - if (typeof(locator) === "object") locator = JSON.stringify(locator); - throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); - } - let elem = res.value[0]; - return this.moveTo(elem.ELEMENT).doDoubleClick(); - }); - } + let elem = res.value[0]; + return this.elementIdClear(elem.ELEMENT).elementIdValue(elem.ELEMENT, value); + }); + } - /** - * Performs right click on an element matched by CSS or XPath. - */ - rightClick(locator) { - return this.browser.rightClick(withStrictLocator(locator)); - } + /** + * {{> ../webapi/appendField }} + */ + appendField(field, value) { + return findFields(this.browser, field).then(function(res) { + if (!res.value || res.value.length === 0) { + throw new Error(`Field ${field} not found by name|text|CSS|XPath`); + } + let elem = res.value[0]; + return this.elementIdValue(elem.ELEMENT, value); + }); + } - /** - * {{> ../webapi/fillField }} - */ - fillField(field, value) { - return findFields(this.browser, field).then(function(res) { - if (!res.value || res.value.length === 0) { - throw new Error(`Field ${field} not found by name|text|CSS|XPath`); - } - let elem = res.value[0]; - return this.elementIdClear(elem.ELEMENT).elementIdValue(elem.ELEMENT, value); - }); - } + /** + * {{> ../webapi/selectOption}} + * + */ + selectOption(select, option) { + return findFields(this.browser, select).then(function(res) { + if (!res.value || res.value.length === 0) { + throw new Error(`Selectable field ${select} not found by name|text|CSS|XPath`); + } + let elem = res.value[0]; - /** - * {{> ../webapi/appendField }} - */ - appendField(field, value) { - return findFields(this.browser, field).then(function(res) { - if (!res.value || res.value.length === 0) { - throw new Error(`Field ${field} not found by name|text|CSS|XPath`); - } - let elem = res.value[0]; - return this.elementIdValue(elem.ELEMENT, value); - }); - } + let normalized, byVisibleText; + let commands = []; - /** - * {{> ../webapi/selectOption}} - * - */ - selectOption(select, option) { - return findFields(this.browser, select).then(function(res) { - if (!res.value || res.value.length === 0) { - throw new Error(`Selectable field ${select} not found by name|text|CSS|XPath`); - } - let elem = res.value[0]; + if (!Array.isArray(option)) { + option = [option]; + } - let normalized, byVisibleText; - let commands = []; + option.forEach((opt) => { + normalized = `[normalize-space(.) = "${opt.trim() }"]`; + byVisibleText = `./option${normalized}|./optgroup/option${normalized}`; + commands.push(this.elementIdElements(elem.ELEMENT, byVisibleText)); + }); + return this.unify(commands, { + extractValue: true + }).then((els) => { + commands = []; + let clickOptionFn = (el) => { + if (el[0]) el = el[0]; + if (el && el.ELEMENT) commands.push(this.elementIdClick(el.ELEMENT)); + }; - if (!Array.isArray(option)) { - option = [option]; + if (els.length) { + els.forEach(clickOptionFn); + return this.unify(commands); } + let normalized, byValue; option.forEach((opt) => { - normalized = `[normalize-space(.) = "${opt.trim() }"]`; - byVisibleText = `./option${normalized}|./optgroup/option${normalized}`; - commands.push(this.elementIdElements(elem.ELEMENT, byVisibleText)); + normalized = `[normalize-space(@value) = "${opt.trim() }"]`; + byValue = `./option${normalized}|./optgroup/option${normalized}`; + commands.push(this.elementIdElements(elem.ELEMENT, byValue)); }); + // try by value return this.unify(commands, { extractValue: true }).then((els) => { - commands = []; - let clickOptionFn = (el) => { - if (el[0]) el = el[0]; - if (el && el.ELEMENT) commands.push(this.elementIdClick(el.ELEMENT)); - }; - - if (els.length) { - els.forEach(clickOptionFn); - return this.unify(commands); - } - let normalized, byValue; - - option.forEach((opt) => { - normalized = `[normalize-space(@value) = "${opt.trim() }"]`; - byValue = `./option${normalized}|./optgroup/option${normalized}`; - commands.push(this.elementIdElements(elem.ELEMENT, byValue)); - }); - // try by value - return this.unify(commands, { - extractValue: true - }).then((els) => { - if (els.length === 0) { - throw new Error(`Option ${option} in ${select} was found neither by visible text not by value`); - } - commands = []; - els.forEach(clickOptionFn); - return this.unify(commands); - }); - }); - }); - } - - /** - * {{> ../webapi/attachFile }} - */ - attachFile(locator, pathToFile) { - let file = path.join(global.codecept_dir, pathToFile); - if (!fileExists(file)) { - throw new Error(`File at ${file} can not be found on local system`); - } - return findFields(this.browser, locator).then((el) => { - this.debug("Uploading " + file); - return this.browser.uploadFile(file).then((res) => { - if (!el.value || el.value.length === 0) { - throw new Error(`File field ${locator} not found by name|text|CSS|XPath`); + if (els.length === 0) { + throw new Error(`Option ${option} in ${select} was found neither by visible text not by value`); } - return this.browser.elementIdValue(el.value[0].ELEMENT, res.value); + commands = []; + els.forEach(clickOptionFn); + return this.unify(commands); }); }); - } + }); + } - /** - * {{> ../webapi/checkOption }} - */ - checkOption(field, context) { - let client = this.browser; - let clickMethod = this.browser.isMobile ? 'touchClick' : 'elementIdClick'; - if (context) { - client = client.element(withStrictLocator(context)); - } - return findCheckable(client, field).then((res) => { - if (!res.value || res.value.length === 0) { - throw new Error(`Checkable ${field} cant be located by name|text|CSS|XPath`); + /** + * {{> ../webapi/attachFile }} + */ + attachFile(locator, pathToFile) { + let file = path.join(global.codecept_dir, pathToFile); + if (!fileExists(file)) { + throw new Error(`File at ${file} can not be found on local system`); + } + return findFields(this.browser, locator).then((el) => { + this.debug("Uploading " + file); + return this.browser.uploadFile(file).then((res) => { + if (!el.value || el.value.length === 0) { + throw new Error(`File field ${locator} not found by name|text|CSS|XPath`); } - let elem = res.value[0]; - return client.elementIdSelected(elem.ELEMENT).then(function(isSelected) { - if (isSelected.value) return true; - return this[clickMethod](elem.ELEMENT); - }); + return this.browser.elementIdValue(el.value[0].ELEMENT, res.value); }); - } + }); + } - /** - * {{> ../webapi/grabTextFrom }} - */ - grabTextFrom(locator) { - return this.browser.getText(withStrictLocator(locator)).then(function(text) { - return text; - }); + /** + * {{> ../webapi/checkOption }} + */ + checkOption(field, context) { + let client = this.browser; + let clickMethod = this.browser.isMobile ? 'touchClick' : 'elementIdClick'; + if (context) { + client = client.element(withStrictLocator(context)); } - - /** - * Retrieves the innerHTML from an element located by CSS or XPath and returns it to test. - * Resumes test execution, so **should be used inside a generator with `yield`** operator. - * - * ```js - * let postHTML = yield I.grabHTMLFrom('#post'); - * ``` - */ - grabHTMLFrom(locator) { - return this.browser.getHTML(withStrictLocator(locator)).then(function(html) { - return html; + return findCheckable(client, field).then((res) => { + if (!res.value || res.value.length === 0) { + throw new Error(`Checkable ${field} cant be located by name|text|CSS|XPath`); + } + let elem = res.value[0]; + return client.elementIdSelected(elem.ELEMENT).then(function(isSelected) { + if (isSelected.value) return true; + return this[clickMethod](elem.ELEMENT); }); - } + }); + } - /** - * {{> ../webapi/grabValueFrom }} - */ - grabValueFrom(locator) { - return this.browser.getValue(withStrictLocator(locator)).then(function(text) { - return text; - }); - } + /** + * {{> ../webapi/grabTextFrom }} + */ + grabTextFrom(locator) { + return this.browser.getText(withStrictLocator(locator)).then(function(text) { + return text; + }); + } - /** - * {{> ../webapi/grabAttributeFrom }} - */ - grabAttributeFrom(locator, attr) { - return this.browser.getAttribute(withStrictLocator(locator), attr).then(function(text) { - return text; - }); - } + /** + * Retrieves the innerHTML from an element located by CSS or XPath and returns it to test. + * Resumes test execution, so **should be used inside a generator with `yield`** operator. + * + * ```js + * let postHTML = yield I.grabHTMLFrom('#post'); + * ``` + */ + grabHTMLFrom(locator) { + return this.browser.getHTML(withStrictLocator(locator)).then(function(html) { + return html; + }); + } - /** - * {{> ../webapi/seeInTitle }} - */ - seeInTitle(text) { - return this.browser.getTitle().then((title) => { - return stringIncludes('web page title').assert(text, title); - }); - } + /** + * {{> ../webapi/grabValueFrom }} + */ + grabValueFrom(locator) { + return this.browser.getValue(withStrictLocator(locator)).then(function(text) { + return text; + }); + } - /** - * {{> ../webapi/dontSeeInTitle }} - */ - dontSeeInTitle(text) { - return this.browser.getTitle().then((title) => { - return stringIncludes('web page title').negate(text, title); - }); - } + /** + * {{> ../webapi/grabAttributeFrom }} + */ + grabAttributeFrom(locator, attr) { + return this.browser.getAttribute(withStrictLocator(locator), attr).then(function(text) { + return text; + }); + } - /** - * {{> ../webapi/grabTitle }} - */ - grabTitle() { - return this.browser.getTitle().then((title) => { - this.debugSection('Title', title); - return title; - }); - } + /** + * {{> ../webapi/seeInTitle }} + */ + seeInTitle(text) { + return this.browser.getTitle().then((title) => { + return stringIncludes('web page title').assert(text, title); + }); + } - /** - * {{> ../webapi/see }} - */ - see(text, context) { - return proceedSee.call(this, 'assert', text, context); - } + /** + * {{> ../webapi/dontSeeInTitle }} + */ + dontSeeInTitle(text) { + return this.browser.getTitle().then((title) => { + return stringIncludes('web page title').negate(text, title); + }); + } - /** - * {{> ../webapi/dontSee }} - */ - dontSee(text, context) { - return proceedSee.call(this, 'negate', text, context); - } + /** + * {{> ../webapi/grabTitle }} + */ + grabTitle() { + return this.browser.getTitle().then((title) => { + this.debugSection('Title', title); + return title; + }); + } - /** - * {{> ../webapi/seeInField }} - */ - seeInField(field, value) { - return proceedSeeField.call(this, 'assert', field, value); - } + /** + * {{> ../webapi/see }} + */ + see(text, context) { + return proceedSee.call(this, 'assert', text, context); + } - /** - * {{> ../webapi/dontSeeInField }} - */ - dontSeeInField(field, value) { - return proceedSeeField.call(this, 'negate', field, value); - } + /** + * {{> ../webapi/dontSee }} + */ + dontSee(text, context) { + return proceedSee.call(this, 'negate', text, context); + } - /** - * {{> ../webapi/seeCheckboxIsChecked }} - */ - seeCheckboxIsChecked(field) { - return proceedSeeCheckbox.call(this, 'assert', field); - } + /** + * {{> ../webapi/seeInField }} + */ + seeInField(field, value) { + return proceedSeeField.call(this, 'assert', field, value); + } - /** - * {{> ../webapi/dontSeeCheckboxIsChecked }} - */ - dontSeeCheckboxIsChecked(field) { - return proceedSeeCheckbox.call(this, 'negate', field); - } + /** + * {{> ../webapi/dontSeeInField }} + */ + dontSeeInField(field, value) { + return proceedSeeField.call(this, 'negate', field, value); + } - /** - * {{> ../webapi/seeElement }} - */ - seeElement(locator) { - return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { - return truth(`elements of ${locator}`, 'to be seen').assert(res); - }); - } + /** + * {{> ../webapi/seeCheckboxIsChecked }} + */ + seeCheckboxIsChecked(field) { + return proceedSeeCheckbox.call(this, 'assert', field); + } - /** - * {{> ../webapi/dontSeeElement}} - */ - dontSeeElement(locator) { - return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { - return truth(`elements of ${locator}`, 'to be seen').negate(res); - }); - } + /** + * {{> ../webapi/dontSeeCheckboxIsChecked }} + */ + dontSeeCheckboxIsChecked(field) { + return proceedSeeCheckbox.call(this, 'negate', field); + } - /** - * {{> ../webapi/seeElementInDOM }} - */ - seeElementInDOM(locator) { - return this.browser.elements(withStrictLocator(locator)).then(function(res) { - return empty('elements').negate(res.value); - }); - } + /** + * {{> ../webapi/seeElement }} + */ + seeElement(locator) { + return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { + return truth(`elements of ${locator}`, 'to be seen').assert(res); + }); + } - /** - * {{> ../webapi/dontSeeElementInDOM }} - */ - dontSeeElementInDOM(locator) { - return this.browser.elements(withStrictLocator(locator)).then(function(res) { - return empty('elements').assert(res.value); - }); - } + /** + * {{> ../webapi/dontSeeElement}} + */ + dontSeeElement(locator) { + return this.browser.isVisible(withStrictLocator(locator)).then(function(res) { + return truth(`elements of ${locator}`, 'to be seen').negate(res); + }); + } - /** - * {{> ../webapi/seeInSource }} - */ - seeInSource(text) { - return this.browser.getSource().then((source) => { - return stringIncludes('HTML source of a page').assert(text, source); - }); - } + /** + * {{> ../webapi/seeElementInDOM }} + */ + seeElementInDOM(locator) { + return this.browser.elements(withStrictLocator(locator)).then(function(res) { + return empty('elements').negate(res.value); + }); + } - /** - * {{> ../webapi/dontSeeInSource }} - */ - dontSeeInSource(text) { - return this.browser.getSource().then((source) => { - return stringIncludes('HTML source of a page').negate(text, source); - }); - } + /** + * {{> ../webapi/dontSeeElementInDOM }} + */ + dontSeeElementInDOM(locator) { + return this.browser.elements(withStrictLocator(locator)).then(function(res) { + return empty('elements').assert(res.value); + }); + } - /** - * asserts that an element appears a given number of times in the DOM - * Element is located by label or name or CSS or XPath. - * - * ```js - * I.seeNumberOfElements('#submitBtn', 1); - * ``` - */ - seeNumberOfElements(selector, num) { - return this.browser.elements(withStrictLocator(selector)) - .then(function(res) { - return assert.equal(res.value.length, num); - }); - } + /** + * {{> ../webapi/seeInSource }} + */ + seeInSource(text) { + return this.browser.getSource().then((source) => { + return stringIncludes('HTML source of a page').assert(text, source); + }); + } - /** - * {{> ../webapi/seeInCurrentUrl }} - */ - seeInCurrentUrl(url) { - return this.browser.url().then(function(res) { - return stringIncludes('url').assert(url, res.value); - }); - } + /** + * {{> ../webapi/dontSeeInSource }} + */ + dontSeeInSource(text) { + return this.browser.getSource().then((source) => { + return stringIncludes('HTML source of a page').negate(text, source); + }); + } - /** - * {{> ../webapi/dontSeeInCurrentUrl }} - */ - dontSeeInCurrentUrl(url) { - return this.browser.url().then(function(res) { - return stringIncludes('url').negate(url, res.value); + /** + * asserts that an element appears a given number of times in the DOM + * Element is located by label or name or CSS or XPath. + * + * ```js + * I.seeNumberOfElements('#submitBtn', 1); + * ``` + */ + seeNumberOfElements(selector, num) { + return this.browser.elements(withStrictLocator(selector)) + .then(function(res) { + return assert.equal(res.value.length, num); }); - } + } - /** - * {{> ../webapi/seeCurrentUrlEquals }} - */ - seeCurrentUrlEquals(url) { - return this.browser.url().then((res) => { - return urlEquals(this.options.url).assert(url, res.value); - }); - } + /** + * {{> ../webapi/seeInCurrentUrl }} + */ + seeInCurrentUrl(url) { + return this.browser.url().then(function(res) { + return stringIncludes('url').assert(url, res.value); + }); + } - /** - * {{> ../webapi/dontSeeCurrentUrlEquals }} - */ - dontSeeCurrentUrlEquals(url) { - return this.browser.url().then((res) => { - return urlEquals(this.options.url).negate(url, res.value); - }); - } + /** + * {{> ../webapi/dontSeeInCurrentUrl }} + */ + dontSeeInCurrentUrl(url) { + return this.browser.url().then(function(res) { + return stringIncludes('url').negate(url, res.value); + }); + } - /** - * {{> ../webapi/executeScript }} - * - * Wraps [execute](http://webdriver.io/api/protocol/execute.html) command. - */ - executeScript(fn) { - return this.browser.execute.apply(this.browser, arguments).then((res) => res.value); - } + /** + * {{> ../webapi/seeCurrentUrlEquals }} + */ + seeCurrentUrlEquals(url) { + return this.browser.url().then((res) => { + return urlEquals(this.options.url).assert(url, res.value); + }); + } - /** - * {{> ../webapi/executeAsyncScript }} - */ - executeAsyncScript(fn) { - return this.browser.executeAsync.apply(this.browser, arguments).then((res) => res.value); - } + /** + * {{> ../webapi/dontSeeCurrentUrlEquals }} + */ + dontSeeCurrentUrlEquals(url) { + return this.browser.url().then((res) => { + return urlEquals(this.options.url).negate(url, res.value); + }); + } - /** - * Scrolls to element matched by locator. - * Extra shift can be set with offsetX and offsetY options - * - * ```js - * I.scrollTo('footer'); - * I.scrollTo('#submit', 5,5); - * ``` - */ - scrollTo(locator, offsetX, offsetY) { - return this.browser.scroll(withStrictLocator(locator), offsetX, offsetY); - } + /** + * {{> ../webapi/executeScript }} + * + * Wraps [execute](http://webdriver.io/api/protocol/execute.html) command. + */ + executeScript(fn) { + return this.browser.execute.apply(this.browser, arguments).then((res) => res.value); + } - /** - * {{> ../webapi/moveCursorTo}} - */ - moveCursorTo(locator, offsetX, offsetY) { - return this.browser.moveToObject(withStrictLocator(locator), offsetX, offsetY); - } + /** + * {{> ../webapi/executeAsyncScript }} + */ + executeAsyncScript(fn) { + return this.browser.executeAsync.apply(this.browser, arguments).then((res) => res.value); + } - /** - * {{> ../webapi/saveScreenshot}} - */ - saveScreenshot(fileName) { - let outputFile = path.join(global.output_dir, fileName); - this.debug('Screenshot has been saved to ' + outputFile); - return this.browser.saveScreenshot(outputFile); - } + /** + * Scrolls to element matched by locator. + * Extra shift can be set with offsetX and offsetY options + * + * ```js + * I.scrollTo('footer'); + * I.scrollTo('#submit', 5,5); + * ``` + */ + scrollTo(locator, offsetX, offsetY) { + return this.browser.scroll(withStrictLocator(locator), offsetX, offsetY); + } - /** - * {{> ../webapi/setCookie}} - * - * Uses Selenium's JSON [cookie format](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object). - */ - setCookie(cookie) { - return this.browser.setCookie(cookie); - } + /** + * {{> ../webapi/moveCursorTo}} + */ + moveCursorTo(locator, offsetX, offsetY) { + return this.browser.moveToObject(withStrictLocator(locator), offsetX, offsetY); + } - /** - * {{> ../webapi/clearCookie}} - */ - clearCookie(cookie) { - return this.browser.deleteCookie(cookie); - } + /** + * {{> ../webapi/saveScreenshot}} + */ + saveScreenshot(fileName) { + let outputFile = path.join(global.output_dir, fileName); + this.debug('Screenshot has been saved to ' + outputFile); + return this.browser.saveScreenshot(outputFile); + } - /** - * {{> ../webapi/clearField}} - */ - clearField(locator) { - return this.browser.clearElement(withStrictLocator(locator)); - } + /** + * {{> ../webapi/setCookie}} + * + * Uses Selenium's JSON [cookie format](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object). + */ + setCookie(cookie) { + return this.browser.setCookie(cookie); + } - /** - * {{> ../webapi/seeCookie}} - */ - seeCookie(name) { - return this.browser.getCookie(name).then(function(res) { - return truth('cookie ' + name, 'to be set').assert(res); - }); - } + /** + * {{> ../webapi/clearCookie}} + */ + clearCookie(cookie) { + return this.browser.deleteCookie(cookie); + } - /** - * {{> ../webapi/dontSeeCookie}} - */ - dontSeeCookie(name) { - return this.browser.getCookie(name).then(function(res) { - return truth('cookie ' + name, 'to be set').negate(res); - }); - } + /** + * {{> ../webapi/clearField}} + */ + clearField(locator) { + return this.browser.clearElement(withStrictLocator(locator)); + } - /** - * {{> ../webapi/grabCookie}} - */ - grabCookie(name) { - return this.browser.getCookie(name); - } + /** + * {{> ../webapi/seeCookie}} + */ + seeCookie(name) { + return this.browser.getCookie(name).then(function(res) { + return truth('cookie ' + name, 'to be set').assert(res); + }); + } - /** - * Accepts the active JavaScript native popup window, as created by window.alert|window.confirm|window.prompt. - * Don't confuse popups with modal windows, as created by [various libraries](http://jster.net/category/windows-modals-popups). - */ - acceptPopup() { - return this.browser.alertText().then(function(res) { - if (res !== null) { - return this.alertAccept(); - } - }); - } + /** + * {{> ../webapi/dontSeeCookie}} + */ + dontSeeCookie(name) { + return this.browser.getCookie(name).then(function(res) { + return truth('cookie ' + name, 'to be set').negate(res); + }); + } - /** - * Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt. - */ - cancelPopup() { - return this.browser.alertText().then(function(res) { - if (res !== null) { - return this.alertDismiss(); - } - }); - } + /** + * {{> ../webapi/grabCookie}} + */ + grabCookie(name) { + return this.browser.getCookie(name); + } - /** - * Checks that the active JavaScript popup, as created by `window.alert|window.confirm|window.prompt`, contains the given string. - */ - seeInPopup(text) { - return this.browser.alertText().then(function(res) { - if (res === null) { - throw new Error('Popup is not opened'); - } - stringIncludes('text in popup').assert(text, res); - }); - } + /** + * Accepts the active JavaScript native popup window, as created by window.alert|window.confirm|window.prompt. + * Don't confuse popups with modal windows, as created by [various libraries](http://jster.net/category/windows-modals-popups). + */ + acceptPopup() { + return this.browser.alertText().then(function(res) { + if (res !== null) { + return this.alertAccept(); + } + }); + } - /** - * {{> ../webapi/pressKey }} - * - * To make combinations with modifier and mouse clicks (like Ctrl+Click) press a modifier, click, then release it. - * - * ```js - * I.pressKey('Control'); - * I.click('#someelement'); - * I.pressKey('Control'); - * ``` - */ - pressKey(key) { - let modifier; - if (Array.isArray(key) && ~['Control', 'Command', 'Shift', 'Alt'].indexOf(key[0])) { - modifier = key[0]; + /** + * Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt. + */ + cancelPopup() { + return this.browser.alertText().then(function(res) { + if (res !== null) { + return this.alertDismiss(); } - return this.browser.keys(key).then(function() { - if (!modifier) return true; - return this.keys(modifier); // release modifeier - }); - } + }); + } - /** - * {{> ../webapi/resizeWindow }} - */ - resizeWindow(width, height) { - if (width === 'maximize') { - return this.browser.windowHandleMaximize(false); + /** + * Checks that the active JavaScript popup, as created by `window.alert|window.confirm|window.prompt`, contains the given string. + */ + seeInPopup(text) { + return this.browser.alertText().then(function(res) { + if (res === null) { + throw new Error('Popup is not opened'); } - return this.browser.windowHandleSize({ - width, - height - }); - } + stringIncludes('text in popup').assert(text, res); + }); + } - /** - * Drag an item to a destination element. - * - * ```js - * I.dragAndDrop('#dragHandle', '#container'); - * ``` - */ - dragAndDrop(srcElement, destElement) { - return this.browser.dragAndDrop( - withStrictLocator(srcElement), - withStrictLocator(destElement) - ); - } + /** + * {{> ../webapi/pressKey }} + * + * To make combinations with modifier and mouse clicks (like Ctrl+Click) press a modifier, click, then release it. + * + * ```js + * I.pressKey('Control'); + * I.click('#someelement'); + * I.pressKey('Control'); + * ``` + */ + pressKey(key) { + let modifier; + if (Array.isArray(key) && ~['Control', 'Command', 'Shift', 'Alt'].indexOf(key[0])) { + modifier = key[0]; + } + return this.browser.keys(key).then(function() { + if (!modifier) return true; + return this.keys(modifier); // release modifeier + }); + } - /** - * {{> ../webapi/wait }} - */ - wait(sec) { - return this.browser.pause(sec * 1000); + /** + * {{> ../webapi/resizeWindow }} + */ + resizeWindow(width, height) { + if (width === 'maximize') { + return this.browser.windowHandleMaximize(false); } + return this.browser.windowHandleSize({ + width, + height + }); + } - /** - * {{> ../webapi/waitForEnabled }} - */ - waitForEnabled(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForEnabled(withStrictLocator(locator), sec * 1000); - } + /** + * Drag an item to a destination element. + * + * ```js + * I.dragAndDrop('#dragHandle', '#container'); + * ``` + */ + dragAndDrop(srcElement, destElement) { + return this.browser.dragAndDrop( + withStrictLocator(srcElement), + withStrictLocator(destElement) + ); + } - /** - * {{> ../webapi/waitForElement }} - */ - waitForElement(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForExist(withStrictLocator(locator), sec * 1000); - } + /** + * {{> ../webapi/wait }} + */ + wait(sec) { + return this.browser.pause(sec * 1000); + } - /** - * {{> ../webapi/waitForText }} - */ - waitForText(text, sec, context) { - sec = sec || this.options.waitForTimeout; - context = context || 'body'; - return this.browser.waitUntil(function() { - return this.getText(context).then(function(source) { - if (Array.isArray(source)) { - return source.filter(part => part.indexOf(text) >= 0).length > 0; - } - return source.indexOf(text) >= 0; - }); - }, sec * 1000) - .catch((e) => { - if (e.type === 'WaitUntilTimeoutError') { - return proceedSee.call(this, 'assert', text, context); - } else { - throw e; + /** + * {{> ../webapi/waitForEnabled }} + */ + waitForEnabled(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForEnabled(withStrictLocator(locator), sec * 1000); + } + + /** + * {{> ../webapi/waitForElement }} + */ + waitForElement(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForExist(withStrictLocator(locator), sec * 1000); + } + + /** + * {{> ../webapi/waitForText }} + */ + waitForText(text, sec, context) { + sec = sec || this.options.waitForTimeout; + context = context || 'body'; + return this.browser.waitUntil(function() { + return this.getText(context).then(function(source) { + if (Array.isArray(source)) { + return source.filter(part => part.indexOf(text) >= 0).length > 0; } + return source.indexOf(text) >= 0; }); - } - - /** - * {{> ../webapi/waitForVisible }} - */ - waitForVisible(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForVisible(withStrictLocator(locator), sec * 1000); - } + }, sec * 1000) + .catch((e) => { + if (e.type === 'WaitUntilTimeoutError') { + return proceedSee.call(this, 'assert', text, context); + } else { + throw e; + } + }); + } - /** - * {{> ../webapi/waitForInvisible }} - */ - waitForInvisible(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForVisible(withStrictLocator(locator), sec * 1000, true); - } + /** + * {{> ../webapi/waitForVisible }} + */ + waitForVisible(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForVisible(withStrictLocator(locator), sec * 1000); + } - /** - * Waits for an element to become invisible on a page (by default waits for 1sec). - * Element can be located by CSS or XPath. - */ - waitToHide(locator, sec) { - return this.waitForInvisible(locator, sec); - } + /** + * {{> ../webapi/waitForInvisible }} + */ + waitForInvisible(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForVisible(withStrictLocator(locator), sec * 1000, true); + } - /** - * {{> ../webapi/waitForStalenessOf }} - */ - waitForStalenessOf(locator, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitForExist(withStrictLocator(locator), sec * 1000, true); - } + /** + * Waits for an element to become invisible on a page (by default waits for 1sec). + * Element can be located by CSS or XPath. + */ + waitToHide(locator, sec) { + return this.waitForInvisible(locator, sec); + } - /** - * Waits for a function to return true (waits for 1sec by default). - */ - waitUntil(fn, sec) { - sec = sec || this.options.waitForTimeout; - return this.browser.waitUntil(fn, sec); - } + /** + * {{> ../webapi/waitForStalenessOf }} + */ + waitForStalenessOf(locator, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitForExist(withStrictLocator(locator), sec * 1000, true); + } - /** - * Switches frame or in case of null locator reverts to parent. - */ - switchTo(locator) { - locator = locator || null; - return this.browser.frame(locator); - } + /** + * Waits for a function to return true (waits for 1sec by default). + */ + waitUntil(fn, sec) { + sec = sec || this.options.waitForTimeout; + return this.browser.waitUntil(fn, sec); } - function proceedSee(assertType, text, context) { - let description; - if (!context) { - context = this.context; - if (this.context === 'body') { - description = 'web page'; - } else { - description = 'current context ' + this.context; - } + /** + * Switches frame or in case of null locator reverts to parent. + */ + switchTo(locator) { + locator = locator || null; + return this.browser.frame(locator); + } +} + +function proceedSee(assertType, text, context) { + let description; + if (!context) { + context = this.context; + if (this.context === 'body') { + description = 'web page'; } else { - description = 'element ' + context; + description = 'current context ' + this.context; } - return this.browser.getText(withStrictLocator(context)).then(function(source) { - return stringIncludes(description)[assertType](text, source); - }); + } else { + description = 'element ' + context; } - - function findClickable(client, locator) { - if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); - if (isCSSorXPathLocator(locator)) return client.elements(locator); - - let literal = xpathLocator.literal(locator); - - let narrowLocator = xpathLocator.combine([ - `.//a[normalize-space(.)=${literal}]`, - `.//button[normalize-space(.)=${literal}]`, - `.//a/img[normalize-space(@alt)=${literal}]/ancestor::a`, - `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][normalize-space(@value)=${literal}]` + return this.browser.getText(withStrictLocator(context)).then(function(source) { + return stringIncludes(description)[assertType](text, source); + }); +} + +function findClickable(client, locator) { + if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); + if (isCSSorXPathLocator(locator)) return client.elements(locator); + + let literal = xpathLocator.literal(locator); + + let narrowLocator = xpathLocator.combine([ + `.//a[normalize-space(.)=${literal}]`, + `.//button[normalize-space(.)=${literal}]`, + `.//a/img[normalize-space(@alt)=${literal}]/ancestor::a`, + `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][normalize-space(@value)=${literal}]` + ]); + return client.elements(narrowLocator).then(function(els) { + if (els.value.length) { + return els; + } + let wideLocator = xpathLocator.combine([ + `.//a[./@href][((contains(normalize-space(string(.)), ${literal})) or .//img[contains(./@alt, ${literal})])]`, + `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][contains(./@value, ${literal})]`, + `.//input[./@type = 'image'][contains(./@alt, ${literal})]`, + `.//button[contains(normalize-space(string(.)), ${literal})]`, + `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][./@name = ${literal}]`, + `.//button[./@name = ${literal}]` ]); - return client.elements(narrowLocator).then(function(els) { + return client.elements(wideLocator).then(function(els) { if (els.value.length) { return els; } - let wideLocator = xpathLocator.combine([ - `.//a[./@href][((contains(normalize-space(string(.)), ${literal})) or .//img[contains(./@alt, ${literal})])]`, - `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][contains(./@value, ${literal})]`, - `.//input[./@type = 'image'][contains(./@alt, ${literal})]`, - `.//button[contains(normalize-space(string(.)), ${literal})]`, - `.//input[./@type = 'submit' or ./@type = 'image' or ./@type = 'button'][./@name = ${literal}]`, - `.//button[./@name = ${literal}]` - ]); - return client.elements(wideLocator).then(function(els) { - if (els.value.length) { - return els; - } - return client.elements(locator); // by css or xpath - }); + return client.elements(locator); // by css or xpath }); - } - - function findFields(client, locator) { - if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); - if (isCSSorXPathLocator(locator)) return client.elements(locator); - - let literal = xpathLocator.literal(locator); - let byText = xpathLocator.combine([ - `.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][(((./@name = ${literal}) or ./@id = //label[contains(normalize-space(string(.)), ${literal})]/@for) or ./@placeholder = ${literal})]`, - `.//label[contains(normalize-space(string(.)), ${literal})]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]` - ]); - return client.elements(byText).then((els) => { + }); +} + +function findFields(client, locator) { + if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); + if (isCSSorXPathLocator(locator)) return client.elements(locator); + + let literal = xpathLocator.literal(locator); + let byText = xpathLocator.combine([ + `.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][(((./@name = ${literal}) or ./@id = //label[contains(normalize-space(string(.)), ${literal})]/@for) or ./@placeholder = ${literal})]`, + `.//label[contains(normalize-space(string(.)), ${literal})]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]` + ]); + return client.elements(byText).then((els) => { + if (els.value.length) return els; + let byName = `.//*[self::input | self::textarea | self::select][@name = ${literal}]`; + return client.elements(byName).then((els) => { if (els.value.length) return els; - let byName = `.//*[self::input | self::textarea | self::select][@name = ${literal}]`; - return client.elements(byName).then((els) => { - if (els.value.length) return els; - return client.elements(locator); // by css or xpath - }); + return client.elements(locator); // by css or xpath }); - } + }); +} - function proceedSeeField(assertType, field, value) { - return findFields(this.browser, field).then(function(res) { - if (!res.value || res.value.length === 0) { - throw new Error(`Field ${field} not found by name|text|CSS|XPath`); - } +function proceedSeeField(assertType, field, value) { + return findFields(this.browser, field).then(function(res) { + if (!res.value || res.value.length === 0) { + throw new Error(`Field ${field} not found by name|text|CSS|XPath`); + } - var proceedMultiple = (fields) => { - let commands = []; - fields.forEach((el) => commands.push(this.elementIdSelected(el.ELEMENT))); - this.unify(commands).then((res) => { - commands = []; - fields.forEach((el) => { - if (el.value === false) return; - commands.push(this.elementIdAttribute(el.ELEMENT, 'value')); - }); - this.unify(commands, { - extractValue: true - }).then((val) => { - return stringIncludes('fields by ' + field)[assertType](value, val); - }); + var proceedMultiple = (fields) => { + let commands = []; + fields.forEach((el) => commands.push(this.elementIdSelected(el.ELEMENT))); + this.unify(commands).then((res) => { + commands = []; + fields.forEach((el) => { + if (el.value === false) return; + commands.push(this.elementIdAttribute(el.ELEMENT, 'value')); }); - } - - var proceedSingle = (el) => { - return this.elementIdAttribute(el.ELEMENT, 'value').then((res) => { - return stringIncludes('fields by ' + field)[assertType](value, res.value); + this.unify(commands, { + extractValue: true + }).then((val) => { + return stringIncludes('fields by ' + field)[assertType](value, val); }); - } - - return this.elementIdName(res.value[0].ELEMENT).then((tag) => { - if (tag.value == 'select') { - return proceedMultiple(res.value); - } - - if (tag.value == 'input') { - return this.elementIdAttribute(res.value[0].ELEMENT, 'type').then((type) => { - if (type.value == 'checkbox' || type.value == 'radio') { - return proceedMultiple(res.value); - } - return proceedSingle(res.value[0]); - }); - } - return proceedSingle(res.value[0]); }); - }); - } + } - function proceedSeeCheckbox(assertType, field) { - return findFields(this.browser, field).then(function(res) { - if (!res.value || res.value.length === 0) { - throw new Error(`Field ${field} not found by name|text|CSS|XPath`); - } - let commands = []; - res.value.forEach((el) => commands.push(this.elementIdSelected(el.ELEMENT))); - return this.unify(commands, { - extractValue: true - }).then((selected) => { - return truth(`checkable field ${field}`, 'to be checked')[assertType](selected); + var proceedSingle = (el) => { + return this.elementIdAttribute(el.ELEMENT, 'value').then((res) => { + return stringIncludes('fields by ' + field)[assertType](value, res.value); }); - }); - } + } - function findCheckable(client, locator) { - if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); - if (isCSSorXPathLocator(locator)) return client.elements(locator); + return this.elementIdName(res.value[0].ELEMENT).then((tag) => { + if (tag.value == 'select') { + return proceedMultiple(res.value); + } - let literal = xpathLocator.literal(locator); - let byText = xpathLocator.combine([ - `.//input[@type = 'checkbox' or @type = 'radio'][(@id = //label[contains(normalize-space(string(.)), ${literal})]/@for) or @placeholder = ${literal}]`, - `.//label[contains(normalize-space(string(.)), ${literal})]//input[@type = 'radio' or @type = 'checkbox']` - ]); - return client.elements(byText).then(function(els) { + if (tag.value == 'input') { + return this.elementIdAttribute(res.value[0].ELEMENT, 'type').then((type) => { + if (type.value == 'checkbox' || type.value == 'radio') { + return proceedMultiple(res.value); + } + return proceedSingle(res.value[0]); + }); + } + return proceedSingle(res.value[0]); + }); + }); +} + +function proceedSeeCheckbox(assertType, field) { + return findFields(this.browser, field).then(function(res) { + if (!res.value || res.value.length === 0) { + throw new Error(`Field ${field} not found by name|text|CSS|XPath`); + } + let commands = []; + res.value.forEach((el) => commands.push(this.elementIdSelected(el.ELEMENT))); + return this.unify(commands, { + extractValue: true + }).then((selected) => { + return truth(`checkable field ${field}`, 'to be checked')[assertType](selected); + }); + }); +} + +function findCheckable(client, locator) { + if (typeof(locator) === 'object') return client.elements(withStrictLocator(locator)); + if (isCSSorXPathLocator(locator)) return client.elements(locator); + + let literal = xpathLocator.literal(locator); + let byText = xpathLocator.combine([ + `.//input[@type = 'checkbox' or @type = 'radio'][(@id = //label[contains(normalize-space(string(.)), ${literal})]/@for) or @placeholder = ${literal}]`, + `.//label[contains(normalize-space(string(.)), ${literal})]//input[@type = 'radio' or @type = 'checkbox']` + ]); + return client.elements(byText).then(function(els) { + if (els.value.length) return els; + let byName = `.//input[@type = 'checkbox' or @type = 'radio'][@name = ${literal}]`; + return client.elements(byName).then(function(els) { if (els.value.length) return els; - let byName = `.//input[@type = 'checkbox' or @type = 'radio'][@name = ${literal}]`; - return client.elements(byName).then(function(els) { - if (els.value.length) return els; - return client.elements(locator); // by css or xpath - }); + return client.elements(locator); // by css or xpath }); - } + }); +} - function isCSSorXPathLocator(locator) { - if (locator[0] === '#' || locator[0] === '.') { - return true; - } - if (locator.substr(0, 2) === '//') { - return true; - } - return false; - } - - function withStrictLocator(locator) { - if (!locator) return null; - if (typeof(locator) !== 'object') return locator; - let key = Object.keys(locator)[0]; - let value = locator[key]; - - locator.toString = () => `{${key}: '${value}'}`; - - switch (key) { - case 'by': - case 'xpath': - case 'css': - return value; - case 'id': - return '#' + value; - case 'name': - return `[name="${value}"]`; - } +function isCSSorXPathLocator(locator) { + if (locator[0] === '#' || locator[0] === '.') { + return true; + } + if (locator.substr(0, 2) === '//') { + return true; + } + return false; +} + +function withStrictLocator(locator) { + if (!locator) return null; + if (typeof(locator) !== 'object') return locator; + let key = Object.keys(locator)[0]; + let value = locator[key]; + + locator.toString = () => `{${key}: '${value}'}`; + + switch (key) { + case 'by': + case 'xpath': + case 'css': + return value; + case 'id': + return '#' + value; + case 'name': + return `[name="${value}"]`; } +} - module.exports = WebDriverIO; +module.exports = WebDriverIO; From ec70fac6c2e2550a277ed9cc00ca5d597ad06024 Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 13:04:43 +0300 Subject: [PATCH 035/111] remove timeouts for appium --- lib/helper/WebDriverIO.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index e4f620109..c35f6dd98 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -296,7 +296,8 @@ class WebDriverIO extends Helper { this.browser = webdriverio.remote(this.options).init(); } - if (this.options.timeouts) { + //TODO: maybe its working in browser on mobile? + if (!this.options.desiredCapabilities.platformName && this.options.timeouts) { this.defineTimeout(this.options.timeouts); } @@ -417,15 +418,19 @@ class WebDriverIO extends Helper { * I.defineTimeout({ implicit: 10000, "page load": 10000, script: 5000 }); * ``` */ + + //TODO: maybe its working in browser on mobile? defineTimeout(timeouts) { - if (timeouts.implicit) { - this.browser.timeouts('implicit', timeouts.implicit); - } - if (timeouts['page load']) { - this.browser.timeouts('page load', timeouts['page load']); - } - if (timeouts.script) { - this.browser.timeouts('script', timeouts.script); + if (!this.options.desiredCapabilities.platformName) { + if (timeouts.implicit) { + this.browser.timeouts('implicit', timeouts.implicit); + } + if (timeouts['page load']) { + this.browser.timeouts('page load', timeouts['page load']); + } + if (timeouts.script) { + this.browser.timeouts('script', timeouts.script); + } } } From 06cdcdacb348751bf41986658af9b915225fac29 Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 13:15:45 +0300 Subject: [PATCH 036/111] try timeouts for browser --- lib/helper/WebDriverIO.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index c35f6dd98..5e1f90b4e 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -297,9 +297,9 @@ class WebDriverIO extends Helper { } //TODO: maybe its working in browser on mobile? - if (!this.options.desiredCapabilities.platformName && this.options.timeouts) { - this.defineTimeout(this.options.timeouts); - } + //if (!this.options.desiredCapabilities.platformName && this.options.timeouts) { + this.defineTimeout(this.options.timeouts); + //} if (this.options.windowSize === 'maximize') { this.browser.windowHandleMaximize(false); @@ -333,6 +333,7 @@ class WebDriverIO extends Helper { if (!this.options.restart) return this.browser.end(); } + //TODO: test with app _failed(test) { let fileName = test.title.replace(/ /g, '_') + '.failed.png'; return this.saveScreenshot(fileName); From 85c2590ba11bf7702d98553d799e2b95990adc69 Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 13:20:52 +0300 Subject: [PATCH 037/111] add browser support --- lib/helper/WebDriverIO.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 5e1f90b4e..c33fbc02c 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -240,7 +240,7 @@ class WebDriverIO extends Helper { } } `); - } else if ((this.options.desiredCapabilities.platformName == "Android") && (!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion)) { + } else if ((this.options.desiredCapabilities.platformName == "Android") && ((!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion) || (!this.options.desiredCapabilities.browserName))) { throw new Error(` WebDriverIO requires at least these parameters to run in Appium mode for Android Check your codeceptjs config file to ensure these are set properly From 865a4f2db4a785477be37eb15b017fe3065b5e6e Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 13:26:39 +0300 Subject: [PATCH 038/111] Experiment --- lib/helper/WebDriverIO.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index c33fbc02c..88c587bdc 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -206,7 +206,8 @@ class WebDriverIO extends Helper { this.options.baseUrl = this.options.url || this.options.baseUrl; this.options.desiredCapabilities.browserName = this.options.browser || this.options.desiredCapabilities.browserName; this.options.waitForTimeout /= 1000; // convert to seconds - + console.log((!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion)); + console.log((!this.options.desiredCapabilities.browserName)); if (!this.options.desiredCapabilities.platformName) { if (!this.options.url || !this.options.browser) { throw new Error(` From 849c9544bed9bee0e0a683737a357b649de4fef7 Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 13:30:45 +0300 Subject: [PATCH 039/111] fix --- lib/helper/WebDriverIO.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 88c587bdc..6bbecbc98 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -206,8 +206,7 @@ class WebDriverIO extends Helper { this.options.baseUrl = this.options.url || this.options.baseUrl; this.options.desiredCapabilities.browserName = this.options.browser || this.options.desiredCapabilities.browserName; this.options.waitForTimeout /= 1000; // convert to seconds - console.log((!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion)); - console.log((!this.options.desiredCapabilities.browserName)); + if (!this.options.desiredCapabilities.platformName) { if (!this.options.url || !this.options.browser) { throw new Error(` @@ -224,7 +223,7 @@ class WebDriverIO extends Helper { `); } } else { - if ((this.options.desiredCapabilities.platformName == "iOS") && (!this.options.desiredCapabilities.app || !this.options.desiredCapabilities.deviceName || !this.options.desiredCapabilities.platformVersion)) { + if ((this.options.desiredCapabilities.platformName == "iOS") && ((!this.options.desiredCapabilities.app || !this.options.desiredCapabilities.deviceName || !this.options.desiredCapabilities.platformVersion) && (!this.options.desiredCapabilities.browserName))) { throw new Error(` WebDriverIO requires at least these parameters to run in Appium mode for iOS Check your codeceptjs config file to ensure these are set properly @@ -241,7 +240,7 @@ class WebDriverIO extends Helper { } } `); - } else if ((this.options.desiredCapabilities.platformName == "Android") && ((!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion) || (!this.options.desiredCapabilities.browserName))) { + } else if ((this.options.desiredCapabilities.platformName == "Android") && ((!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion) && (!this.options.desiredCapabilities.browserName))) { throw new Error(` WebDriverIO requires at least these parameters to run in Appium mode for Android Check your codeceptjs config file to ensure these are set properly From 87814f6b32c5fd09d52d2626715b6850c9589ab1 Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 13:39:15 +0300 Subject: [PATCH 040/111] experimental 2 --- lib/helper/WebDriverIO.js | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 6bbecbc98..5b77bd017 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -239,6 +239,19 @@ class WebDriverIO extends Helper { } } } + or + { + "helpers": { + "WebDriverIO": { + desiredCapabilities: { + platformName: "iOS", + browserName: "Safari", + deviceName: "iPhone Simulator", + platformVersion: "7.1" + } + } + } + } `); } else if ((this.options.desiredCapabilities.platformName == "Android") && ((!this.options.desiredCapabilities.appPackage || !this.options.desiredCapabilities.appActivity || !this.options.desiredCapabilities.platformVersion) && (!this.options.desiredCapabilities.browserName))) { throw new Error(` @@ -256,6 +269,18 @@ class WebDriverIO extends Helper { } } } + or + { + "helpers": { + "WebDriverIO": { + desiredCapabilities: { + platformName: "Android", + browserName: "Chrome", + platformVersion: "6.0.1" + } + } + } + } `); } } @@ -295,11 +320,10 @@ class WebDriverIO extends Helper { } else { this.browser = webdriverio.remote(this.options).init(); } - - //TODO: maybe its working in browser on mobile? - //if (!this.options.desiredCapabilities.platformName && this.options.timeouts) { - this.defineTimeout(this.options.timeouts); - //} + console.log(!this.options.desiredCapabilities.browserName); + if (!this.options.desiredCapabilities.browserName) { + this.defineTimeout(this.options.timeouts); + } if (this.options.windowSize === 'maximize') { this.browser.windowHandleMaximize(false); @@ -333,7 +357,6 @@ class WebDriverIO extends Helper { if (!this.options.restart) return this.browser.end(); } - //TODO: test with app _failed(test) { let fileName = test.title.replace(/ /g, '_') + '.failed.png'; return this.saveScreenshot(fileName); From e50a4bba1c8566e426aa537bcf650fc2396a1788 Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 17:08:26 +0300 Subject: [PATCH 041/111] More methods tested --- lib/helper/WebDriverIO.js | 131 ++++++++++++++++++++++++++------------ 1 file changed, 91 insertions(+), 40 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 5b77bd017..d92669709 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -320,8 +320,8 @@ class WebDriverIO extends Helper { } else { this.browser = webdriverio.remote(this.options).init(); } - console.log(!this.options.desiredCapabilities.browserName); - if (!this.options.desiredCapabilities.browserName) { + + if (this.options.desiredCapabilities.browserName) { this.defineTimeout(this.options.timeouts); } @@ -341,7 +341,8 @@ class WebDriverIO extends Helper { _before() { if (this.options.restart && !this.options.manualStart) this._startBrowser(); this.failedTestName = null; - this.context = 'body'; + if (this.options.desiredCapabilities.browserName) this.context = 'body'; + else this.context = "//android.widget.FrameLayout" return this.browser; } @@ -445,7 +446,7 @@ class WebDriverIO extends Helper { //TODO: maybe its working in browser on mobile? defineTimeout(timeouts) { - if (!this.options.desiredCapabilities.platformName) { + if (this.options.desiredCapabilities.browserName) { if (timeouts.implicit) { this.browser.timeouts('implicit', timeouts.implicit); } @@ -462,10 +463,14 @@ class WebDriverIO extends Helper { * {{> ../webapi/amOnPage }} */ amOnPage(url) { - return this.browser.url(url).url((err, res) => { - if (err) throw err; - this.debugSection('Url', res.value); - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.url(url).url((err, res) => { + if (err) throw err; + this.debugSection('Url', res.value); + }); + } else { + throw new Error(`amOnPage method can't be used in apps testing`); + } } /** @@ -491,23 +496,29 @@ class WebDriverIO extends Helper { * {{> ../webapi/doubleClick }} */ doubleClick(locator, context) { - let client = this.browser; - if (context) { - client = client.element(context); - } - return findClickable(client, locator).then(function(res) { - if (!res.value || res.value.length === 0) { - if (typeof(locator) === "object") locator = JSON.stringify(locator); - throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); + if (this.options.desiredCapabilities.browserName) { + let client = this.browser; + if (context) { + client = client.element(context); } - let elem = res.value[0]; - return this.moveTo(elem.ELEMENT).doDoubleClick(); - }); + return findClickable(client, locator).then(function(res) { + if (!res.value || res.value.length === 0) { + if (typeof(locator) === "object") locator = JSON.stringify(locator); + throw new Error(`Clickable element ${locator.toString()} was not found by text|CSS|XPath`); + } + let elem = res.value[0]; + return this.moveTo(elem.ELEMENT).doDoubleClick(); + }); + } else { + throw new Error(`doubleClick method can't be used in apps testing`); + } } /** * Performs right click on an element matched by CSS or XPath. */ + + //TODO: works but should? rightClick(locator) { return this.browser.rightClick(withStrictLocator(locator)); } @@ -515,6 +526,7 @@ class WebDriverIO extends Helper { /** * {{> ../webapi/fillField }} */ + //TODO: Works via buffer fillField(field, value) { return findFields(this.browser, field).then(function(res) { if (!res.value || res.value.length === 0) { @@ -528,6 +540,7 @@ class WebDriverIO extends Helper { /** * {{> ../webapi/appendField }} */ + //TODO: it's clear a field before insert appendField(field, value) { return findFields(this.browser, field).then(function(res) { if (!res.value || res.value.length === 0) { @@ -542,6 +555,7 @@ class WebDriverIO extends Helper { * {{> ../webapi/selectOption}} * */ + //TODO: not working in Appium apps selectOption(select, option) { return findFields(this.browser, select).then(function(res) { if (!res.value || res.value.length === 0) { @@ -599,6 +613,7 @@ class WebDriverIO extends Helper { /** * {{> ../webapi/attachFile }} */ + //TODO: not tested in appium attachFile(locator, pathToFile) { let file = path.join(global.codecept_dir, pathToFile); if (!fileExists(file)) { @@ -618,6 +633,7 @@ class WebDriverIO extends Helper { /** * {{> ../webapi/checkOption }} */ + //TODO: not tested in Appium checkOption(field, context) { let client = this.browser; let clickMethod = this.browser.isMobile ? 'touchClick' : 'elementIdClick'; @@ -639,6 +655,7 @@ class WebDriverIO extends Helper { /** * {{> ../webapi/grabTextFrom }} */ + //TODO: works in appium grabTextFrom(locator) { return this.browser.getText(withStrictLocator(locator)).then(function(text) { return text; @@ -653,56 +670,84 @@ class WebDriverIO extends Helper { * let postHTML = yield I.grabHTMLFrom('#post'); * ``` */ + + //TODO: can't be used in app tests grabHTMLFrom(locator) { - return this.browser.getHTML(withStrictLocator(locator)).then(function(html) { - return html; - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.getHTML(withStrictLocator(locator)).then(function(html) { + return html; + }); + } else { + throw new Error(`grabHTMLFrom method can't be used in apps testing`); + } } /** * {{> ../webapi/grabValueFrom }} */ + //TODO: is not supported by apps (no values!) grabValueFrom(locator) { - return this.browser.getValue(withStrictLocator(locator)).then(function(text) { - return text; - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.getValue(withStrictLocator(locator)).then(function(text) { + return text; + }); + } else { + throw new Error(`grabValueFrom method can't be used in apps testing, use grabTextFrom or grabAttributeFrom`); + } } /** * {{> ../webapi/grabAttributeFrom }} */ + //TODO: can be used for apps only with several values grabAttributeFrom(locator, attr) { - return this.browser.getAttribute(withStrictLocator(locator), attr).then(function(text) { - return text; - }); + if ((this.options.desiredCapabilities.browserName) || (attr === "contentDescription" || attr === "text" || attr === "className" || attr === "resourceId")) { + return this.browser.getAttribute(withStrictLocator(locator), attr).then(function(text) { + return text; + }); + } else { + throw new Error(`grabValueFrom method can't be used in apps testing, use grabTextFrom or grabAttributeFrom`); + } } /** * {{> ../webapi/seeInTitle }} */ seeInTitle(text) { - return this.browser.getTitle().then((title) => { - return stringIncludes('web page title').assert(text, title); - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.getTitle().then((title) => { + return stringIncludes('web page title').assert(text, title); + }); + } else { + throw new Error(`seeInTitle method can't be used in apps testing, there is no title in app`); + } } /** * {{> ../webapi/dontSeeInTitle }} */ dontSeeInTitle(text) { - return this.browser.getTitle().then((title) => { - return stringIncludes('web page title').negate(text, title); - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.getTitle().then((title) => { + return stringIncludes('web page title').negate(text, title); + }); + } else { + throw new Error(`dontSeeInTitle method can't be used in apps testing, there is no title in app`); + } } /** * {{> ../webapi/grabTitle }} */ grabTitle() { - return this.browser.getTitle().then((title) => { - this.debugSection('Title', title); - return title; - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.getTitle().then((title) => { + this.debugSection('Title', title); + return title; + }); + } else { + throw new Error(`grabTitle method can't be used in apps testing, there is no title in app`); + } } /** @@ -1276,7 +1321,10 @@ function findCheckable(client, locator) { function isCSSorXPathLocator(locator) { if (locator[0] === '#' || locator[0] === '.') { - return true; + if (this.options.desiredCapabilities.browserName) + return true; + //TODO: add accessibility id, -android uiauto support + else throw new Error(`Unable to use css locators in apps. Locator strategies for this request: xpath, id, class name`); } if (locator.substr(0, 2) === '//') { return true; @@ -1295,8 +1343,11 @@ function withStrictLocator(locator) { switch (key) { case 'by': case 'xpath': - case 'css': return value; + case 'css': + if (this.options.desiredCapabilities.browserName) + return value; + else throw new Error(`Unable to use css locators in apps. Locator strategies for this request: xpath, id, class name`); case 'id': return '#' + value; case 'name': From e74b6b8c332078a8cd085a2acc319754617aad2d Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 18:38:25 +0300 Subject: [PATCH 042/111] New changes --- lib/helper/WebDriverIO.js | 197 +++++++++++++++++++++++++++----------- 1 file changed, 142 insertions(+), 55 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index d92669709..8994b8505 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -341,8 +341,7 @@ class WebDriverIO extends Helper { _before() { if (this.options.restart && !this.options.manualStart) this._startBrowser(); this.failedTestName = null; - if (this.options.desiredCapabilities.browserName) this.context = 'body'; - else this.context = "//android.widget.FrameLayout" + this.context = 'body'; return this.browser; } @@ -754,33 +753,50 @@ class WebDriverIO extends Helper { * {{> ../webapi/see }} */ see(text, context) { - return proceedSee.call(this, 'assert', text, context); + if ((this.options.desiredCapabilities.browserName) || (context)) { + return proceedSee.call(this, 'assert', text, context); + } else { + throw new Error(`see method can be used in apps testing only with context`); + } } /** * {{> ../webapi/dontSee }} */ dontSee(text, context) { - return proceedSee.call(this, 'negate', text, context); + if ((this.options.desiredCapabilities.browserName) || (context)) { + return proceedSee.call(this, 'negate', text, context); + } else { + throw new Error(`dontSee method can be used in apps testing only with context`); + } } /** * {{> ../webapi/seeInField }} */ seeInField(field, value) { - return proceedSeeField.call(this, 'assert', field, value); + if (this.options.desiredCapabilities.browserName) { + return proceedSeeField.call(this, 'assert', field, value); + } else { + throw new Error(`seeInField method can be used in apps testing only with context`); + } } /** * {{> ../webapi/dontSeeInField }} */ dontSeeInField(field, value) { - return proceedSeeField.call(this, 'negate', field, value); + if (this.options.desiredCapabilities.browserName) { + return proceedSeeField.call(this, 'negate', field, value); + } else { + throw new Error(`dontSeeInField method can be used in apps testing only with context`); + } } /** * {{> ../webapi/seeCheckboxIsChecked }} */ + //TODO: not tested in Appium seeCheckboxIsChecked(field) { return proceedSeeCheckbox.call(this, 'assert', field); } @@ -788,6 +804,7 @@ class WebDriverIO extends Helper { /** * {{> ../webapi/dontSeeCheckboxIsChecked }} */ + //TODO: not tested in Appium dontSeeCheckboxIsChecked(field) { return proceedSeeCheckbox.call(this, 'negate', field); } @@ -865,36 +882,52 @@ class WebDriverIO extends Helper { * {{> ../webapi/seeInCurrentUrl }} */ seeInCurrentUrl(url) { - return this.browser.url().then(function(res) { - return stringIncludes('url').assert(url, res.value); - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.url().then(function(res) { + return stringIncludes('url').assert(url, res.value); + }); + } else { + throw new Error(`seeInCurrentUrl method can't be used in apps. There is no URL`); + } } /** * {{> ../webapi/dontSeeInCurrentUrl }} */ dontSeeInCurrentUrl(url) { - return this.browser.url().then(function(res) { - return stringIncludes('url').negate(url, res.value); - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.url().then(function(res) { + return stringIncludes('url').negate(url, res.value); + }); + } else { + throw new Error(`dontSeeInCurrentUrl method can't be used in apps. There is no URL`); + } } /** * {{> ../webapi/seeCurrentUrlEquals }} */ seeCurrentUrlEquals(url) { - return this.browser.url().then((res) => { - return urlEquals(this.options.url).assert(url, res.value); - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.url().then((res) => { + return urlEquals(this.options.url).assert(url, res.value); + }); + } else { + throw new Error(`seeCurrentUrlEquals method can't be used in apps. There is no URL`); + } } /** * {{> ../webapi/dontSeeCurrentUrlEquals }} */ dontSeeCurrentUrlEquals(url) { - return this.browser.url().then((res) => { - return urlEquals(this.options.url).negate(url, res.value); - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.url().then((res) => { + return urlEquals(this.options.url).negate(url, res.value); + }); + } else { + throw new Error(`seeCurrentUrlEquals method can't be used in apps. There is no URL`); + } } /** @@ -903,14 +936,22 @@ class WebDriverIO extends Helper { * Wraps [execute](http://webdriver.io/api/protocol/execute.html) command. */ executeScript(fn) { - return this.browser.execute.apply(this.browser, arguments).then((res) => res.value); + if (this.options.desiredCapabilities.browserName) { + return this.browser.execute.apply(this.browser, arguments).then((res) => res.value); + } else { + throw new Error(`executeScript method can't be used in apps.`); + } } /** * {{> ../webapi/executeAsyncScript }} */ executeAsyncScript(fn) { - return this.browser.executeAsync.apply(this.browser, arguments).then((res) => res.value); + if (this.options.desiredCapabilities.browserName) { + return this.browser.executeAsync.apply(this.browser, arguments).then((res) => res.value); + } else { + throw new Error(`executeAsyncScript method can't be used in apps.`); + } } /** @@ -923,14 +964,22 @@ class WebDriverIO extends Helper { * ``` */ scrollTo(locator, offsetX, offsetY) { - return this.browser.scroll(withStrictLocator(locator), offsetX, offsetY); + if (this.options.desiredCapabilities.browserName) { + return this.browser.scroll(withStrictLocator(locator), offsetX, offsetY); + } else { + throw new Error(`scrollTo method can't be used in apps.`); + } } /** * {{> ../webapi/moveCursorTo}} */ moveCursorTo(locator, offsetX, offsetY) { - return this.browser.moveToObject(withStrictLocator(locator), offsetX, offsetY); + if (this.options.desiredCapabilities.browserName) { + return this.browser.moveToObject(withStrictLocator(locator), offsetX, offsetY); + } else { + throw new Error(`moveCursorTo method can't be used in apps.`); + } } /** @@ -948,14 +997,22 @@ class WebDriverIO extends Helper { * Uses Selenium's JSON [cookie format](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object). */ setCookie(cookie) { - return this.browser.setCookie(cookie); + if (this.options.desiredCapabilities.browserName) { + return this.browser.setCookie(cookie); + } else { + throw new Error(`setCookie method can't be used in apps. it hasn't Cookie storage`); + } } /** * {{> ../webapi/clearCookie}} */ clearCookie(cookie) { - return this.browser.deleteCookie(cookie); + if (this.options.desiredCapabilities.browserName) { + return this.browser.deleteCookie(cookie); + } else { + throw new Error(`clearCookie method can't be used in apps. it hasn't Cookie storage`); + } } /** @@ -969,25 +1026,37 @@ class WebDriverIO extends Helper { * {{> ../webapi/seeCookie}} */ seeCookie(name) { - return this.browser.getCookie(name).then(function(res) { - return truth('cookie ' + name, 'to be set').assert(res); - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.getCookie(name).then(function(res) { + return truth('cookie ' + name, 'to be set').assert(res); + }); + } else { + throw new Error(`seeCookie method can't be used in apps. it hasn't Cookie storage`); + } } /** * {{> ../webapi/dontSeeCookie}} */ dontSeeCookie(name) { - return this.browser.getCookie(name).then(function(res) { - return truth('cookie ' + name, 'to be set').negate(res); - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.getCookie(name).then(function(res) { + return truth('cookie ' + name, 'to be set').negate(res); + }); + } else { + throw new Error(`dontSeeCookie method can't be used in apps. it hasn't Cookie storage`); + } } /** * {{> ../webapi/grabCookie}} */ grabCookie(name) { - return this.browser.getCookie(name); + if (this.options.desiredCapabilities.browserName) { + return this.browser.getCookie(name); + } else { + throw new Error(`grabCookie method can't be used in apps. it hasn't Cookie storage`); + } } /** @@ -995,34 +1064,46 @@ class WebDriverIO extends Helper { * Don't confuse popups with modal windows, as created by [various libraries](http://jster.net/category/windows-modals-popups). */ acceptPopup() { - return this.browser.alertText().then(function(res) { - if (res !== null) { - return this.alertAccept(); - } - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.alertText().then(function(res) { + if (res !== null) { + return this.alertAccept(); + } + }); + } else { + throw new Error(`acceptPopup method can't be used in apps. There is no JavaScript native popup in apps`); + } } /** * Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt. */ cancelPopup() { - return this.browser.alertText().then(function(res) { - if (res !== null) { - return this.alertDismiss(); - } - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.alertText().then(function(res) { + if (res !== null) { + return this.alertDismiss(); + } + }); + } else { + throw new Error(`cancelPopup method can't be used in apps. There is no JavaScript native popup in apps`); + } } /** * Checks that the active JavaScript popup, as created by `window.alert|window.confirm|window.prompt`, contains the given string. */ seeInPopup(text) { - return this.browser.alertText().then(function(res) { - if (res === null) { - throw new Error('Popup is not opened'); - } - stringIncludes('text in popup').assert(text, res); - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.alertText().then(function(res) { + if (res === null) { + throw new Error('Popup is not opened'); + } + stringIncludes('text in popup').assert(text, res); + }); + } else { + throw new Error(`seeInPopup method can't be used in apps. There is no JavaScript native popup in apps`); + } } /** @@ -1036,6 +1117,7 @@ class WebDriverIO extends Helper { * I.pressKey('Control'); * ``` */ + //TODO: works, but clear field before pressing pressKey(key) { let modifier; if (Array.isArray(key) && ~['Control', 'Command', 'Shift', 'Alt'].indexOf(key[0])) { @@ -1051,13 +1133,17 @@ class WebDriverIO extends Helper { * {{> ../webapi/resizeWindow }} */ resizeWindow(width, height) { - if (width === 'maximize') { - return this.browser.windowHandleMaximize(false); + if (this.options.desiredCapabilities.browserName) { + if (width === 'maximize') { + return this.browser.windowHandleMaximize(false); + } + return this.browser.windowHandleSize({ + width, + height + }); + } else { + throw new Error(`resizeWindow method can't be used in apps. You can't resize app on mobile`); } - return this.browser.windowHandleSize({ - width, - height - }); } /** @@ -1067,6 +1153,7 @@ class WebDriverIO extends Helper { * I.dragAndDrop('#dragHandle', '#container'); * ``` */ + //TODO: not tested dragAndDrop(srcElement, destElement) { return this.browser.dragAndDrop( withStrictLocator(srcElement), @@ -1104,7 +1191,7 @@ class WebDriverIO extends Helper { sec = sec || this.options.waitForTimeout; context = context || 'body'; return this.browser.waitUntil(function() { - return this.getText(context).then(function(source) { + return this.getText(withStrictLocator(context)).then(function(source) { if (Array.isArray(source)) { return source.filter(part => part.indexOf(text) >= 0).length > 0; } @@ -1113,7 +1200,7 @@ class WebDriverIO extends Helper { }, sec * 1000) .catch((e) => { if (e.type === 'WaitUntilTimeoutError') { - return proceedSee.call(this, 'assert', text, context); + return proceedSee.call(this, 'assert', text, withStrictLocator(context)); } else { throw e; } From b625458155a1796e9174079b5e178105d62f3777 Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 19:47:36 +0300 Subject: [PATCH 043/111] fix restart:false --- lib/helper/WebDriverIO.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 8994b8505..379c8bda7 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -348,9 +348,11 @@ class WebDriverIO extends Helper { _after() { if (this.options.restart) return this.browser.end(); this.debugSection('Session', 'cleaning cookies and localStorage'); - return this.browser.execute('localStorage.clear();').then(() => { - return this.browser.deleteCookie(); - }); + if (this.options.desiredCapabilities.browserName) { + return this.browser.execute('localStorage.clear();').then(() => { + return this.browser.deleteCookie(); + }); + } } _afterSuite() { From 45570042f8eff4f59eee3e6f5a24e679d367480f Mon Sep 17 00:00:00 2001 From: Pshenkin Andrey Date: Tue, 29 Nov 2016 20:40:16 +0300 Subject: [PATCH 044/111] update docs --- docs/helpers/WebDriverIO.md | 205 ++++++++++++---- lib/helper/WebDriverIO.js | 470 +++++++++++++++++++++--------------- 2 files changed, 434 insertions(+), 241 deletions(-) diff --git a/docs/helpers/WebDriverIO.md b/docs/helpers/WebDriverIO.md index 9924ee851..804688df6 100644 --- a/docs/helpers/WebDriverIO.md +++ b/docs/helpers/WebDriverIO.md @@ -1,7 +1,7 @@ # WebDriverIO WebDriverIO helper which wraps [webdriverio](http://webdriver.io/) library to -manipulate browser using Selenium WebDriver or PhantomJS. +manipulate browser using Selenium WebDriver, PhantomJS or Appium. #### Selenium Installation @@ -16,9 +16,19 @@ It allows you to run Selenium tests on a server without a GUI installed. 1. Download [PhantomJS](http://phantomjs.org/download.html) 2. Run PhantomJS in WebDriver mode: `phantomjs --webdriver=4444` +#### Appium Installation + +Appium is an open source test automation framework for use with native, hybrid and mobile web apps that implements the WebDriver protocol. +It allows you to run Selenium tests on mobile devices and also test native, hybrid and mobile web apps. + +1. Download and install [Appium](http://appium.io/) +2. Launch the daemon: `appium` + ### Configuration -This helper should be configured in codecept.json +This helper should be configured in codecept.conf.js + +#### Desktop configuration - `url` - base url of website to be tested - `browser` - browser in which perform testing @@ -31,17 +41,17 @@ This helper should be configured in codecept.json Example: -```json +```js { - "helpers": { - "WebDriverIO" : { - "browser": "chrome", - "restart": false, - "windowSize": "maximize, - "timeouts": { - "script": 60000, - "page load": 10000, - "implicit" : 5000 + helpers: { + WebDriverIO : { + browser: "chrome", + restart: false, + windowSize: "maximize", + timeouts: { + script: 60000, + page load: 10000, + implicit : 5000 } } } @@ -57,19 +67,19 @@ need to update the `helpers.WebDriverIO.desiredCapabilities.proxy` key. ```js { - "helpers": { - "WebDriverIO": { - "desiredCapabilities": { - "proxy": { - "proxyType": "manual|pac", - "proxyAutoconfigUrl": "URL TO PAC FILE", - "httpProxy": "PROXY SERVER", - "sslProxy": "PROXY SERVER", - "ftpProxy": "PROXY SERVER", - "socksProxy": "PROXY SERVER", - "socksUsername": "USERNAME", - "socksPassword": "PASSWORD", - "noProxy": "BYPASS ADDRESSES" + helpers: { + WebDriverIO: { + desiredCapabilities: { + proxy: { + proxyType: "manual|pac", + proxyAutoconfigUrl: "URL TO PAC FILE", + httpProxy: "PROXY SERVER", + sslProxy: "PROXY SERVER", + ftpProxy: "PROXY SERVER", + socksProxy: "PROXY SERVER", + socksUsername: "USERNAME", + socksPassword: "PASSWORD", + noProxy: "BYPASS ADDRESSES" } } } @@ -81,15 +91,15 @@ For example, ```js { - "helpers": { - "WebDriverIO": { - "desiredCapabilities": { - "proxy": { - "proxyType": "manual", - "httpProxy": "http://corporate.proxy:8080", - "socksUsername": "codeceptjs", - "socksPassword": "secret", - "noProxy": "127.0.0.1,localhost" + helpers: { + WebDriverIO: { + desiredCapabilities: { + proxy: { + proxyType: "manual", + httpProxy: "http://corporate.proxy:8080", + socksUsername: "codeceptjs", + socksPassword: "secret", + noProxy: "127.0.0.1,localhost" } } } @@ -110,13 +120,13 @@ service provider to connect to. ```js { - "helpers":{ - "WebDriverIO": { - "url": "YOUR_DESIERED_HOST", - "user": "YOUR_BROWSERSTACK_USER", - "key": "YOUR_BROWSERSTACK_KEY", - "desiredCapabilities": { - "browserName": "chrome", + helpers:{ + WebDriverIO: { + url: "YOUR_DESIERED_HOST", + user: "YOUR_BROWSERSTACK_USER", + key: "YOUR_BROWSERSTACK_KEY", + desiredCapabilities: { + browserName: "chrome", // only set this if you're using BrowserStackLocal to test a local domain // "browserstack.local": true, @@ -138,17 +148,17 @@ Here is the [webdriverio docs](http://webdriver.io/guide/usage/multiremote.html) ```js { - "helpers": { - "WebDriverIO": { - "multiremote": { - "MyChrome": { - "desiredCapabilities": { - "browserName": "chrome" + helpers: { + WebDriverIO: { + multiremote: { + MyChrome: { + desiredCapabilities: { + browserName: "chrome" } }, - "MyFirefox": { - "desiredCapabilities": { - "browserName": "firefox" + MyFirefox: { + desiredCapabilities: { + browserName: "firefox" } } } @@ -157,6 +167,41 @@ Here is the [webdriverio docs](http://webdriver.io/guide/usage/multiremote.html) } ``` +#### Appium configuration + +- `port`: Appium serverport +- `restart`: restart browser or app between tests (default: true), if set to false cookies will be cleaned but browser window will be kept and for apps nothing will be changed. +- `desiredCapabilities`: Appium capabilities +-- `platformName` - Which mobile OS platform to use +-- `appPackage` - Java package of the Android app you want to run +-- `appActivity` - Activity name for the Android activity you want to launch from your package. +-- `deviceName`: The kind of mobile device or emulator to use +-- `platformVersion`: Mobile OS version +-- `app` - The absolute local path or remote http URL to an .ipa or .apk file, or a .zip containing one of these. Appium will attempt to install this app binary on the appropriate device first. +-- `browserName`: Name of mobile web browser to automate. Should be an empty string if automating an app instead. + +Example: + +```js +{ + helpers: { + WebDriverIO: { + desiredCapabilities: { + platformName: "Android", + appPackage: "com.example.android.myApp", + appActivity: "MainActivity", + deviceName: "OnePlus3", + platformVersion: "6.0.1" + }, + port: 4723, + restart: false + } + } +} +``` +Additional configuration params can be used from + + ## Access From Helpers Receive a WebDriverIO client from a custom helper by accessing `browser` property: @@ -222,11 +267,13 @@ this.helpers['WebDriverIO']._locateFields('Your email').then // ... Accepts the active JavaScript native popup window, as created by window.alert|window.confirm|window.prompt. Don't confuse popups with modal windows, as created by [various libraries](http://jster.net/category/windows-modals-popups). +Appium: support only web testing ## amOnPage Opens a web page in a browser. Requires relative or absolute url. If url starts with `/`, opens a web page of a site defined in `url` config parameter. +Appium: support only web testing ```js I.amOnPage('/'); // opens main page of website @@ -242,6 +289,7 @@ I.amOnPage('/login'); // opens a login page Appends text to a input field or textarea. Field is located by name, label, CSS or XPath +Appium: support, but it's clear a field before insert in apps ```js I.appendField('#myTextField', 'appended'); @@ -257,6 +305,7 @@ I.appendField('#myTextField', 'appended'); Attaches a file to element located by label, name, CSS or XPath Path to file is relative current codecept directory (where codecept.json is located). File will be uploaded to remote system (if tests are running remotely). +Appium: not tested ```js I.attachFile('Avatar', 'data/avatar.jpg'); @@ -276,6 +325,7 @@ Dismisses the active JavaScript popup, as created by window.alert|window.confirm Selects a checkbox or radio button. Element is located by label or name or CSS or XPath. +Appium: support only web testing The second parameter is a context (CSS or XPath locator) to narrow the search. @@ -294,6 +344,7 @@ I.checkOption('agree', '//form'); Clears a cookie by name, if none provided clears all cookies +Appium: support only web testing ```js I.clearCookie(); @@ -307,6 +358,7 @@ I.clearCookie('test'); ## clearField Clears a `