From f00f8595ab7ef9e2d3b2fbe936b1d233d863736a Mon Sep 17 00:00:00 2001 From: Craig de Gouveia Date: Mon, 15 May 2017 13:23:40 +0100 Subject: [PATCH 01/16] Expanded saveScreenshot fucntion for nightmare and updated docs --- docs/webapi/saveScreenshot.mustache | 7 +++-- lib/helper/Nightmare.js | 42 ++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/docs/webapi/saveScreenshot.mustache b/docs/webapi/saveScreenshot.mustache index 3014ee9df..7bdf16329 100644 --- a/docs/webapi/saveScreenshot.mustache +++ b/docs/webapi/saveScreenshot.mustache @@ -1,7 +1,10 @@ Saves a screenshot to ouput folder (set in codecept.json). -Filename is relative to output folder. +Filename is relative to output folder. +Optionally resize the window to the full availible page height and width to capture the entire page by passing `true` in as the second argument. ```js I.saveScreenshot('debug.png'); +I.saveScreenshot('debug.png',ture) \\resizes to availible scrollHeight and scrollWidth before taking screenshot ``` -@param fileName \ No newline at end of file +@param fileName +@param fullPage (optional) \ No newline at end of file diff --git a/lib/helper/Nightmare.js b/lib/helper/Nightmare.js index dd3c5bb05..b8250097c 100644 --- a/lib/helper/Nightmare.js +++ b/lib/helper/Nightmare.js @@ -797,20 +797,36 @@ class Nightmare extends Helper { }, lctype(locator), lcval(locator)); } - /** - * {{> ../webapi/saveScreenshot }} - */ - saveScreenshot(fileName) { - let outputFile = path.join(global.output_dir, fileName); - this.debug('Screenshot is saving to ' + outputFile); - let recorder = require('../recorder'); - return this.browser.screenshot(outputFile); - } + /** + * {{> ../webapi/saveScreenshot }} + */ + saveScreenshot(fileName, fullPage) { + fullPage = fullPage || false + let outputFile = path.join(global.output_dir, fileName); + this.debug('Screenshot is saving to ' + outputFile); + let recorder = require('../recorder'); + + if (full === true) { + this.browser.evaluate(function () { + o = { + height: document.body.scrollHeight, + width: document.body.scrollWidth + } + return o + }).then(doc => { + console.log(doc.width, doc.height) + this.browser.viewport(doc.width, doc.width); + return this.browser.screenshot(outputFile) + }) + } - _failed(test) { - let fileName = test.title.replace(/\W/g, '_') + '.failed.png'; - return this.saveScreenshot(fileName); - } + return this.browser.screenshot(outputFile); + } + + _failed(test) { + let fileName = test.title.replace(/\W/g, '_') + '.failed.png'; + return this.saveScreenshot(fileName, true); + } /** * Scrolls to element matched by locator. From fb3aa01b7df5d860681b9fb50f3d5153b37a68b3 Mon Sep 17 00:00:00 2001 From: Craig de Gouveia Date: Tue, 16 May 2017 12:21:05 +0100 Subject: [PATCH 02/16] Webdriver and WDIO work to bring inline with Nightmare. --- lib/helper/Nightmare.js | 35 +++++++++++++++------------------ lib/helper/SeleniumWebdriver.js | 29 ++++++++++++++++++++++----- lib/helper/WebDriverIO.js | 23 ++++++++++++++++++---- 3 files changed, 59 insertions(+), 28 deletions(-) diff --git a/lib/helper/Nightmare.js b/lib/helper/Nightmare.js index b8250097c..80f56470d 100644 --- a/lib/helper/Nightmare.js +++ b/lib/helper/Nightmare.js @@ -800,27 +800,24 @@ class Nightmare extends Helper { /** * {{> ../webapi/saveScreenshot }} */ - saveScreenshot(fileName, fullPage) { - fullPage = fullPage || false - let outputFile = path.join(global.output_dir, fileName); - this.debug('Screenshot is saving to ' + outputFile); - let recorder = require('../recorder'); - - if (full === true) { - this.browser.evaluate(function () { - o = { - height: document.body.scrollHeight, - width: document.body.scrollWidth - } - return o - }).then(doc => { - console.log(doc.width, doc.height) - this.browser.viewport(doc.width, doc.width); - return this.browser.screenshot(outputFile) - }) - } + saveScreenshot(fileName, fullPage = false) { + let outputFile = path.join(global.output_dir, fileName); + this.debug('Screenshot is saving to ' + outputFile); + let recorder = require('../recorder'); + if (!fullPage) { return this.browser.screenshot(outputFile); + } + return this.browser.evaluate(() => ({ + height: document.body.scrollHeight, + width: document.body.scrollWidth + })).then(({ + width, + height + }) => { + this.browser.viewport(width, height); + return this.browser.screenshot(outputFile) + }) } _failed(test) { diff --git a/lib/helper/SeleniumWebdriver.js b/lib/helper/SeleniumWebdriver.js index 6c799702b..ac8889e9e 100644 --- a/lib/helper/SeleniumWebdriver.js +++ b/lib/helper/SeleniumWebdriver.js @@ -159,7 +159,7 @@ class SeleniumWebdriver extends Helper { _failed(test) { let fileName = test.title.replace(/ /g, '_') + '.failed.png'; - return this.saveScreenshot(fileName); + return this.saveScreenshot(fileName,true); } _withinBegin(locator) { @@ -571,20 +571,39 @@ class SeleniumWebdriver extends Helper { /** * {{> ../webapi/saveScreenshot }} */ - saveScreenshot(fileName) { + saveScreenshot(fileName, fullPage = false) { let outputFile = path.join(global.output_dir, fileName); this.debug('Screenshot has been saved to ' + outputFile); - return this.browser.takeScreenshot().then(function (png) { + + const writeFile = (png,outputFile) => { let fs = require('fs'); - var stream = fs.createWriteStream(outputFile); + let stream = fs.createWriteStream(outputFile); stream.write(new Buffer(png, 'base64')); stream.end(); return new Promise(function (resolve) { return stream.on('finish', resolve); }); - }); + } + + if (!fullPage) { + return this.browser.takeScreenshot() + .then(png => writeFile(png,outputFile)); + } + return this.browser.executeScript(() => ({ + height: document.body.scrollHeight, + width: document.body.scrollWidth + })).then(({ + width, + height + }) => { + this.browser.manage().window().setSize(width, height) + return this.browser.takeScreenshot() + .then(png => writeFile(png,outputFile)); + }) } + + /** * {{> ../webapi/setCookie}} * diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 0a7b5cd40..e1f56601b 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -304,7 +304,7 @@ class WebDriverIO extends Helper { } else { fileName = test.title.replace(/ /g, '_') + '.failed.png'; } - return this.saveScreenshot(fileName); + return this.saveScreenshot(fileName,true); } _withinBegin(locator) { @@ -859,12 +859,27 @@ class WebDriverIO extends Helper { /** * {{> ../webapi/saveScreenshot}} */ - saveScreenshot(fileName) { + saveScreenshot(fileName,fullPage = false) { let outputFile = path.join(global.output_dir, fileName); - this.debug('Screenshot has been saved to ' + outputFile); - return this.browser.saveScreenshot(outputFile); + + if (!fullPage) { + this.debug('Screenshot has been saved to ' + outputFile); + return this.browser.saveScreenshot(outputFile); + } + return this.browser.execute(() => ({ + height: document.body.scrollHeight, + width: document.body.scrollWidth + })).then(({ + width, + height + }) => { + this.browser.windowHandleSize(width, height); + this.debug('Screenshot has been saved to ' + outputFile); + return this.browser.saveScreenshot(outputFile) + }) } + /** * {{> ../webapi/setCookie}} * From a207d659bd3332485fdf21b8bd8a6dd25591f565 Mon Sep 17 00:00:00 2001 From: Craig de Gouveia Date: Tue, 16 May 2017 12:56:47 +0100 Subject: [PATCH 03/16] Updated docs and added tests --- docs/webapi/saveScreenshot.mustache | 2 +- test/helper/Protractor_test.js | 6 ++++++ test/helper/webapi.js | 7 +++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/webapi/saveScreenshot.mustache b/docs/webapi/saveScreenshot.mustache index 7bdf16329..902420adf 100644 --- a/docs/webapi/saveScreenshot.mustache +++ b/docs/webapi/saveScreenshot.mustache @@ -1,6 +1,6 @@ Saves a screenshot to ouput folder (set in codecept.json). Filename is relative to output folder. -Optionally resize the window to the full availible page height and width to capture the entire page by passing `true` in as the second argument. +Optionally resize the window to the full availible page `scrollHeight` and `scrollWidth` to capture the entire page by passing `true` in as the second argument. ```js I.saveScreenshot('debug.png'); diff --git a/test/helper/Protractor_test.js b/test/helper/Protractor_test.js index 4cec20691..ab39964f5 100644 --- a/test/helper/Protractor_test.js +++ b/test/helper/Protractor_test.js @@ -372,6 +372,12 @@ describe('Protractor', function() { return assert.ok(fileExists(path.join(output_dir, 'protractor_user.png')), null, 'file does not exists'); }); + it('should create full page a screenshot file in output dir', function*() { + yield I.amOnPage('/'); + yield I.saveScreenshot('protractor_user_full.png',true); + return assert.ok(fileExists(path.join(output_dir, 'protractor_user_full.png')), null, 'file does not exists'); + }); + it('should create a screenshot on fail', function*() { let test = { title: 'protractor should do smth' }; yield I.amOnPage('/') diff --git a/test/helper/webapi.js b/test/helper/webapi.js index 781c51256..79f67e93a 100644 --- a/test/helper/webapi.js +++ b/test/helper/webapi.js @@ -472,6 +472,13 @@ module.exports.tests = function() { .then(() => assert.ok(fileExists(path.join(output_dir, 'screenshot_'+sec)), null, 'file does not exists')); }); + it('should create a full page screenshot file in output dir', () => { + let sec = (new Date()).getUTCMilliseconds(); + return I.amOnPage('/') + .then(() => I.saveScreenshot(`screenshot_full_${+sec}`,true)) + .then(() => assert.ok(fileExists(path.join(output_dir, `screenshot_full_${+sec}`)), null, 'file does not exists')); + }); + it('should create a screenshot on fail', () => { let sec = (new Date()).getUTCMilliseconds().toString(); let test = { title: 'sw should do smth '+sec }; From 793eeefae0a3e38f29f8e67bdc9b6b01c5e3cd05 Mon Sep 17 00:00:00 2001 From: Craig de Gouveia Date: Tue, 16 May 2017 13:22:00 +0100 Subject: [PATCH 04/16] typos and spelling mistakes in docs --- docs/webapi/saveScreenshot.mustache | 4 +-- lib/helper/Nightmare.js | 38 ++++++++++++++--------------- lib/helper/SeleniumWebdriver.js | 15 ++++++------ lib/helper/WebDriverIO.js | 8 +++--- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/docs/webapi/saveScreenshot.mustache b/docs/webapi/saveScreenshot.mustache index 902420adf..cc9305659 100644 --- a/docs/webapi/saveScreenshot.mustache +++ b/docs/webapi/saveScreenshot.mustache @@ -1,10 +1,10 @@ Saves a screenshot to ouput folder (set in codecept.json). Filename is relative to output folder. -Optionally resize the window to the full availible page `scrollHeight` and `scrollWidth` to capture the entire page by passing `true` in as the second argument. +Optionally resize the window to the full available page `scrollHeight` and `scrollWidth` to capture the entire page by passing `true` in as the second argument. ```js I.saveScreenshot('debug.png'); -I.saveScreenshot('debug.png',ture) \\resizes to availible scrollHeight and scrollWidth before taking screenshot +I.saveScreenshot('debug.png',true) \\resizes to available scrollHeight and scrollWidth before taking screenshot ``` @param fileName @param fullPage (optional) \ No newline at end of file diff --git a/lib/helper/Nightmare.js b/lib/helper/Nightmare.js index 80f56470d..01db4085d 100644 --- a/lib/helper/Nightmare.js +++ b/lib/helper/Nightmare.js @@ -800,30 +800,30 @@ class Nightmare extends Helper { /** * {{> ../webapi/saveScreenshot }} */ - saveScreenshot(fileName, fullPage = false) { - let outputFile = path.join(global.output_dir, fileName); - this.debug('Screenshot is saving to ' + outputFile); - let recorder = require('../recorder'); + saveScreenshot(fileName, fullPage = false) { + let outputFile = path.join(global.output_dir, fileName); + this.debug('Screenshot is saving to ' + outputFile); + let recorder = require('../recorder'); - if (!fullPage) { - return this.browser.screenshot(outputFile); - } - return this.browser.evaluate(() => ({ - height: document.body.scrollHeight, - width: document.body.scrollWidth - })).then(({ + if (!fullPage) { + return this.browser.screenshot(outputFile); + } + return this.browser.evaluate(() => ({ + height: document.body.scrollHeight, + width: document.body.scrollWidth + })).then(({ width, height }) => { - this.browser.viewport(width, height); - return this.browser.screenshot(outputFile) - }) - } + this.browser.viewport(width, height); + return this.browser.screenshot(outputFile); + }); + } - _failed(test) { - let fileName = test.title.replace(/\W/g, '_') + '.failed.png'; - return this.saveScreenshot(fileName, true); - } + _failed(test) { + let fileName = test.title.replace(/\W/g, '_') + '.failed.png'; + return this.saveScreenshot(fileName, true); + } /** * Scrolls to element matched by locator. diff --git a/lib/helper/SeleniumWebdriver.js b/lib/helper/SeleniumWebdriver.js index ac8889e9e..e2218fb6a 100644 --- a/lib/helper/SeleniumWebdriver.js +++ b/lib/helper/SeleniumWebdriver.js @@ -159,7 +159,7 @@ class SeleniumWebdriver extends Helper { _failed(test) { let fileName = test.title.replace(/ /g, '_') + '.failed.png'; - return this.saveScreenshot(fileName,true); + return this.saveScreenshot(fileName, true); } _withinBegin(locator) { @@ -575,7 +575,7 @@ class SeleniumWebdriver extends Helper { let outputFile = path.join(global.output_dir, fileName); this.debug('Screenshot has been saved to ' + outputFile); - const writeFile = (png,outputFile) => { + const writeFile = (png, outputFile) => { let fs = require('fs'); let stream = fs.createWriteStream(outputFile); stream.write(new Buffer(png, 'base64')); @@ -583,11 +583,11 @@ class SeleniumWebdriver extends Helper { return new Promise(function (resolve) { return stream.on('finish', resolve); }); - } + }; if (!fullPage) { return this.browser.takeScreenshot() - .then(png => writeFile(png,outputFile)); + .then(png => writeFile(png, outputFile)); } return this.browser.executeScript(() => ({ height: document.body.scrollHeight, @@ -596,14 +596,13 @@ class SeleniumWebdriver extends Helper { width, height }) => { - this.browser.manage().window().setSize(width, height) + this.browser.manage().window().setSize(width, height); return this.browser.takeScreenshot() - .then(png => writeFile(png,outputFile)); - }) + .then(png => writeFile(png, outputFile)); + }); } - /** * {{> ../webapi/setCookie}} * diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index e1f56601b..070d8b2af 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -304,7 +304,7 @@ class WebDriverIO extends Helper { } else { fileName = test.title.replace(/ /g, '_') + '.failed.png'; } - return this.saveScreenshot(fileName,true); + return this.saveScreenshot(fileName, true); } _withinBegin(locator) { @@ -859,7 +859,7 @@ class WebDriverIO extends Helper { /** * {{> ../webapi/saveScreenshot}} */ - saveScreenshot(fileName,fullPage = false) { + saveScreenshot(fileName, fullPage = false) { let outputFile = path.join(global.output_dir, fileName); if (!fullPage) { @@ -875,8 +875,8 @@ class WebDriverIO extends Helper { }) => { this.browser.windowHandleSize(width, height); this.debug('Screenshot has been saved to ' + outputFile); - return this.browser.saveScreenshot(outputFile) - }) + return this.browser.saveScreenshot(outputFile); + }); } From 551d79ea59da415a2b0659b6badc6aa3bed41ec7 Mon Sep 17 00:00:00 2001 From: Craig de Gouveia Date: Tue, 16 May 2017 14:52:58 +0100 Subject: [PATCH 05/16] Moved test for resizeWindow This was causing the SeleniumWebdriver tests to fail as the window size isn't reset after prior tests. --- test/helper/webapi.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/test/helper/webapi.js b/test/helper/webapi.js index 79f67e93a..c24fb721a 100644 --- a/test/helper/webapi.js +++ b/test/helper/webapi.js @@ -460,6 +460,23 @@ module.exports.tests = function() { }); }); + describe('window size #resizeWindow', () => { + it('should set initial window size', () => { + return I.amOnPage('/form/resize') + .then(() => I.click('Window Size')) + .then(() => I.see('Height 400', '#height')) + .then(() => I.see('Width 500', '#width')) + }); + + it('should resize window to specific dimensions', () => { + return I.amOnPage('/form/resize') + .then(() => I.resizeWindow(800, 600)) + .then(() => I.click('Window Size')) + .then(() => I.see('Height 600', '#height')) + .then(() => I.see('Width 800', '#width')) + }); + }); + describe('#saveScreenshot', () => { beforeEach(() => { global.output_dir = path.join(global.codecept_dir, 'output'); @@ -531,23 +548,6 @@ module.exports.tests = function() { }); }); - describe('window size #resizeWindow', () => { - it('should set initial window size', () => { - return I.amOnPage('/form/resize') - .then(() => I.click('Window Size')) - .then(() => I.see('Height 400', '#height')) - .then(() => I.see('Width 500', '#width')) - }); - - it('should resize window to specific dimensions', () => { - return I.amOnPage('/form/resize') - .then(() => I.resizeWindow(800, 600)) - .then(() => I.click('Window Size')) - .then(() => I.see('Height 600', '#height')) - .then(() => I.see('Width 800', '#width')) - }); - }); - describe('#waitForElement', () => { it('should wait for visibile element', () => { return I.amOnPage('/form/wait_visible') From defb80b0dd7628c4b4bf724759f85c9c003c8da8 Mon Sep 17 00:00:00 2001 From: Craig de Gouveia Date: Wed, 7 Jun 2017 17:04:13 +0100 Subject: [PATCH 06/16] Resolves #540. Added config option to helpers for saveScreenshot() --- lib/helper/Nightmare.js | 3 ++- lib/helper/Protractor.js | 1 + lib/helper/SeleniumWebdriver.js | 1 + lib/helper/WebDriverIO.js | 48 +++++++++++++++++---------------- 4 files changed, 29 insertions(+), 24 deletions(-) diff --git a/lib/helper/Nightmare.js b/lib/helper/Nightmare.js index 01db4085d..27cadd354 100644 --- a/lib/helper/Nightmare.js +++ b/lib/helper/Nightmare.js @@ -48,6 +48,7 @@ class Nightmare extends Helper { this.options = { waitForAction: 500, waitForTimeout: 1000, + fullPageScreenshots: true, rootElement: 'body', restart: true, keepCookies: false, @@ -800,7 +801,7 @@ class Nightmare extends Helper { /** * {{> ../webapi/saveScreenshot }} */ - saveScreenshot(fileName, fullPage = false) { + saveScreenshot(fileName, fullPage = this.options.fullPageScreenshots) { let outputFile = path.join(global.output_dir, fileName); this.debug('Screenshot is saving to ' + outputFile); let recorder = require('../recorder'); diff --git a/lib/helper/Protractor.js b/lib/helper/Protractor.js index 02ec07d98..de1532568 100644 --- a/lib/helper/Protractor.js +++ b/lib/helper/Protractor.js @@ -68,6 +68,7 @@ class Protractor extends SeleniumWebdriver { browser: 'chrome', url: 'http://localhost', seleniumAddress: 'http://localhost:4444/wd/hub', + fullPageScreenshots: true, rootElement: 'body', scriptsTimeout: 10000, waitForTimeout: 1000, // ms diff --git a/lib/helper/SeleniumWebdriver.js b/lib/helper/SeleniumWebdriver.js index 5813a6725..cdacf5d07 100644 --- a/lib/helper/SeleniumWebdriver.js +++ b/lib/helper/SeleniumWebdriver.js @@ -72,6 +72,7 @@ class SeleniumWebdriver extends Helper { restart: true, keepCookies: false, windowSize: null, + fullPageScreenshots: true, waitForTimeout: 1000, // ms scriptTimeout: 1000, // ms manualStart: false, diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 9aa0d92a1..26ef1f815 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -921,13 +921,15 @@ class WebDriverIO extends Helper { this.debug('Screenshot has been saved to ' + outputFile); return this.browser.saveScreenshot(outputFile); } - return this.browser.execute(() => ({ - height: document.body.scrollHeight, - width: document.body.scrollWidth - })).then(({ - width, - height - }) => { + return this.browser.execute(function () { + return ({ + height: document.body.scrollHeight, + width: document.body.scrollWidth + }) + }).then(({ + width, + height + }) => { this.browser.windowHandleSize(width, height); this.debug('Screenshot has been saved to ' + outputFile); return this.browser.saveScreenshot(outputFile); @@ -1120,13 +1122,13 @@ class WebDriverIO extends Helper { sec = sec || this.options.waitForTimeout; context = context || 'body'; return this.browser.waitUntil(function () { - return this.getText(withStrictLocator(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) + return this.getText(withStrictLocator(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, withStrictLocator(context)); @@ -1361,15 +1363,15 @@ function withStrictLocator(locator) { locator.toString = () => `{${key}: '${value}'}`; switch (key) { - case 'by': - case 'xpath': - return value; - case 'css': - return value; - case 'id': - return '#' + value; - case 'name': - return `[name="${value}"]`; + case 'by': + case 'xpath': + return value; + case 'css': + return value; + case 'id': + return '#' + value; + case 'name': + return `[name="${value}"]`; } } From 5b9a20f180f9caa08047f795959233338277d9fd Mon Sep 17 00:00:00 2001 From: Craig de Gouveia Date: Thu, 8 Jun 2017 16:38:19 +0100 Subject: [PATCH 07/16] initial work on database helper --- lib/helper/Loki.js | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 lib/helper/Loki.js diff --git a/lib/helper/Loki.js b/lib/helper/Loki.js new file mode 100644 index 000000000..f60756d2f --- /dev/null +++ b/lib/helper/Loki.js @@ -0,0 +1,52 @@ +const requireg = require('requireg'); +const Helper = require('../helper'); + +const fileExists = require('../utils').fileExists; +const fileIncludes = require('../assert/include').fileIncludes; +const fileEquals = require('../assert/equal').fileEquals; + +const assert = require('assert'); +const path = require('path'); +const fs = require('fs'); + +/** + * Helper with in memory databse for. + * Can be easily used to : + * + * ```js + * I.amInPath('test'); + * I.seeFile('codecept.json'); + * I.seeInThisFile('FileSystem'); + * I.dontSeeInThisFile("WebDriverIO"); + * ``` + */ + +class Loki extends Helper { + + constructor(config) { + super(config); + loki = requireg('lokijs'); + + // set defaults + this.options = { + database: "./db.json", + }; + + // override defaults with config + Object.assign(this.options, config); + } + + _init() { + const db = new loki("./database/db.json", { + autosave: true, + autosaveInterval: 10000, + }); + } + + _before() {} + + +} + + +module.exports = Loki; From ffb3f6240b9ebf77ba9740e52506667cf16bbf55 Mon Sep 17 00:00:00 2001 From: Craig de Gouveia Date: Tue, 13 Jun 2017 17:00:13 +0100 Subject: [PATCH 08/16] Initial work on helper including tab;e creation and initial config --- lib/command/init.js | 2 +- lib/helper/Loki.js | 56 +++++++++++++++++++++++++++++++++++++-------- package.json | 1 + 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/lib/command/init.js b/lib/command/init.js index b64f194c8..8a64b352e 100644 --- a/lib/command/init.js +++ b/lib/command/init.js @@ -21,7 +21,7 @@ let defaultConfig = { mocha: {} }; -let helpers = ['WebDriverIO', 'Protractor', 'SeleniumWebdriver', 'Nightmare', 'FileSystem']; +let helpers = ['WebDriverIO', 'Protractor', 'SeleniumWebdriver', 'Nightmare', 'FileSystem', 'Loki']; let translations = Object.keys(require('../../translations')); let noTranslation = 'English (no localization)'; translations.unshift(noTranslation); diff --git a/lib/helper/Loki.js b/lib/helper/Loki.js index f60756d2f..e9e05c2f4 100644 --- a/lib/helper/Loki.js +++ b/lib/helper/Loki.js @@ -8,9 +8,11 @@ const fileEquals = require('../assert/equal').fileEquals; const assert = require('assert'); const path = require('path'); const fs = require('fs'); +const loki = require('lokijs') /** - * Helper with in memory databse for. + * TODO: Complete this with relevant info + * Helper with in memory databse for * Can be easily used to : * * ```js @@ -25,27 +27,63 @@ class Loki extends Helper { constructor(config) { super(config); - loki = requireg('lokijs'); + // loki = requireg('lokijs'); // set defaults this.options = { - database: "./db.json", + dbName: "./db.json", + dbOpts: { + autosave: true, + autosaveInterval: 10000, + }, + dbSeed: [] }; // override defaults with config Object.assign(this.options, config); } - _init() { - const db = new loki("./database/db.json", { - autosave: true, - autosaveInterval: 10000, - }); + // _init() { + // this.db = new loki(this.options.dbName, { + // autosave: true, + // autosaveInterval: 10000, + // }); + // } + + // _before() {} + + // addCollection(data) {} + + _beforeSuite() { + this._startDb(); + } + + _before() { + // this.dir = global.codecept_dir; + // this.file = null; + // this.debugSection('Dir', this.dir); + // console.log(this) + } + + _afterSuite() { + this._stopDb() + } + + _startDb() { + this.db = new loki(this.options.dbName, this.options.dbOpts); } - _before() {} + _stopDb() { + this.db.close(); + } + addCollection(data) { + !this.findCollection(data) && this.db.addCollection(data) + } + findCollection(data) { + return this.db.getCollection(data) + } } diff --git a/package.json b/package.json index a258c4949..c1e07f9db 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "gulp-mustache": "^2.2.0", "gulp-plumber": "^1.0.0", "guppy-pre-commit": "^0.4.0", + "lokijs": "^1.5.0", "nightmare": "^2.5.2", "nightmare-upload": "^0.1.1", "protractor": ">4.0.9 <6.0", From 80cd0bd818f273a2f71dd587c2a014c53d8db6f3 Mon Sep 17 00:00:00 2001 From: Craig de Gouveia Date: Tue, 13 Jun 2017 17:03:56 +0100 Subject: [PATCH 09/16] Initial work on helper including table creation and initial config --- lib/helper/Loki.js | 8 ++++---- lib/helper/WebDriverIO.js | 36 ++++++++++++++++++------------------ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/helper/Loki.js b/lib/helper/Loki.js index e9e05c2f4..b0e8d7953 100644 --- a/lib/helper/Loki.js +++ b/lib/helper/Loki.js @@ -8,7 +8,7 @@ const fileEquals = require('../assert/equal').fileEquals; const assert = require('assert'); const path = require('path'); const fs = require('fs'); -const loki = require('lokijs') +const loki = require('lokijs'); /** * TODO: Complete this with relevant info @@ -66,7 +66,7 @@ class Loki extends Helper { } _afterSuite() { - this._stopDb() + this._stopDb(); } _startDb() { @@ -78,11 +78,11 @@ class Loki extends Helper { } addCollection(data) { - !this.findCollection(data) && this.db.addCollection(data) + !this.findCollection(data) && this.db.addCollection(data); } findCollection(data) { - return this.db.getCollection(data) + return this.db.getCollection(data); } } diff --git a/lib/helper/WebDriverIO.js b/lib/helper/WebDriverIO.js index 26ef1f815..b9e8755ab 100644 --- a/lib/helper/WebDriverIO.js +++ b/lib/helper/WebDriverIO.js @@ -922,10 +922,10 @@ class WebDriverIO extends Helper { return this.browser.saveScreenshot(outputFile); } return this.browser.execute(function () { - return ({ + return { height: document.body.scrollHeight, width: document.body.scrollWidth - }) + }; }).then(({ width, height @@ -1122,13 +1122,13 @@ class WebDriverIO extends Helper { sec = sec || this.options.waitForTimeout; context = context || 'body'; return this.browser.waitUntil(function () { - return this.getText(withStrictLocator(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) + return this.getText(withStrictLocator(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, withStrictLocator(context)); @@ -1363,15 +1363,15 @@ function withStrictLocator(locator) { locator.toString = () => `{${key}: '${value}'}`; switch (key) { - case 'by': - case 'xpath': - return value; - case 'css': - return value; - case 'id': - return '#' + value; - case 'name': - return `[name="${value}"]`; + case 'by': + case 'xpath': + return value; + case 'css': + return value; + case 'id': + return '#' + value; + case 'name': + return `[name="${value}"]`; } } From 93644dc8353ae8285ef760626262a9c6d2f32685 Mon Sep 17 00:00:00 2001 From: Craig de Gouveia Date: Wed, 14 Jun 2017 17:37:48 +0100 Subject: [PATCH 10/16] More db helper functions --- lib/helper/Loki.js | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/lib/helper/Loki.js b/lib/helper/Loki.js index b0e8d7953..f783c709f 100644 --- a/lib/helper/Loki.js +++ b/lib/helper/Loki.js @@ -1,4 +1,3 @@ -const requireg = require('requireg'); const Helper = require('../helper'); const fileExists = require('../utils').fileExists; @@ -28,7 +27,6 @@ class Loki extends Helper { constructor(config) { super(config); // loki = requireg('lokijs'); - // set defaults this.options = { dbName: "./db.json", @@ -43,27 +41,14 @@ class Loki extends Helper { Object.assign(this.options, config); } - // _init() { - // this.db = new loki(this.options.dbName, { - // autosave: true, - // autosaveInterval: 10000, - // }); - // } - - // _before() {} - - // addCollection(data) {} - - _beforeSuite() { + _init() { this._startDb(); + this.importSeed(this.options.dbSeed); } - _before() { - // this.dir = global.codecept_dir; - // this.file = null; - // this.debugSection('Dir', this.dir); - // console.log(this) - } + _beforeSuite() {} + + _before() {} _afterSuite() { this._stopDb(); @@ -78,13 +63,29 @@ class Loki extends Helper { } addCollection(data) { - !this.findCollection(data) && this.db.addCollection(data); + return !this.findCollection(data) && this.db.addCollection(data); } findCollection(data) { return this.db.getCollection(data); } -} + removeCollection(data) { + return this.db.removeCollection(data); + } + + insert(collection, data) { + const c = this.findCollection(collection); + return c.insert(data); + } + + importSeed(array = []) { + array.map(i => { + const name = i.substr(0, i.lastIndexOf('.')) || i; + const collection = this.db.addCollection(name); + return collection.insert(require(i)); + }); + } +} module.exports = Loki; From 3deb4023fe0b1fad72a6bb5893a4d1c079b25b24 Mon Sep 17 00:00:00 2001 From: Craig de Gouveia Date: Thu, 15 Jun 2017 14:58:12 +0100 Subject: [PATCH 11/16] Database seeding mechanism for simple object creation --- lib/helper/Loki.js | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/lib/helper/Loki.js b/lib/helper/Loki.js index f783c709f..85f17c7bc 100644 --- a/lib/helper/Loki.js +++ b/lib/helper/Loki.js @@ -1,9 +1,9 @@ const Helper = require('../helper'); - const fileExists = require('../utils').fileExists; const fileIncludes = require('../assert/include').fileIncludes; const fileEquals = require('../assert/equal').fileEquals; +const requireg = require('requireg'); const assert = require('assert'); const path = require('path'); const fs = require('fs'); @@ -29,21 +29,39 @@ class Loki extends Helper { // loki = requireg('lokijs'); // set defaults this.options = { - dbName: "./db.json", + dbName: "db.json", dbOpts: { autosave: true, autosaveInterval: 10000, }, - dbSeed: [] + dbSeedDir: "seed/" }; // override defaults with config Object.assign(this.options, config); } + static _checkRequirements() { + try { + requireg("lokijs"); + } catch (e) { + return ["lokijs"]; + } + } + + static _config() { + return [{ + name: 'dbName', + message: "Database filename", + default: 'db.json' + }]; + } + _init() { + this.dir = global.codecept_dir; + this.debugSection('Dir', this.dir); this._startDb(); - this.importSeed(this.options.dbSeed); + this._loadSeedData(); } _beforeSuite() {} @@ -62,6 +80,10 @@ class Loki extends Helper { this.db.close(); } + _loadSeedData() { + this.importData(this.options.dbSeedDir); + } + addCollection(data) { return !this.findCollection(data) && this.db.addCollection(data); } @@ -79,11 +101,13 @@ class Loki extends Helper { return c.insert(data); } - importSeed(array = []) { - array.map(i => { - const name = i.substr(0, i.lastIndexOf('.')) || i; - const collection = this.db.addCollection(name); - return collection.insert(require(i)); + importData(data) { + const files = fs.readdirSync(data); + files.map(i => { + const fileName = i.substr(0, i.lastIndexOf('.')) || i; + const filePath = path.join(this.dir, data, i); + const collection = this.db.addCollection(fileName); + return collection.insert(require(filePath)); }); } } From 372582e11f778a0ee3c999e9d24b4ec605409486 Mon Sep 17 00:00:00 2001 From: Craig de Gouveia Date: Thu, 15 Jun 2017 17:03:57 +0100 Subject: [PATCH 12/16] Database seeding optional flag --- lib/helper/Loki.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/helper/Loki.js b/lib/helper/Loki.js index 85f17c7bc..57ce8cc33 100644 --- a/lib/helper/Loki.js +++ b/lib/helper/Loki.js @@ -34,7 +34,7 @@ class Loki extends Helper { autosave: true, autosaveInterval: 10000, }, - dbSeedDir: "seed/" + dbSeed: true }; // override defaults with config @@ -52,16 +52,24 @@ class Loki extends Helper { static _config() { return [{ name: 'dbName', - message: "Database filename", + message: "What would you like to name the database?", default: 'db.json' + }, { + name: 'dbSeed', + type: "confirm", + message: 'Would you like to enable databse seeding?', + default: true }]; } _init() { this.dir = global.codecept_dir; this.debugSection('Dir', this.dir); + this._startDb(); - this._loadSeedData(); + this.seedDir = path.join("./seed"); + this.options.dbSeed && fs.mkdirSync(this.seedDir); + this.options.dbSeed && this._loadSeedData(); } _beforeSuite() {} @@ -81,7 +89,7 @@ class Loki extends Helper { } _loadSeedData() { - this.importData(this.options.dbSeedDir); + this.importData(this.seedDir); } addCollection(data) { From bb4510e1c6003963ea86bc3bb7999c490a12f48b Mon Sep 17 00:00:00 2001 From: Craig de Gouveia Date: Thu, 15 Jun 2017 17:09:50 +0100 Subject: [PATCH 13/16] Removed Appium from travis.yml to stop failing tests --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index cbc1b32b0..645e172fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,6 @@ env: - HELPER=Nightmare - HELPER=SeleniumWebdriver - HELPER=WebDriverIO - - HELPER=Appium addons: apt: packages: From 956264903d049da85ef52ed5ebe695d31d446688 Mon Sep 17 00:00:00 2001 From: Craig de Gouveia Date: Fri, 16 Jun 2017 10:08:49 +0100 Subject: [PATCH 14/16] Added docs for work so far --- docs/helpers/Appium.md | 1550 ++++------------------------------- docs/helpers/WebDriverIO.md | 230 ++++-- lib/helper/Loki.js | 78 +- package.json | 3 +- 4 files changed, 359 insertions(+), 1502 deletions(-) diff --git a/docs/helpers/Appium.md b/docs/helpers/Appium.md index 63dd4bb15..925593a15 100644 --- a/docs/helpers/Appium.md +++ b/docs/helpers/Appium.md @@ -1,1611 +1,377 @@ -Appium helper is extends from WebriverIO. It's support all browser methods and also includes special methods for mobile apps testing. You can use this helper to test Web on desktop and mobile devices and mobile apps. - -#### 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/) - - ``` - npm i -g appium - ``` -2. Download and install Android SDK or Android Studio to manage emulators from [Android Studio site](https://developer.android.com/studio/index.html#downloads) -3. Set the $ANDROID_HOME env variable to your Android SDK path - - Example: - ``` - export ANDROID_HOME=/Users/bestUser/Library/Android/sdk - ``` -4. Add Android SDK tools to $PATH - - Example: - ``` - export PATH="${ANDROID_HOME}/tools:$PATH" - export PATH="${ANDROID_HOME}/tools/bin:$PATH" - ``` -5. Add Java Home bin directory to $PATH - - Example: - ``` - export PATH="${JAVA_HOME}/bin:$PATH" - ``` -6. Create and launch emulator: - - From Android Studio: - * Create any empty android application - * Create emulator using this [documentation](https://developer.android.com/studio/run/managing-avds.html) - * Note that x86 emulators works much faster as ARM emulators, but you can use them only on Mac and Linux. - * Launch emulator from interface - - From Command line: - * Create emulator using [avdmanager commandline tool](https://developer.android.com/studio/command-line/avdmanager.html) - * Launch emulator using [emulator commandline tool](https://developer.android.com/studio/run/emulator-commandline.html) -7. Test that Appium is installed correctly using `appium-doctor` - - ``` - npm i -g appium-doctor - appium-doctor - ``` - If there are any issues, you have to fix them before Appium launch - -8. Launch the daemon: `appium` -9. Also on Mac you can launch tests for iOS devices. For this, install xcode from App store. You can get detailed information [here](http://appium.io/slate/en/master/?ruby#running-appium-on-mac-os-x) - - - -### Configuration - -This helper should be configured in codecept.conf.js - -#### Appium configuration - -- `port`: Appium server port -- `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: { - Appium: { - 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 Appium client from a custom helper by accessing `browser` property: - -```js -this.helpers['Appium'].browser -``` - -**Parameters** - -- `config` - -## _locate - -Get elements by different locator types, including strict locator -Should be used in custom helpers: - -```js -this.helpers['Appium']._locate({name: 'password'}).then //... -``` - -**Parameters** - -- `locator` - -## _locateCheckable - -Find a checkbox by providing human readable text: - -```js -this.helpers['Appium']._locateCheckable('I agree with terms and conditions').then // ... -``` - -**Parameters** - -- `locator` - -## _locateClickable - -Find a clickable element by providing human readable text: - -```js -this.helpers['Appium']._locateClickable('Next page').then // ... -``` - -**Parameters** - -- `locator` - -## _locateFields - -Find field elements by providing human readable text: - -```js -this.helpers['Appium']._locateFields('Your email').then // ... -``` - -**Parameters** - -- `locator` - -## acceptPopup - -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 -I.amOnPage('https://github.com'); // opens github -I.amOnPage('/login'); // opens a login page -``` - -**Parameters** - -- `url` url path or global url - -## appendField - -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'); -``` - -**Parameters** - -- `field` located by label|name|CSS|XPath|strict locator -- `value` text value - -## attachFile - -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'); -I.attachFile('form input[name=avatar]', 'data/avatar.jpg'); -``` - -**Parameters** - -- `locator` field located by label|name|CSS|XPath|strict locator -- `pathToFile` local file path relative to codecept.json config file - -## cancelPopup - -Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt. - -Appium: support only web testing - -## checkOption - -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. - -```js -I.checkOption('#agree'); -I.checkOption('I Agree to Terms and Conditions'); -I.checkOption('agree', '//form'); -``` - -**Parameters** - -- `field` checkbox located by label | name | CSS | XPath | strict locator -- `context` (optional) element located by CSS | XPath | strict locator - -## clearCookie - -Clears a cookie by name, -if none provided clears all cookies - -Appium: support only web testing - -```js -I.clearCookie(); -I.clearCookie('test'); -``` - -**Parameters** - -- `cookie` (optional) - -## clearField - -Clears a `