From 1779eee0e0c1b21074f8aa4dbd984a31cfc91374 Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Wed, 13 Jan 2016 14:12:20 -0600 Subject: [PATCH 1/7] Update tests to run more robustly locally --- .gitignore | 3 +++ .travis.yml | 17 ++++---------- package.json | 6 +++-- test/test-run.sh | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 15 deletions(-) create mode 100644 test/test-run.sh diff --git a/.gitignore b/.gitignore index f67609d..e3648b8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ pids *.pid *.seed +# Temp files +.tmp* + # Directory for instrumented libs generated by jscoverage/JSCover lib-cov diff --git a/.travis.yml b/.travis.yml index f0f5793..15d05aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,24 +5,15 @@ language: node_js node_js: - '0.10' + - '0.12' + - '4' + - '5' before_install: - sudo apt-get update - sudo apt-get install imagemagick graphicsmagick libcairo2-dev -before_script: - - "npm install -g bower http-server" - - "cd test/site && bower install && cd ../../" - - "curl -O http://selenium-release.storage.googleapis.com/2.43/selenium-server-standalone-2.43.1.jar" - - "java -jar selenium-server-standalone-2.43.1.jar -host 127.0.0.1 -port 4444 2>/dev/null 1>/dev/null &" - - "http-server -p 8080 &" - - "sleep 10" - - "if [[ $WEBDRIVERCSS_COVERAGE == '1' ]]; then ./node_modules/.bin/istanbul i lib -o lib-cov && cp lib/getPageInfo.js lib-cov && cp lib/makeScreenshot.js lib-cov && cp lib/documentScreenshot.js lib-cov && cp lib/viewportScreenshot.js lib-cov && cp lib/startSession.js lib-cov && cp lib/setScreenWidth.js lib-cov; fi" - -script: "npm run-script travis" - -after_script: - - "if [[ $WEBDRIVERCSS_COVERAGE == '1' ]]; then cat lcov.info | ./node_modules/coveralls/bin/coveralls.js; fi" +script: "npm test" env: matrix: diff --git a/package.json b/package.json index 3c248cf..e484b14 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "license": "MIT", "main": "index.js", "scripts": { - "test": "./node_modules/.bin/mocha", - "travis": "./node_modules/.bin/mocha -R $MOCHA_REPORTERS", + "test": "bash ./test/test-run.sh", + "test-cov": "WEBDRIVERCSS_COVERAGE=1 bash ./test/test-run.sh", "prepublish": "npm prune" }, "repository": { @@ -27,8 +27,10 @@ "tar.gz": "^1.0.1" }, "devDependencies": { + "bower": "^1.7.2", "chai": "^2.3.0", "coveralls": "~2.11.2", + "http-server": "^0.8.5", "istanbul": "^0.3.13", "mocha": "^2.2.4", "mocha-istanbul": "^0.2.0", diff --git a/test/test-run.sh b/test/test-run.sh new file mode 100644 index 0000000..13150dd --- /dev/null +++ b/test/test-run.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +set -ex + +finish() { + pkill -P $$ # kills all processes that have this pid - $$ - as the parent + exit $STATUS +} + +trap finish EXIT + +DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +PROJECT_DIR=$DIR/.. +NODE_BIN=$PROJECT_DIR/node_modules/.bin +MOCHA_REPORTERS="spec" + +# Runs selenium-server on travis +if [[ $TRAVIS == 'true' ]]; then + curl -O http://selenium-release.storage.googleapis.com/2.43/selenium-server-standalone-2.43.1.jar + java -jar selenium-server-standalone-2.43.1.jar -host 127.0.0.1 -port 4444 2>/dev/null 1>/dev/null & + # Wait for it + while ! echo exit | nc localhost 4444; do sleep 1; done +else + nc -zv 127.0.0.1 4444 > /dev/null + if [[ $? -eq 1 ]]; then + echo "Start selenium server before testing (selenium-standalone start)!" + exit 1 + fi +fi + +# Setup static site +cd $PROJECT_DIR/test/site && $NODE_BIN/bower install --config.interactive=false && cd $PROJECT_DIR +$NODE_BIN/http-server -p 8080 & + +# Wait for http-server +while ! echo exit | nc localhost 8080; do sleep 1; done + +# Setup coverage +if [[ $WEBDRIVERCSS_COVERAGE == '1' ]]; then + MOCHA_REPORTERS="mocha-istanbul" + $NODE_BIN/istanbul i lib -o lib-cov && \ + cp $PROJECT_DIR/lib/getPageInfo.js lib-cov && \ + cp $PROJECT_DIR/lib/makeScreenshot.js lib-cov && \ + cp $PROJECT_DIR/lib/documentScreenshot.js lib-cov && \ + cp $PROJECT_DIR/lib/viewportScreenshot.js lib-cov && \ + cp $PROJECT_DIR/lib/startSession.js lib-cov && \ + cp $PROJECT_DIR/lib/setScreenWidth.js lib-cov +fi + +# Run tests +$NODE_BIN/_mocha -R $MOCHA_REPORTERS +STATUS=$? + +# Echo coverage information +if [[ $WEBDRIVERCSS_COVERAGE == '1' ]]; then + ./node_modules/coveralls/bin/coveralls.js < lcov.info +fi + +exit $STATUS From 0b8ef6c50f68704e5dce89425359ccd5af60f038 Mon Sep 17 00:00:00 2001 From: Benjamin Urban Date: Tue, 8 Sep 2015 15:35:52 +0200 Subject: [PATCH 2/7] Add webdriverio 3 API compatibility --- lib/asyncCallback.js | 2 +- lib/documentScreenshot.js | 45 ++++++++++++++++++++++++------------- lib/getPageInfo.js | 17 ++++++++++---- lib/startSession.js | 17 +++++++------- lib/syncImages.js | 11 +++++---- lib/viewportScreenshot.js | 20 ++++++++++------- lib/webdrivercss.js | 8 +++---- lib/workflow.js | 22 +++++++++++++++--- package.json | 3 ++- test/spec/imageCapturing.js | 3 ++- 10 files changed, 99 insertions(+), 49 deletions(-) diff --git a/lib/asyncCallback.js b/lib/asyncCallback.js index 2fb5d38..a437cbe 100644 --- a/lib/asyncCallback.js +++ b/lib/asyncCallback.js @@ -4,7 +4,7 @@ * run workflow again or execute callback function */ -var workflow = require('./workflow.js'), +var workflow = require('./workflow.js').workflow, endSession = require('./endSession.js'); module.exports = function(err) { diff --git a/lib/documentScreenshot.js b/lib/documentScreenshot.js index 07ef210..752a39a 100644 --- a/lib/documentScreenshot.js +++ b/lib/documentScreenshot.js @@ -29,22 +29,20 @@ var async = require('async'), gm = require('gm'), rimraf = require('rimraf'), generateUUID = require('./generateUUID.js'), - path = require('path'); + path = require('path'), + q = require('q'); module.exports = function documentScreenshot(fileName) { + var defer = q.defer(); var ErrorHandler = this.instance.ErrorHandler; - /*! - * make sure that callback contains chainit callback - */ - var callback = arguments[arguments.length - 1]; - /*! * parameter check */ if (typeof fileName !== 'string') { - return callback(new ErrorHandler.CommandError('number or type of arguments don\'t agree with saveScreenshot command')); + defer.reject(new ErrorHandler.CommandError('number or type of arguments don\'t agree with saveScreenshot command')); + return defer.promise; } var self = this.instance, @@ -121,7 +119,10 @@ module.exports = function documentScreenshot(fileName) { documentHeight: document.documentElement.scrollHeight, devicePixelRatio: window.devicePixelRatio }; - }, cb); + }) + .then(function (res) { + cb(null, res); + }); }, /*! @@ -188,13 +189,18 @@ module.exports = function documentScreenshot(fileName) { * scroll to next area */ function() { + var callback = arguments[arguments.length - 1]; self.execute(scrollFn, currentXPos * response.execute[0].value.screenWidth, - currentYPos * response.execute[0].value.screenHeight, - function(val, res) { - response.execute.push(res); - } - ).pause(100).call(arguments[arguments.length - 1]); + currentYPos * response.execute[0].value.screenHeight + ) + .then(function (res) { + response.execute.push(res); + }) + .pause(100) + .then(function () { + callback(); + }); } ], finishedScreenshot); @@ -242,10 +248,19 @@ module.exports = function documentScreenshot(fileName) { * scroll back to start position */ function(cb) { - self.execute(scrollFn, 0, 0, cb); + self + .execute(scrollFn, 0, 0) + .then(function () { + cb(); + }); } ], function(err) { - callback(err, null, response); + if (err) { + defer.reject(err); + } else { + defer.resolve(); + } }); + return defer.promise }; diff --git a/lib/getPageInfo.js b/lib/getPageInfo.js index 2b266c4..c7041e5 100644 --- a/lib/getPageInfo.js +++ b/lib/getPageInfo.js @@ -72,8 +72,10 @@ module.exports = function(done) { screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), screenHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) }; - - }, cb); + }) + .then(function (pageInfo) { + cb(null, pageInfo); + }); }, /** @@ -101,7 +103,11 @@ module.exports = function(done) { left: boundingRect.left } }; - }, cb); + }) + .then(function (elementInfo) { + // ! TODO check response and its schema being correct here + cb(null, elementInfo, response); + }); }, /** @@ -168,7 +174,10 @@ module.exports = function(done) { return excludeRect; - }, done); + }) + .then(function (excludeRect) { + done(null, excludeRect); + }); } ], function(err, excludeElements) { diff --git a/lib/startSession.js b/lib/startSession.js index 0e5fd27..1aba41d 100644 --- a/lib/startSession.js +++ b/lib/startSession.js @@ -29,15 +29,16 @@ module.exports = function() { screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), documentHeight: document.documentElement.scrollHeight }; - }, function(err, res) { - that.useragent = res.value.useragent; - that.displaySize = { - width: that.screenWidth && that.screenWidth.length ? that.screenWidth[0] : res.value.screenWidth, - height: res.value.documentHeight - }; + }) + .then(function(res) { + that.useragent = res.value.useragent; + that.displaySize = { + width: that.screenWidth && that.screenWidth.length ? that.screenWidth[0] : res.value.screenWidth, + height: res.value.documentHeight + }; - return cb(); - }); + return cb(); + }); }, /** diff --git a/lib/syncImages.js b/lib/syncImages.js index e878ce3..3595177 100644 --- a/lib/syncImages.js +++ b/lib/syncImages.js @@ -5,7 +5,8 @@ var fs = require('fs-extra'), zlib = require('zlib'), targz = require('tar.gz'), rimraf = require('rimraf'), - request = require('request'); + request = require('request'), + q = require('q'); /** * sync down @@ -105,6 +106,8 @@ var syncUp = function(done) { */ module.exports = function(done) { + var defer = q.defer(); + if(!this.api) { return done(new Error('No sync options specified! Please provide an api path and user/key (optional).')); } @@ -116,11 +119,11 @@ module.exports = function(done) { /*istanbul ignore next*/ if(err || (httpResponse && httpResponse.statusCode !== 200)) { - return done(new Error(err || body)); + return defer.reject(new Error(err || body)); } - done(); + defer.resolve(); }); - return this; + return defer.promise; }; diff --git a/lib/viewportScreenshot.js b/lib/viewportScreenshot.js index 64ee704..e4e55fb 100644 --- a/lib/viewportScreenshot.js +++ b/lib/viewportScreenshot.js @@ -7,22 +7,20 @@ */ var async = require('async'), - gm = require('gm'); + gm = require('gm'), + q = require('q'); module.exports = function viewportScreenshot(fileName) { var ErrorHandler = this.instance.ErrorHandler; - - /*! - * make sure that callback contains chainit callback - */ - var callback = arguments[arguments.length - 1]; + var defer = q.defer(); /*! * parameter check */ if (typeof fileName !== 'string') { - return callback(new ErrorHandler.CommandError('number or type of arguments don\'t agree with saveScreenshot command')); + defer.reject(new ErrorHandler.CommandError('number or type of arguments don\'t agree with saveScreenshot command')); + return defer.promise; } var self = this.instance, @@ -95,8 +93,14 @@ module.exports = function viewportScreenshot(fileName) { } ], function(err) { - callback(err, null, response); + if (err) { + return defer.reject(err); + } + + defer.resolve(response); }); + return defer.promise; + }; diff --git a/lib/webdrivercss.js b/lib/webdrivercss.js index 9f71025..d2ccbcf 100644 --- a/lib/webdrivercss.js +++ b/lib/webdrivercss.js @@ -69,10 +69,10 @@ var WebdriverCSS = function(webdriverInstance, options) { /** * add WebdriverCSS command to WebdriverIO instance */ - this.instance.addCommand('saveViewportScreenshot', viewportScreenshot.bind(this)); - this.instance.addCommand('saveDocumentScreenshot', documentScreenshot.bind(this)); - this.instance.addCommand('webdrivercss', workflow.bind(this)); - this.instance.addCommand('sync', syncImages.bind(this)); + this.instance.addCommand('saveViewportScreenshot', viewportScreenshot.bind(this), true); + this.instance.addCommand('saveDocumentScreenshot', documentScreenshot.bind(this), true); + this.instance.addCommand('webdrivercss', workflow.promise.bind(this), true); + this.instance.addCommand('sync', syncImages.bind(this), true); return this; }; diff --git a/lib/workflow.js b/lib/workflow.js index 6481ebb..13aa0f5 100644 --- a/lib/workflow.js +++ b/lib/workflow.js @@ -5,9 +5,21 @@ */ var async = require('async'); +var q = require('q'); -module.exports = function(pagename, args) { +function wrappedWorkflow(pagename, args) { + var defer = q.defer(); + function cb(err, result) { + defer.resolve(result); + } + + workflow.call(this, pagename, args, cb); + + return defer.promise; +} + +function workflow(pagename, args) { /*! * make sure that callback contains chainit callback */ @@ -126,6 +138,10 @@ module.exports = function(pagename, args) { * run workflow again or execute callback function */ require('./asyncCallback.js').bind(context) - ); -}; +} + +module.exports = { + workflow: workflow, + promise: wrappedWorkflow +}; \ No newline at end of file diff --git a/package.json b/package.json index e484b14..1a6088b 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "glob": "^5.0.5", "gm": "^1.17.0", "node-resemble-js": "0.0.4", + "q": "^1.4.1", "request": "^2.55.0", "rimraf": "^2.3.2", "tar": "^2.1.0", @@ -35,7 +36,7 @@ "mocha": "^2.2.4", "mocha-istanbul": "^0.2.0", "nock": "^1.7.1", - "webdriverio": "^2.4.5" + "webdriverio": "^3.2.1" }, "keywords": [ "webdriverjs", diff --git a/test/spec/imageCapturing.js b/test/spec/imageCapturing.js index 54513bf..1815d64 100644 --- a/test/spec/imageCapturing.js +++ b/test/spec/imageCapturing.js @@ -24,7 +24,8 @@ describe('WebdriverCSS captures desired parts of a website as screenshot with sp .webdrivercss('testWithoutParameter', { name: 'withoutParams'}) .execute(function(){ return document.body.clientHeight; - }, function(err,res) { + }) + .then(function(res) { documentHeight = res.value; }) .call(done); From ff30613fe69fbdb5aefad4602b42309f08fcae82 Mon Sep 17 00:00:00 2001 From: Benjamin Urban Date: Tue, 8 Sep 2015 18:05:35 +0200 Subject: [PATCH 3/7] Update a few more function calls to wdio 3 api --- lib/endSession.js | 5 ++++- lib/makeScreenshot.js | 41 ++++++++++++++++++++++++++------------- lib/setScreenWidth.js | 9 +++++---- lib/viewportScreenshot.js | 24 ++++++++++++++++++++--- 4 files changed, 57 insertions(+), 22 deletions(-) diff --git a/lib/endSession.js b/lib/endSession.js index e50f617..362332c 100644 --- a/lib/endSession.js +++ b/lib/endSession.js @@ -20,7 +20,10 @@ module.exports = function(done) { that.instance.windowHandleSize({ width: that.self.defaultScreenDimension.width, height: that.self.defaultScreenDimension.height - }, cb); + }) + .then(function (res) { + cb(null, res); + }); }, /** * end session when using applitools diff --git a/lib/makeScreenshot.js b/lib/makeScreenshot.js index 5c2cd99..5fb2305 100644 --- a/lib/makeScreenshot.js +++ b/lib/makeScreenshot.js @@ -1,5 +1,7 @@ 'use strict'; +var q = require('q'); + /** * make screenshot via [GET] /session/:sessionId/screenshot */ @@ -8,7 +10,7 @@ var modifyElements = function(elements, style, value) { return; } - this.instance.selectorExecute(elements, function() { + return this.instance.selectorExecute(elements, function() { var args = Array.prototype.slice.call(arguments).filter(function(n){ return !!n; }), style = args[args.length - 2], value = args[args.length - 1]; @@ -24,6 +26,7 @@ var modifyElements = function(elements, style, value) { }; module.exports = function(done) { + var that = this; /** * take actual screenshot in given screensize just once @@ -57,18 +60,28 @@ module.exports = function(done) { /** * hide / remove elements */ - modifyElements.call(this, hiddenElements, 'visibility', 'hidden'); - modifyElements.call(this, removeElements, 'display', 'none'); - - /** - * take 100ms pause to give browser time for rendering - */ - this.instance.pause(100).saveDocumentScreenshot(this.screenshot, done); - - /** - * make hidden elements visible again - */ - modifyElements.call(this, hiddenElements, 'visibility', ''); - modifyElements.call(this, removeElements, 'display', ''); + q.all([ + modifyElements.call(that, hiddenElements, 'visibility', 'hidden'), + modifyElements.call(that, removeElements, 'display', 'none') + ]) + .then(function () { + /** + * take 100ms pause to give browser time for rendering + */ + return that.instance.pause(100).saveDocumentScreenshot(that.screenshot); + }) + .then(function () { + /** + * make hidden elements visible again + */ + return q.all([ + modifyElements.call(that, hiddenElements, 'visibility', ''), + modifyElements.call(that, removeElements, 'display', '') + ]); + }) + .then(function () { + done(); + }) + .done(); }; diff --git a/lib/setScreenWidth.js b/lib/setScreenWidth.js index 3f53e21..dd6f7d7 100644 --- a/lib/setScreenWidth.js +++ b/lib/setScreenWidth.js @@ -19,10 +19,11 @@ module.exports = function(done) { */ function(cb) { if(!that.self.defaultScreenDimension && that.screenWidth && that.screenWidth.length) { - that.instance.windowHandleSize(function(err,res) { - that.self.defaultScreenDimension = res.value; - cb(); - }); + that.instance.windowHandleSize() + .then(function(res) { + that.self.defaultScreenDimension = res.value; + cb(); + }); } else { cb(); } diff --git a/lib/viewportScreenshot.js b/lib/viewportScreenshot.js index e4e55fb..a10c593 100644 --- a/lib/viewportScreenshot.js +++ b/lib/viewportScreenshot.js @@ -56,7 +56,13 @@ module.exports = function viewportScreenshot(fileName) { * get scroll position */ function(cb) { - self.execute(getScrollingPosition, null, cb); + self.execute(getScrollingPosition, null) + .then(function (res) { + cb(null, res); + }) + .catch(function (err) { + cb(err); + }); }, /*! @@ -70,11 +76,23 @@ module.exports = function viewportScreenshot(fileName) { screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), screenHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) }; - }, cb); + }) + .then(function (res) { + cb(null, res); + }) + .catch(function (err) { + cb(err); + }); }, function(res, cb) { response.execute.push(res); - self.screenshot(cb); + self.screenshot() + .then(function (screenshotRes) { + cb(null, screenshotRes); + }) + .catch(function (err) { + cb(err); + }); }, function(res, cb) { response.screenshot = res; From 3f7497ddf5ed2743619478185c37af448be1c5fc Mon Sep 17 00:00:00 2001 From: christian-bromann Date: Wed, 28 Oct 2015 23:01:36 +0100 Subject: [PATCH 4/7] v2.0.0beta-rc1 --- CHANGELOG.md | 3 +++ README.md | 2 +- package.json | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f610f7..549f546 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## Release History +## 2.0.0beta-rc1 (2015-10-28) +* merged dev branch to make WebdriverCSS compatible with v3 + ## v1.1.10 (2015-12-19) * Adjust scrolling to better support sticky headers (#131) * Add option to tweak node-resemble-js image comparison (#121) diff --git a/README.md b/README.md index a7a0240..d055a3b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -WebdriverCSS [![Version](http://img.shields.io/badge/version-v1.1.10-brightgreen.svg)](https://www.npmjs.org/package/webdrivercss) [![Build Status](https://travis-ci.org/webdriverio/webdrivercss.png?branch=master)](https://travis-ci.org/webdriverio/webdrivercss) [![Coverage Status](https://coveralls.io/repos/webdriverio/webdrivercss/badge.png?branch=master)](https://coveralls.io/r/webdriverio/webdrivercss?branch=master) [![Join the chat at https://gitter.im/webdriverio/webdrivercss](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/webdriverio/webdrivercss?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +WebdriverCSS [![Version](http://img.shields.io/badge/version-v2.0.0beta-rc1-brightgreen.svg)](https://www.npmjs.org/package/webdrivercss) [![Build Status](https://travis-ci.org/webdriverio/webdrivercss.png?branch=master)](https://travis-ci.org/webdriverio/webdrivercss) [![Coverage Status](https://coveralls.io/repos/webdriverio/webdrivercss/badge.png?branch=master)](https://coveralls.io/r/webdriverio/webdrivercss?branch=master) [![Join the chat at https://gitter.im/webdriverio/webdrivercss](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/webdriverio/webdrivercss?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ============ --- diff --git a/package.json b/package.json index 1a6088b..f0a097b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webdrivercss", - "version": "1.1.10", + "version": "2.0.0beta-rc1", "description": "Regression testing tool for WebdriverJS", "author": "Christian Bromann ", "license": "MIT", From 75f7e3778df3eeb3f6fc27eefd9195bb90df7782 Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Wed, 13 Jan 2016 17:37:31 -0600 Subject: [PATCH 5/7] Convert to bluebird promises. --- lib/asyncCallback.js | 69 ++++--- lib/compareImages.js | 57 +++--- lib/cropImage.js | 134 ++++++------ lib/documentScreenshot.js | 397 +++++++++++++++++------------------- lib/endSession.js | 100 ++++----- lib/getPageInfo.js | 266 ++++++++++++------------ lib/makeScreenshot.js | 40 ++-- lib/renameFiles.js | 19 +- lib/saveImageDiff.js | 151 +++++++------- lib/setScreenWidth.js | 107 +++++----- lib/startSession.js | 135 ++++++------ lib/syncImages.js | 115 +++++------ lib/util/streamToPromise.js | 8 + lib/viewportScreenshot.js | 159 ++++++--------- lib/webdrivercss.js | 2 +- lib/workflow.js | 107 ++++------ package.json | 4 +- 17 files changed, 853 insertions(+), 1017 deletions(-) create mode 100644 lib/util/streamToPromise.js diff --git a/lib/asyncCallback.js b/lib/asyncCallback.js index a437cbe..9d3e841 100644 --- a/lib/asyncCallback.js +++ b/lib/asyncCallback.js @@ -4,53 +4,56 @@ * run workflow again or execute callback function */ -var workflow = require('./workflow.js').workflow, - endSession = require('./endSession.js'); +var workflow = require('./workflow.js'); +var endSession = require('./endSession.js'); +var Promise = require('bluebird'); module.exports = function(err) { - - var that = this; - /** * if error occured don't do another shot (if multiple screen width are set) */ /*istanbul ignore next*/ - if(err) { - return this.cb(err); - } + if (err) return Promise.reject(err); + return Promise.try(function() { - /** - * on multiple screenWidth or multiple page elements - * repeat workflow - */ - if(this.screenWidth && this.screenWidth.length) { + var ctx = this; /** - * if multiple screen widths are given - * start workflow all over again with same parameter + * on multiple screenWidth or multiple page elements + * repeat workflow */ - this.queuedShots[0].screenWidth = this.screenWidth; - return workflow.call(this.self, this.pagename, this.queuedShots, this.cb); + if(ctx.screenWidth && ctx.screenWidth.length) { + + /** + * if multiple screen widths are given + * start workflow all over again with same parameter + */ + ctx.queuedShots[0].screenWidth = ctx.screenWidth; + return workflow.call(ctx.self, ctx.pagename, ctx.queuedShots); + + } else if (ctx.queuedShots.length > 1) { - } else if (this.queuedShots.length > 1) { + /** + * if multiple page modules are given + */ + return endSession.call(ctx) + .then(function() { + ctx.queuedShots.shift(); + return workflow.call(ctx.self, ctx.pagename, ctx.queuedShots, ctx.cb); + }); + + } /** - * if multiple page modules are given + * finish command */ - return endSession.call(this, function() { - that.queuedShots.shift(); - return workflow.call(that.self, that.pagename, that.queuedShots, that.cb); + return endSession.call(ctx) + .then(function() { + ctx.self.takeScreenshot = undefined; + return ctx.self.resultObject; + }) + .finally(function() { + ctx.self.resultObject = {}; }); - - } - - /** - * finish command - */ - return endSession.call(this, function(err) { - that.self.takeScreenshot = undefined; - that.cb(err, that.self.resultObject); - that.self.resultObject = {}; }); - }; diff --git a/lib/compareImages.js b/lib/compareImages.js index 15133a9..62d1450 100644 --- a/lib/compareImages.js +++ b/lib/compareImages.js @@ -13,30 +13,35 @@ module.exports = function() { */ var done = arguments[arguments.length - 1]; - /** - * if there is no need for image comparison or no images gets saved on fs, just continue - */ - if(!this.isComparable || !this.self.saveImages) { - return done(); - } - - /** - * compare images - */ - var diff = resemble(this.baselinePath).compareTo(this.regressionPath); - - /** - * map 'ignore' configuration to resemble options - */ - var ignore = this.currentArgs.ignore || ""; - if (ignore.indexOf("color") === 0) { - diff.ignoreColors(); - } else if (ignore.indexOf("antialias") === 0) { - diff.ignoreAntialiasing(); - } - - /** - * execute the comparison - */ - diff.onComplete(done.bind(null,null)); + return Promise.try(function() { + /** + * if there is no need for image comparison or no images gets saved on fs, just continue + */ + if(!this.isComparable || !this.self.saveImages) { + return; + } + + /** + * compare images + */ + var diff = resemble(this.baselinePath).compareTo(this.regressionPath); + + /** + * map 'ignore' configuration to resemble options + */ + var ignore = this.currentArgs.ignore || ""; + if (ignore.indexOf("color") === 0) { + diff.ignoreColors(); + } else if (ignore.indexOf("antialias") === 0) { + diff.ignoreAntialiasing(); + } + + /** + * execute the comparison + */ + return Promise.fromCallback(function(cb) { + diff.onComplete(cb.bind(null,null)); + }); + }) + .nodeify(done); }; diff --git a/lib/cropImage.js b/lib/cropImage.js index 8a7fc34..ad79dd2 100644 --- a/lib/cropImage.js +++ b/lib/cropImage.js @@ -4,99 +4,93 @@ * crop image according to user arguments and its position on screen and save it */ -var gm = require('gm'), - async = require('async'), - request = require('request'), - exclude = require('./exclude.js'); +var Promise = require('bluebird'); +var gm = require('gm'); +Promise.promisifyAll(gm.prototype); +var request = Promise.promisify(require('request'), {multiArg: true}); +var exclude = require('./exclude.js'); module.exports = function(res, done) { - var that = this, - excludeRect = res.excludeRect, - shot = gm(this.screenshot).quality(100), - cropDim; + var ctx = this; + var excludeRect = res.excludeRect; + var shot = gm(this.screenshot).quality(100); + var cropDim; - var x = parseInt(this.currentArgs.x, 10); - var y = parseInt(this.currentArgs.y, 10); - var width = parseInt(this.currentArgs.width, 10); - var height = parseInt(this.currentArgs.height, 10); + return Promise.try(function() { - if (!isNaN(x) && !isNaN(y) && !isNaN(width) && !isNaN(height)) { + var x = parseInt(this.currentArgs.x, 10); + var y = parseInt(this.currentArgs.y, 10); + var width = parseInt(this.currentArgs.width, 10); + var height = parseInt(this.currentArgs.height, 10); - /** - * crop image with given arguments - */ - cropDim = { - x: x - res.scrollPos.x, - y: y - res.scrollPos.y, - width: width, - height: height - }; + if (!isNaN(x) && !isNaN(y) && !isNaN(width) && !isNaN(height)) { - exclude(shot, excludeRect); - shot.crop(cropDim.width, cropDim.height, cropDim.x, cropDim.y); + /** + * crop image with given arguments + */ + cropDim = { + x: x - res.scrollPos.x, + y: y - res.scrollPos.y, + width: width, + height: height + }; - } else if (res && res.elemBounding) { + exclude(shot, excludeRect); + shot.crop(cropDim.width, cropDim.height, cropDim.x, cropDim.y); - /** - * or use boundary of specific CSS element - */ - cropDim = { - x: res.elemBounding.left + (res.elemBounding.width / 2), - y: res.elemBounding.top + (res.elemBounding.height / 2), - width: isNaN(width) ? res.elemBounding.width : width, - height: isNaN(height) ? res.elemBounding.height : height - }; + } else if (res && res.elemBounding) { - exclude(shot, excludeRect); - shot.crop(cropDim.width, cropDim.height, cropDim.x - (cropDim.width / 2), cropDim.y - (cropDim.height / 2)); + /** + * or use boundary of specific CSS element + */ + cropDim = { + x: res.elemBounding.left + (res.elemBounding.width / 2), + y: res.elemBounding.top + (res.elemBounding.height / 2), + width: isNaN(width) ? res.elemBounding.width : width, + height: isNaN(height) ? res.elemBounding.height : height + }; - } else { - exclude(shot, excludeRect); - } + exclude(shot, excludeRect); + shot.crop(cropDim.width, cropDim.height, cropDim.x - (cropDim.width / 2), cropDim.y - (cropDim.height / 2)); - async.waterfall([ - /** - * save image to fs - */ - function(cb) { - if(!that.self.saveImages) { - return cb(); - } + } else { + exclude(shot, excludeRect); + } + }) + .then(function() { + if (!ctx.self.saveImages) return; - return shot.write(that.filename || that.baselinePath, cb); - }, /** - * generate image buffer + * save image to fs */ - function() { - var cb = arguments[arguments.length - 1]; - return shot.toBuffer('PNG', cb); - }, + return shot.writeAsync(ctx.filename || ctx.baselinePath) + .then(function() { + /** + * generate image buffer + */ + return shot.toBufferAsync('PNG'); + }) /** * upload image to applitools */ - function(buffer) { - var cb = arguments[arguments.length - 1]; - if (!that.self.usesApplitools) { - return cb(); - } - request({ - qs: {apiKey: that.applitools.apiKey}, - url: that.self.host + '/api/sessions/running/' + that.self.sessionId, + .then(function(buffer) { + if (!ctx.self.usesApplitools) return; + return request({ + qs: {apiKey: ctx.applitools.apiKey}, + url: ctx.self.host + '/api/sessions/running/' + ctx.self.sessionId, method: 'POST', - headers: that.self.headers, - timeout: that.self.reqTimeout, + headers: ctx.self.headers, + timeout: ctx.self.reqTimeout, json: { 'appOutput': { 'title': res.title, 'screenshot64': new Buffer(buffer).toString('base64') }, - 'tag': that.currentArgs.tag || '', - 'ignoreMismatch': that.currentArgs.ignoreMismatch || false + 'tag': ctx.currentArgs.tag || '', + 'ignoreMismatch': ctx.currentArgs.ignoreMismatch || false } - }, cb); - } - ], done); - + }); + }); + }).nodeify(done); }; diff --git a/lib/documentScreenshot.js b/lib/documentScreenshot.js index 752a39a..a00028b 100644 --- a/lib/documentScreenshot.js +++ b/lib/documentScreenshot.js @@ -24,16 +24,15 @@ /* global document,window */ -var async = require('async'), - fs = require('fs'), - gm = require('gm'), - rimraf = require('rimraf'), - generateUUID = require('./generateUUID.js'), - path = require('path'), - q = require('q'); +var Promise = require('bluebird'); +var fs = Promise.promisifyAll(require('fs')); +var gm = require('gm'); +Promise.promisifyAll(gm.prototype); +var rimraf = Promise.promisify(require('rimraf')); +var generateUUID = require('./generateUUID.js'); +var path = require('path'); -module.exports = function documentScreenshot(fileName) { - var defer = q.defer(); +module.exports = function documentScreenshot(fileName, cb) { var ErrorHandler = this.instance.ErrorHandler; @@ -41,226 +40,196 @@ module.exports = function documentScreenshot(fileName) { * parameter check */ if (typeof fileName !== 'string') { - defer.reject(new ErrorHandler.CommandError('number or type of arguments don\'t agree with saveScreenshot command')); - return defer.promise; + return Promise.reject( + new ErrorHandler.CommandError('first parameter to documentScreenshot() must be a string fileName')); } - var self = this.instance, - response = { - execute: [], - screenshot: [] - }, - tmpDir = null, - cropImages = [], - currentXPos = 0, - currentYPos = 0, - screenshot = null, - scrollFn = function(w, h) { - /** - * IE8 or older - */ - if(document.all && !document.addEventListener) { - /** - * this still might not work - * seems that IE8 scroll back to 0,0 before taking screenshots - */ - document.body.style.marginTop = '-' + h + 'px'; - document.body.style.marginLeft = '-' + w + 'px'; - return; - } - - document.documentElement.style.webkitTransform = 'translate(-' + w + 'px, -' + h + 'px)'; - document.documentElement.style.mozTransform = 'translate(-' + w + 'px, -' + h + 'px)'; - document.documentElement.style.msTransform = 'translate(-' + w + 'px, -' + h + 'px)'; - document.documentElement.style.oTransform = 'translate(-' + w + 'px, -' + h + 'px)'; - document.documentElement.style.transform = 'translate(-' + w + 'px, -' + h + 'px)'; - }; + var self = this.instance; + var response = { + execute: [], + screenshot: [] + }; + var tmpDir = null; + var cropImages = []; + var currentXPos = 0; + var currentYPos = 0; + var screenshot = null; - async.waterfall([ - - /*! - * create tmp directory to cache viewport shots - */ - function(cb) { - var uuid = generateUUID(); - tmpDir = path.join(__dirname, '..', '.tmp-' + uuid); + /*! + * create tmp directory to cache viewport shots + */ + return Promise.try(function createTmp() { + var uuid = generateUUID(); + tmpDir = path.join(__dirname, '..', '.tmp-' + uuid); + + return fs.existsAsync(tmpDir) + .then(function(exists) { + if (!exists) return fs.mkdirAsync(tmpDir, '0755'); + }); + }) + /*! + * prepare page scan + */ + .then(function pageScan() { + return self.execute(getViewportSize); + }) + /*! + * take viewport shots and cache them into tmp dir + */ + .then(function takeShots(res) { + response.execute.push(res); + response.screenshot = []; + + function takeScreenshot() { + + // Take screenshot + return self.screenshot.bind(self) + // Cache image into tmp dir + .then(function cacheImage(res) { + var file = tmpDir + '/' + currentXPos + '-' + currentYPos + '.png'; + var image = gm(new Buffer(res.value, 'base64')); + + if (response.execute[0].value.devicePixelRatio > 1) { + var percent = 100 / response.execute[0].value.devicePixelRatio; + image.resize(percent, percent, "%"); + } - fs.exists(tmpDir, function(exists) { - return exists ? cb() : fs.mkdir(tmpDir, '0755', cb); - }); - }, + image.crop(response.execute[0].value.screenWidth, response.execute[0].value.screenHeight, 0, 0); + response.screenshot.push(res); - /*! - * prepare page scan - */ - function() { - var cb = arguments[arguments.length - 1]; - self.execute(function() { - /** - * remove scrollbars - */ - // reset height in case we're changing viewports - document.documentElement.style.height = 'auto'; - document.documentElement.style.height = document.documentElement.scrollHeight + 'px'; - document.documentElement.style.overflow = 'hidden'; + if (!cropImages[currentXPos]) { + cropImages[currentXPos] = []; + } - /** - * scroll back to start scanning - */ - window.scrollTo(0, 0); + cropImages[currentXPos][currentYPos] = file; - /** - * get viewport width/height and total width/height - */ - return { - screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), - screenHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0), - documentWidth: document.documentElement.scrollWidth, - documentHeight: document.documentElement.scrollHeight, - devicePixelRatio: window.devicePixelRatio - }; + currentYPos++; + if (currentYPos > Math.floor(response.execute[0].value.documentHeight / response.execute[0].value.screenHeight)) { + currentYPos = 0; + currentXPos++; + } + return image.writeAsync(file); }) - .then(function (res) { - cb(null, res); + // scroll to next area + .then(function scrollToNext() { + return self.execute(scrollFn, + currentXPos * response.execute[0].value.screenWidth, + currentYPos * response.execute[0].value.screenHeight + ) + .then(function (res) { + response.execute.push(res); + }) + // FIXME do we need this? + .pause(100); }); - }, - - /*! - * take viewport shots and cache them into tmp dir - */ - function(res, cb) { - response.execute.push(res); - - /*! - * run scan - */ - async.whilst( - - /*! - * while expression - */ - function() { - return (currentXPos < (response.execute[0].value.documentWidth / response.execute[0].value.screenWidth)); - }, - - /*! - * loop function - */ - function(finishedScreenshot) { - response.screenshot = []; - - async.waterfall([ - - /*! - * take screenshot of viewport - */ - self.screenshot.bind(self), - - /*! - * cache image into tmp dir - */ - function(res, cb) { - var file = tmpDir + '/' + currentXPos + '-' + currentYPos + '.png'; - var image = gm(new Buffer(res.value, 'base64')); - - if (response.execute[0].value.devicePixelRatio > 1) { - var percent = 100 / response.execute[0].value.devicePixelRatio; - image.resize(percent, percent, "%"); - } - - image.crop(response.execute[0].value.screenWidth, response.execute[0].value.screenHeight, 0, 0); - image.write(file, cb); - response.screenshot.push(res); - - if (!cropImages[currentXPos]) { - cropImages[currentXPos] = []; - } - - cropImages[currentXPos][currentYPos] = file; - - currentYPos++; - if (currentYPos > Math.floor(response.execute[0].value.documentHeight / response.execute[0].value.screenHeight)) { - currentYPos = 0; - currentXPos++; - } - }, - - /*! - * scroll to next area - */ - function() { - var callback = arguments[arguments.length - 1]; - self.execute(scrollFn, - currentXPos * response.execute[0].value.screenWidth, - currentYPos * response.execute[0].value.screenHeight - ) - .then(function (res) { - response.execute.push(res); - }) - .pause(100) - .then(function () { - callback(); - }); - } - - ], finishedScreenshot); - }, - cb - ); - }, - - /*! - * concats all shots - */ - function(cb) { - var subImg = 0; - - async.eachSeries(cropImages, function(x, cb) { - var col = gm(x.shift()); - col.append.apply(col, x); + } - if (!screenshot) { - screenshot = col; - col.write(fileName, cb); - } else { - col.write(tmpDir + '/' + (++subImg) + '.png', function() { - gm(fileName).append(tmpDir + '/' + subImg + '.png', true).write(fileName, cb); - }); + // async while expression + function loop() { + return takeScreenshot() + .then(function checkIfDone() { + if (currentXPos < (response.execute[0].value.documentWidth / response.execute[0].value.screenWidth)) { + return loop(); } - }, cb); - }, - - /*! - * crop screenshot regarding page size - */ - function() { - gm(fileName).crop(response.execute[0].value.documentWidth, response.execute[0].value.documentHeight, 0, 0).write(fileName, arguments[arguments.length - 1]); - }, + }); + } /*! - * remove tmp dir + * run scan */ - function() { - rimraf(tmpDir, arguments[arguments.length - 1]); - }, + return loop(); + }) + // FIXME this seems like an optimization opportunity, we're thrashing disk; + // could also append in the loop above + .then(function concat() { + var subImg = 0; + return Promise.mapSeries(cropImages, function(item) { + var col = gm(item.shift()); + col.append.apply(col, item); + + if (!screenshot) { + screenshot = col; + return col.writeAsync(fileName); + } else { + return col.writeAsync(tmpDir + '/' + (++subImg) + '.png') + .then(function() { + return gm(fileName).append(tmpDir + '/' + subImg + '.png', true).writeAsync(fileName); + }); + } + }); + }) + /*! + * crop screenshot regarding page size + */ + .then(function cropScreenshot() { + return Promise.fromCallback(function(cb) { + gm(fileName) + .crop(response.execute[0].value.documentWidth, response.execute[0].value.documentHeight, 0, 0) + .write(fileName, cb); + }); + }) + /*! + * remove tmp dir + */ + .then(function() { + return rimraf(tmpDir); + }) + /*! + * scroll back to start position + */ + .then(function() { + return self.execute(scrollFn, 0, 0); + }) + // Return response object. + .return(response) + .nodeify(cb); +}; - /*! - * scroll back to start position +/*eslint-disable*/ +function scrollFn(w, h) { + /** + * IE8 or older + */ + if(document.all && !document.addEventListener) { + /** + * this still might not work + * seems that IE8 scroll back to 0,0 before taking screenshots */ - function(cb) { - self - .execute(scrollFn, 0, 0) - .then(function () { - cb(); - }); - } - ], function(err) { - if (err) { - defer.reject(err); - } else { - defer.resolve(); - } - }); + document.body.style.marginTop = '-' + h + 'px'; + document.body.style.marginLeft = '-' + w + 'px'; + return; + } - return defer.promise + document.documentElement.style.webkitTransform = 'translate(-' + w + 'px, -' + h + 'px)'; + document.documentElement.style.mozTransform = 'translate(-' + w + 'px, -' + h + 'px)'; + document.documentElement.style.msTransform = 'translate(-' + w + 'px, -' + h + 'px)'; + document.documentElement.style.oTransform = 'translate(-' + w + 'px, -' + h + 'px)'; + document.documentElement.style.transform = 'translate(-' + w + 'px, -' + h + 'px)'; }; + +function getViewportSize() { + /** + * remove scrollbars + */ + // reset height in case we're changing viewports + document.documentElement.style.height = 'auto'; + document.documentElement.style.height = document.documentElement.scrollHeight + 'px'; + document.documentElement.style.overflow = 'hidden'; + + /** + * scroll back to start scanning + */ + window.scrollTo(0, 0); + + /** + * get viewport width/height and total width/height + */ + return { + screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), + screenHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0), + documentWidth: document.documentElement.scrollWidth, + documentHeight: document.documentElement.scrollHeight, + devicePixelRatio: window.devicePixelRatio + }; +} +/*eslint-enable*/ diff --git a/lib/endSession.js b/lib/endSession.js index 362332c..41ab818 100644 --- a/lib/endSession.js +++ b/lib/endSession.js @@ -1,71 +1,51 @@ 'use strict'; -var async = require('async'), - merge = require('deepmerge'), - request = require('request'); +var merge = require('deepmerge'); +var Promise = require('bluebird'); +var request = Promise.promisify(require('request'), {multiArgs: true}); module.exports = function(done) { var that = this; - async.waterfall([ + return Promise.try(function setOriginalResolution() { /** * if screenwidth was set, get back to old resolution */ - function(cb) { - if (!that.self.defaultScreenDimension) { - return cb(); - } - - that.instance.windowHandleSize({ - width: that.self.defaultScreenDimension.width, - height: that.self.defaultScreenDimension.height - }) - .then(function (res) { - cb(null, res); - }); - }, - /** - * end session when using applitools - */ - function() { - var cb = arguments[arguments.length - 1]; - - if(!that.self.usesApplitools) { - return cb(); - } - - // Whether or not we should automatically save this test as baseline. - var updateBaseline = (that.self.isNew && that.applitools.saveNewTests) || - (!that.self.isNew && that.applitools.saveFailedTests); - - return request({ - qs: {apiKey: that.applitools.apiKey, updateBaseline: updateBaseline}, - url: that.self.host + '/api/sessions/running/' + that.self.sessionId, - method: 'DELETE', - headers: that.self.headers, - timeout: that.self.reqTimeout - }, cb); - }, - /** - * clear session, store result - */ - function(res, body) { - var cb = arguments[arguments.length - 1]; - - if(body) { - that.self.resultObject[that.currentArgs.name] = merge({ - id: that.self.sessionId, - url: that.self.url - }, JSON.parse(body)); - that.self.url = undefined; - that.self.sessionId = undefined; - that.self.isNew = undefined; - } - return cb(); - } - - ], function(err) { - return done(err); - }); + if (!that.self.defaultScreenDimension) return; + + return that.instance.windowHandleSize({ + width: that.self.defaultScreenDimension.width, + height: that.self.defaultScreenDimension.height + }); + }) + /** + * If we have an applitools session open, close it + */ + .then(function closeApplitools() { + if (!that.self.usesApplitools) return; + + // Whether or not we should automatically save this test as baseline. + var updateBaseline = (that.self.isNew && that.applitools.saveNewTests) || + (!that.self.isNew && that.applitools.saveFailedTests); + + return request({ + qs: {apiKey: that.applitools.apiKey, updateBaseline: updateBaseline}, + url: that.self.host + '/api/sessions/running/' + that.self.sessionId, + method: 'DELETE', + headers: that.self.headers, + timeout: that.self.reqTimeout + }); + }) + .spread(function clearAndStore(res, body) { + if (!body) return; + + that.self.resultObject[that.currentArgs.name] = merge({ + id: that.self.sessionId, + url: that.self.url + }, JSON.parse(body)); + that.self.url = undefined; + that.self.sessionId = undefined; + that.self.isNew = undefined; + }).nodeify(done); }; diff --git a/lib/getPageInfo.js b/lib/getPageInfo.js index c7041e5..0062d42 100644 --- a/lib/getPageInfo.js +++ b/lib/getPageInfo.js @@ -5,8 +5,8 @@ * IMPORTANT: all of this code gets executed on browser side, so you won't have * access to node specific interfaces at all */ -var async = require('async'), - merge = require('deepmerge'); +var Promise = require('bluebird'); +var merge = require('deepmerge'); /** * little helper function to check against argument values @@ -18,173 +18,159 @@ function isNumber(variable) { } module.exports = function(done) { - var that = this, - response = { - excludeRect: [], - scrollPos: {x: 0, y:0}, - }, - excludeRect = [], - element = that.currentArgs.elem; - - async.waterfall([ - /** - * get page information - */ - function(cb) { - that.instance.execute(function() { - /** - * get current scroll position - * @return {Object} x and y coordinates of current scroll position - */ - var getScrollPosition = function() { - var x = 0, - y = 0; - - if (typeof window.pageYOffset === 'number') { + var that = this; + var response = { + excludeRect: [], + scrollPos: {x: 0, y:0}, + }; + var excludeRect = []; + var element = that.currentArgs.elem; + + return Promise.try(function() { + /*eslint-disable*/ + return that.instance.execute(function() { + /** + * get current scroll position + * @return {Object} x and y coordinates of current scroll position + */ + var getScrollPosition = function() { + var x = 0, + y = 0; - /* Netscape compliant */ - y = window.pageYOffset; - x = window.pageXOffset; + if (typeof window.pageYOffset === 'number') { - } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) { + /* Netscape compliant */ + y = window.pageYOffset; + x = window.pageXOffset; - /* DOM compliant */ - y = document.body.scrollTop; - x = document.body.scrollLeft; + } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) { - } else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) { + /* DOM compliant */ + y = document.body.scrollTop; + x = document.body.scrollLeft; - /* IE6 standards compliant mode */ - y = document.documentElement.scrollTop; - x = document.documentElement.scrollLeft; + } else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) { - } + /* IE6 standards compliant mode */ + y = document.documentElement.scrollTop; + x = document.documentElement.scrollLeft; - return { - x: x, - y: y - }; - }; + } return { - title: document.title, - scrollPos: getScrollPosition(), - screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), - screenHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) + x: x, + y: y }; - }) - .then(function (pageInfo) { - cb(null, pageInfo); - }); - }, + }; + + return { + title: document.title, + scrollPos: getScrollPosition(), + screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), + screenHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) + }; + }); + /*eslint-enable*/ + }) + .then(function getElementInformation(res) { + response = merge(response, res.value); + + if(!element) { + return Promise.resolve([{}, {}]); + } + + /** + * needs to get defined that verbose to make it working in IE driver + */ + that.instance.selectorExecute(element, function(elem) { + var boundingRect = elem[0].getBoundingClientRect(); + return { + elemBounding: { + width: boundingRect.width ? boundingRect.width : boundingRect.right - boundingRect.left, + height: boundingRect.height ? boundingRect.height : boundingRect.bottom - boundingRect.top, + top: boundingRect.top, + right: boundingRect.right, + bottom: boundingRect.bottom, + left: boundingRect.left + } + }; + }) + .then(function (elementInfo) { + // ! TODO check response and its schema being correct here + return [elementInfo, response]; + }); + }) + /** + * get information about exclude elements + */ + .then(function getExcludeInfo(res, responses, done) { + response = merge(response, res); /** - * get element information + * concatenate exclude elements to one dimensional array + * excludeElements = elements queried by specific selector strategy (typeof string) + * excludeCoords = x & y coords to exclude custom areas */ - function(res, cb) { - response = merge(response, res.value); + var excludeElements = []; - if(!element) { - return cb(null, {}, {}); + if (!that.currentArgs.exclude) { + return done(null, []); + } else if (!(that.currentArgs.exclude instanceof Array)) { + that.currentArgs.exclude = [that.currentArgs.exclude]; + } + + that.currentArgs.exclude.forEach(function(excludeElement) { + if (typeof excludeElement === 'string') { + excludeElements.push(excludeElement); + } else { + /** + * excludeCoords are a set of x,y rectangle + * then just check if the first 4 coords are numbers (minumum to span a rectangle) + */ + if (isNumber(excludeElement.x0) && isNumber(excludeElement.x1) && + isNumber(excludeElement.y0) && isNumber(excludeElement.y1)) { + response.excludeRect.push(excludeElement); + } } + }); - /** - * needs to get defined that verbose to make it working in IE driver - */ - that.instance.selectorExecute(element, function(elem) { - var boundingRect = elem[0].getBoundingClientRect(); - return { - elemBounding: { - width: boundingRect.width ? boundingRect.width : boundingRect.right - boundingRect.left, - height: boundingRect.height ? boundingRect.height : boundingRect.bottom - boundingRect.top, - top: boundingRect.top, - right: boundingRect.right, - bottom: boundingRect.bottom, - left: boundingRect.left - } - }; - }) - .then(function (elementInfo) { - // ! TODO check response and its schema being correct here - cb(null, elementInfo, response); - }); - }, + // Bail if no excludes + if(excludeElements.length === 0) { + return []; + } - /** - * get information about exclude elements - */ - function(res, responses, done) { - response = merge(response, res); + return that.instance.selectorExecute(excludeElements, function() { /** - * concatenate exclude elements to one dimensional array - * excludeElements = elements queried by specific selector strategy (typeof string) - * excludeCoords = x & y coords to exclude custom areas + * excludeElements are elements queried by specific selenium strategy */ - var excludeElements = []; + var excludeElements = Array.prototype.slice.call(arguments), + excludeRect = []; - if (!that.currentArgs.exclude) { - return done(null, []); - } else if (!(that.currentArgs.exclude instanceof Array)) { - that.currentArgs.exclude = [that.currentArgs.exclude]; - } + excludeElements.forEach(function(elements) { - that.currentArgs.exclude.forEach(function(excludeElement) { - if (typeof excludeElement === 'string') { - excludeElements.push(excludeElement); - } else { - /** - * excludeCoords are a set of x,y rectangle - * then just check if the first 4 coords are numbers (minumum to span a rectangle) - */ - if (isNumber(excludeElement.x0) && isNumber(excludeElement.x1) && isNumber(excludeElement.y0) && isNumber(excludeElement.y1)) { - response.excludeRect.push(excludeElement); - } + if(!elements) { + return; } - }); - if(excludeElements.length === 0) { - return done(null, []); - } - - that.instance.selectorExecute(excludeElements, function() { - - /** - * excludeElements are elements queried by specific selenium strategy - */ - var excludeElements = Array.prototype.slice.call(arguments), - excludeRect = []; - - excludeElements.forEach(function(elements) { - - if(!elements) { - return; - } - - elements.forEach(function(elem) { - var elemRect = elem.getBoundingClientRect(); - excludeRect.push({ - x0: elemRect.left, - y0: elemRect.top, - x1: elemRect.right, - y1: elemRect.bottom - }); + elements.forEach(function(elem) { + var elemRect = elem.getBoundingClientRect(); + excludeRect.push({ + x0: elemRect.left, + y0: elemRect.top, + x1: elemRect.right, + y1: elemRect.bottom }); }); - - return excludeRect; - - }) - .then(function (excludeRect) { - done(null, excludeRect); }); - } - ], function(err, excludeElements) { + return excludeRect; + + }); + }) + .then(function(excludeElements) { if(excludeElements && excludeElements.length) { response.excludeRect = excludeRect.concat(excludeElements); } - - done(err, response); - }); + }).nodeify(done); }; diff --git a/lib/makeScreenshot.js b/lib/makeScreenshot.js index 5fb2305..3eaae25 100644 --- a/lib/makeScreenshot.js +++ b/lib/makeScreenshot.js @@ -1,6 +1,6 @@ 'use strict'; -var q = require('q'); +var Promise = require('bluebird'); /** * make screenshot via [GET] /session/:sessionId/screenshot @@ -42,6 +42,7 @@ module.exports = function(done) { */ var hiddenElements = [], removeElements = []; + this.queuedShots.forEach(function(args) { if(typeof args.hide === 'string') { hiddenElements.push(args.hide); @@ -60,28 +61,25 @@ module.exports = function(done) { /** * hide / remove elements */ - q.all([ + return Promise.all([ modifyElements.call(that, hiddenElements, 'visibility', 'hidden'), modifyElements.call(that, removeElements, 'display', 'none') ]) - .then(function () { - /** - * take 100ms pause to give browser time for rendering - */ - return that.instance.pause(100).saveDocumentScreenshot(that.screenshot); - }) - .then(function () { - /** - * make hidden elements visible again - */ - return q.all([ - modifyElements.call(that, hiddenElements, 'visibility', ''), - modifyElements.call(that, removeElements, 'display', '') - ]); - }) - .then(function () { - done(); - }) - .done(); + .then(function () { + /** + * take 100ms pause to give browser time for rendering + */ + return that.instance.pause(100).saveDocumentScreenshot(that.screenshot); + }) + .then(function () { + /** + * make hidden elements visible again + */ + return Promise.all([ + modifyElements.call(that, hiddenElements, 'visibility', ''), + modifyElements.call(that, removeElements, 'display', '') + ]); + }) + .nodeify(done); }; diff --git a/lib/renameFiles.js b/lib/renameFiles.js index d3feb48..41c98b6 100644 --- a/lib/renameFiles.js +++ b/lib/renameFiles.js @@ -1,12 +1,15 @@ 'use strict'; -var glob = require('glob'), - fs = require('fs'); +var Promise = require('bluebird'); +var glob = Promise.promisify(require('glob')); +var fs = require('fs'); module.exports = function() { var done = arguments[arguments.length - 1]; + var that = this; - glob('{' + this.regressionPath + ',' + this.baselinePath + '}', {}, function(err,files) { + return glob('{' + this.regressionPath + ',' + this.baselinePath + '}', {}) + .then(function(files) { /** * if no files were found continue @@ -15,17 +18,17 @@ module.exports = function() { return done(); } - this.isComparable = true; - this.filename = this.regressionPath; + that.isComparable = true; + that.filename = that.regressionPath; /** * rename existing files */ - if(files.length === 2 && this.updateBaseline && !this.self.usesApplitools) { - return fs.rename(this.regressionPath, this.baselinePath, done); + if(files.length === 2 && that.updateBaseline && !that.self.usesApplitools) { + return fs.rename(that.regressionPath, that.baselinePath, done); } else { return done(); } - }.bind(this)); + }).nodeify(done); }; diff --git a/lib/saveImageDiff.js b/lib/saveImageDiff.js index f94ce97..7732cab 100644 --- a/lib/saveImageDiff.js +++ b/lib/saveImageDiff.js @@ -1,105 +1,102 @@ 'use strict'; -var fs = require('fs'), - async = require('async'), - logWarning = require('./logWarning.js'); +var Promise = require('bluebird'); +var fs = Promise.promisifyAll(require('fs')); +var logWarning = require('./logWarning.js'); module.exports = function(imageDiff,done) { - var that = this, - misMatchTolerance = parseFloat(imageDiff.misMatchPercentage,10); - - if(typeof imageDiff === 'function') { - this.self.resultObject[this.currentArgs.name].push({ - baselinePath: this.baselinePath, - message: 'first image of module "' + this.currentArgs.name + '" from page "' + this.pagename + '" successfully taken', - misMatchPercentage: 0, - isExactSameImage: true, - isSameDimensions: true, - isWithinMisMatchTolerance: true, - properties: this.currentArgs - }); + var that = this; + var misMatchTolerance = parseFloat(imageDiff.misMatchPercentage,10); + + return Promise.try(function() { + if(typeof imageDiff === 'function') { + this.self.resultObject[this.currentArgs.name].push({ + baselinePath: this.baselinePath, + message: 'first image of module "' + this.currentArgs.name + '" from page "' + this.pagename + '" successfully taken', + misMatchPercentage: 0, + isExactSameImage: true, + isSameDimensions: true, + isWithinMisMatchTolerance: true, + properties: this.currentArgs + }); - return imageDiff(); - } + return imageDiff(); + } - /** - * if set misMatchTolerance is smaller then compared misMatchTolerance - * make image diff - */ - if(this.misMatchTolerance < misMatchTolerance) { + /** + * if set misMatchTolerance is smaller then compared misMatchTolerance + * make image diff + */ + if(this.misMatchTolerance < misMatchTolerance) { - /*istanbul ignore next*/ - if(!imageDiff.isSameDimensions) { - logWarning.call(this.instance, 'DimensionWarning'); - } + /*istanbul ignore next*/ + if(!imageDiff.isSameDimensions) { + logWarning.call(this.instance, 'DimensionWarning'); + } - this.self.resultObject[this.currentArgs.name].push({ - baselinePath: this.baselinePath, - regressionPath: this.regressionPath, - diffPath: this.diffPath, - message: 'mismatch tolerance exceeded (+' + (misMatchTolerance - this.misMatchTolerance) + '), image-diff created', - misMatchPercentage: misMatchTolerance, - isExactSameImage: false, - isSameDimensions: imageDiff.isSameDimensions, - isWithinMisMatchTolerance: false, - properties: this.currentArgs - }); + this.self.resultObject[this.currentArgs.name].push({ + baselinePath: this.baselinePath, + regressionPath: this.regressionPath, + diffPath: this.diffPath, + message: 'mismatch tolerance exceeded (+' + (misMatchTolerance - this.misMatchTolerance) + '), image-diff created', + misMatchPercentage: misMatchTolerance, + isExactSameImage: false, + isSameDimensions: imageDiff.isSameDimensions, + isWithinMisMatchTolerance: false, + properties: this.currentArgs + }); - imageDiff.getDiffImage().pack() - .on('end', done.bind(null, null, this.resultObject)) - .pipe(fs.createWriteStream(this.diffPath)); + imageDiff.getDiffImage().pack() + .on('end', done.bind(null, null, this.resultObject)) + .pipe(fs.createWriteStream(this.diffPath)); - } else { + } - /** - * otherwise delete diff - */ + /** + * otherwise delete diff + */ + else { - async.waterfall([ /** * check if diff shot exists */ - function(done) { - fs.exists(that.diffPath,done.bind(null,null)); - }, + return Promise.try(function checkDiffShot() { + return fs.existsAsync(that.diffPath); + }) /** * remove diff if yes */ - function(exists,done) { - if(exists) { - fs.unlink(that.diffPath,done); - } else { - done(); - } - }, + .then(function unlinkIfExists(exists) { + if (exists) return fs.unlinkAsync(that.diffPath); + }) /** * Save a new baseline image, if one doesn't already exist. * * If one does exist, we delete the temporary regression. */ - function(done) { - fs.exists(that.baselinePath, function(exists) { - return !!exists ? fs.unlink(that.regressionPath, done) : fs.rename(that.regressionPath, that.baselinePath, done); + .then(function saveNewBaseline() { + return fs.existsAsync(that.baselinePath) + .then(function(exists) { + return exists ? + fs.unlinkAsync(that.regressionPath) : + fs.renameAsync(that.regressionPath, that.baselinePath); + }); + }) + .then(function setResults() { + /** + * return result object to WebdriverIO instance + */ + that.self.resultObject[that.currentArgs.name].push({ + baselinePath: that.baselinePath, + message: 'mismatch tolerance not exceeded (~' + misMatchTolerance + '), baseline didn\'t change', + misMatchPercentage: misMatchTolerance, + isExactSameImage: misMatchTolerance === 0, + isSameDimensions: imageDiff.isSameDimensions, + isWithinMisMatchTolerance: true }); - } - ], function(err) { - - /** - * return result object to WebdriverIO instance - */ - that.self.resultObject[that.currentArgs.name].push({ - baselinePath: that.baselinePath, - message: 'mismatch tolerance not exceeded (~' + misMatchTolerance + '), baseline didn\'t change', - misMatchPercentage: misMatchTolerance, - isExactSameImage: misMatchTolerance === 0, - isSameDimensions: imageDiff.isSameDimensions, - isWithinMisMatchTolerance: true }); + } + }).nodeify(done); - done(err); - - }); - - } }; diff --git a/lib/setScreenWidth.js b/lib/setScreenWidth.js index dd6f7d7..72234b1 100644 --- a/lib/setScreenWidth.js +++ b/lib/setScreenWidth.js @@ -4,81 +4,70 @@ * if multiple screen width are given resize browser dimension */ -var async = require('async'), - takenScreenSizes = {}; +var takenScreenSizes = {}; +var Promise = require('bluebird'); module.exports = function(done) { var that = this; this.newScreenSize = {}; - async.waterfall([ + return Promise.try(function getResolution() { /** * get current browser resolution to change back to it * after all shots were taken (only if a screenWidth is set) */ - function(cb) { - if(!that.self.defaultScreenDimension && that.screenWidth && that.screenWidth.length) { - that.instance.windowHandleSize() - .then(function(res) { - that.self.defaultScreenDimension = res.value; - cb(); - }); - } else { - cb(); - } - }, - function(cb) { - - if(!that.screenWidth || that.screenWidth.length === 0) { - - /** - * if no screenWidth option was set just continue - */ - return cb(); - - } - - that.newScreenSize.width = parseInt(that.screenWidth.shift(), 10); - that.newScreenSize.height = parseInt(that.self.defaultScreenDimension.height, 10); + if(!that.self.defaultScreenDimension && that.screenWidth && that.screenWidth.length) { + return that.instance.windowHandleSize() + .then(function(res) { + that.self.defaultScreenDimension = res.value; + }); + } + }) + .then(function() { + if(!that.screenWidth || that.screenWidth.length === 0) { + /** + * if no screenWidth option was set just continue + */ + return; + } - that.self.takeScreenshot = false; - if(!takenScreenSizes[that.pagename] || takenScreenSizes[that.pagename].indexOf(that.newScreenSize.width) < 0) { - /** - * set flag to retake screenshot - */ - that.self.takeScreenshot = true; + that.newScreenSize.width = parseInt(that.screenWidth.shift(), 10); + that.newScreenSize.height = parseInt(that.self.defaultScreenDimension.height, 10); - /** - * cache already taken screenshot / screenWidth combinations - */ - if(!takenScreenSizes[that.pagename]) { - takenScreenSizes[that.pagename] = [that.newScreenSize.width]; - } else { - takenScreenSizes[that.pagename].push(that.newScreenSize.width); - } - } + that.self.takeScreenshot = false; + if(!takenScreenSizes[that.pagename] || takenScreenSizes[that.pagename].indexOf(that.newScreenSize.width) < 0) { + /** + * set flag to retake screenshot + */ + that.self.takeScreenshot = true; /** - * resize browser resolution + * cache already taken screenshot / screenWidth combinations */ - that.instance.call(function() { + if(!takenScreenSizes[that.pagename]) { + takenScreenSizes[that.pagename] = [that.newScreenSize.width]; + } else { + takenScreenSizes[that.pagename].push(that.newScreenSize.width); + } + } + + /** + * resize browser resolution + */ - /** - * if shot will be taken in a specific screenWidth, rename file and append screen width - * value in filename - */ - that.baselinePath = that.baselinePath.replace(/\.(baseline|regression|diff)\.png/,'.' + that.newScreenSize.width + 'px.$1.png'); - that.regressionPath = that.regressionPath.replace(/\.(baseline|regression|diff)\.png/,'.' + that.newScreenSize.width + 'px.$1.png'); - that.diffPath = that.diffPath.replace(/\.(baseline|regression|diff)\.png/, '.' + that.newScreenSize.width + 'px.$1.png'); - that.screenshot = that.screenshot.replace(/\.png/, '.' + that.newScreenSize.width + 'px.png'); - that.filename = that.baselinePath; + /** + * if shot will be taken in a specific screenWidth, rename file and append screen width + * value in filename + */ + that.baselinePath = that.baselinePath.replace(/\.(baseline|regression|diff)\.png/,'.' + that.newScreenSize.width + 'px.$1.png'); + that.regressionPath = that.regressionPath.replace(/\.(baseline|regression|diff)\.png/,'.' + that.newScreenSize.width + 'px.$1.png'); + that.diffPath = that.diffPath.replace(/\.(baseline|regression|diff)\.png/, '.' + that.newScreenSize.width + 'px.$1.png'); + that.screenshot = that.screenshot.replace(/\.png/, '.' + that.newScreenSize.width + 'px.png'); + that.filename = that.baselinePath; - that.instance.setViewportSize({width: that.newScreenSize.width, height: that.newScreenSize.height}) - .pause(100) - .call(cb); + return that.instance.setViewportSize({width: that.newScreenSize.width, height: that.newScreenSize.height}) + .pause(100); - }); - } - ], done); + }).nodeify(done); }; diff --git a/lib/startSession.js b/lib/startSession.js index 1aba41d..3a9ba46 100644 --- a/lib/startSession.js +++ b/lib/startSession.js @@ -1,86 +1,75 @@ 'use strict'; -var pkg = require('../package.json'), - request = require('request'), - async = require('async'), - WebdriverIO = require('webdriverio'); +var Promise = require('bluebird'); +var pkg = require('../package.json'); +var request = Promise.promisify(require('request'), {multiArg: true}); +var WebdriverIO = require('webdriverio'); module.exports = function() { - var that = this, - done = arguments[arguments.length - 1]; + var ctx = this; + var done = arguments[arguments.length - 1]; /** * skip when not using applitools */ if(!this.self.usesApplitools || this.self.sessionId) { - return done(); + return Promise.resolve().nodeify(done); } - async.waterfall([ - - /** - * get meta information of current session - */ - function(cb) { - that.instance.execute(function() { - return { - useragent: navigator.userAgent, - screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), - documentHeight: document.documentElement.scrollHeight - }; - }) - .then(function(res) { - that.useragent = res.value.useragent; - that.displaySize = { - width: that.screenWidth && that.screenWidth.length ? that.screenWidth[0] : res.value.screenWidth, - height: res.value.documentHeight - }; - - return cb(); - }); - }, - - /** - * initialise applitools session - */ - function(cb) { - request({ - url: that.self.host + '/api/sessions/running', - qs: {apiKey: that.applitools.apiKey}, - method: 'POST', - json: { - 'startInfo': { - 'appIdOrName': that.applitools.appName, - 'scenarioIdOrName': that.currentArgs.name, - 'batchInfo': { - 'id': that.applitools.batchId, - 'name': that.pagename, - 'startedAt': new Date().toISOString() - }, - 'environment': { - 'displaySize': that.displaySize, - 'inferred': 'useragent:' + that.useragent - }, - 'matchLevel': 'Strict', - 'agentId': pkg.name + '/' + pkg.version - } - }, - headers: that.self.headers, - timeout: that.self.reqTimeout - }, cb); - - } - ], function(err, res, body) { - - if (err || res.statusCode !== 200 && res.statusCode !== 201) { - return done(new WebdriverIO.ErrorHandler.CommandError('Couldn\'t start applitools session')); - } - - that.self.sessionId = body.id; - that.self.url = body.url; - that.self.isNew = res.statusCode === 201; - return done(); - - }); + /** + * get meta information of current session + */ + return Promise.try(function() { + /*eslint-disable*/ + ctx.instance.execute(function() { + return { + useragent: navigator.userAgent, + screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), + documentHeight: document.documentElement.scrollHeight + }; + }) + /*eslint-enable*/ + .then(function(res) { + ctx.useragent = res.value.useragent; + ctx.displaySize = { + width: ctx.screenWidth && ctx.screenWidth.length ? ctx.screenWidth[0] : res.value.screenWidth, + height: res.value.documentHeight + }; + }); + }) + .then(function initializeApplitools(){ + return request({ + url: ctx.self.host + '/api/sessions/running', + qs: {apiKey: ctx.applitools.apiKey}, + method: 'POST', + json: { + startInfo: { + appIdOrName: ctx.applitools.appName, + scenarioIdOrName: ctx.currentArgs.name, + batchInfo: { + id: ctx.applitools.batchId, + name: ctx.pagename, + startedAt: new Date().toISOString() + }, + environment: { + displaySize: ctx.displaySize, + inferred: 'useragent:' + ctx.useragent + }, + matchLevel: 'Strict', + agentId: pkg.name + '/' + pkg.version + } + }, + headers: ctx.self.headers, + timeout: ctx.self.reqTimeout + }) + .spread(function(res, body) { + ctx.self.sessionId = body.id; + ctx.self.url = body.url; + ctx.self.isNew = res.statusCode === 201; + }) + .catch(function() { + throw new WebdriverIO.ErrorHandler.CommandError('Couldn\'t start applitools session'); + }); + }).nodeify(done); }; diff --git a/lib/syncImages.js b/lib/syncImages.js index 3595177..6ba9023 100644 --- a/lib/syncImages.js +++ b/lib/syncImages.js @@ -1,12 +1,15 @@ 'use strict'; -var fs = require('fs-extra'), - tar = require('tar'), - zlib = require('zlib'), - targz = require('tar.gz'), - rimraf = require('rimraf'), - request = require('request'), - q = require('q'); +var tar = require('tar'); +var zlib = require('zlib'); +var Promise = require('bluebird'); +var targz = Promise.promisify(require('tar.gz')); +Promise.promisifyAll(targz.prototype); +var fs = Promise.promisifyAll(require('fs-extra')); +var request = Promise.promisifyAll(require('request'), {multiArg: true}); +var rimraf = Promise.promisify(require('rimraf')); +var streamToPromise = require('./util/streamToPromise'); +var assign = require('object-assign'); /** * sync down @@ -14,50 +17,52 @@ var fs = require('fs-extra'), * * @param {Function} done callback to be called after sync finishes */ -var syncDown = function(done) { +var syncDown = function(ctx) { var args = { - url: this.api + (this.api.substr(-1) !== '/' ? '/' : '') + this.screenshotRoot + '.tar.gz', + url: ctx.api + (ctx.api.substr(-1) !== '/' ? '/' : '') + ctx.screenshotRoot + '.tar.gz', headers: { 'accept-encoding': 'gzip,deflate' }, }; - if(typeof this.user === 'string' && typeof this.key === 'string') { + if(typeof ctx.user === 'string' && typeof ctx.key === 'string') { args.auth = { - user: this.user, - pass: this.key + user: ctx.user, + pass: ctx.key }; } - var r = request.get(args), - self = this; - - r.on('error', done); - r.on('response', function(resp) { - + return request.getAsync(args) + .then(function(resp) { /*! * no error if repository doesn't exists */ /*istanbul ignore if*/ if(resp.statusCode === 404) { - return done(); + return; } /*istanbul ignore next*/ if(resp.statusCode !== 200 || resp.headers['content-type'] !== 'application/octet-stream') { - return done(new Error('unexpected statusCode (' + resp.statusCode + ' != 200) or content-type (' + resp.headers['content-type'] + ' != application/octet-stream)')); + throw new Error('unexpected statusCode (' + resp.statusCode + ' != 200) or content-type (' + + resp.headers['content-type'] + ' != application/octet-stream)'); } /** * check if repository directory already exists and * clear it if yes */ - if(fs.existsSync(self.screenshotRoot)) { - rimraf.sync(self.screenshotRoot); - fs.mkdirsSync(self.screenshotRoot, '0755', true); - } - - resp.pipe(zlib.Gunzip()).pipe(tar.Extract({ path: '.' })).on('end', done); - + return fs.existsAsync(ctx.screenshotRoot) + .then(function(exists) { + if (exists) { + return rimraf(ctx.screenshotRoot) + .then(function() { + fs.mkdirsAsync(ctx.screenshotRoot, '0755', true); + }); + } + }) + .then(function() { + return streamToPromise(resp.pipe(zlib.Gunzip()).pipe(tar.Extract({ path: '.' }))); + }); }); }; @@ -67,33 +72,24 @@ var syncDown = function(done) { * * @param {Function} done callback to be called after tarball was uploaded */ -var syncUp = function(done) { - var screenshotRoot = this.screenshotRoot, - args = { url: this.api }, +var syncUp = function(ctx) { + var screenshotRoot = ctx.screenshotRoot, + args = { url: ctx.api }, tarballPath = screenshotRoot + '.tar.gz'; - if(typeof this.user === 'string' && typeof this.key === 'string') { + if(typeof ctx.user === 'string' && typeof ctx.key === 'string') { args.auth = { - user: this.user, - pass: this.key + user: ctx.user, + pass: ctx.key }; } - new targz().compress(screenshotRoot, tarballPath, function(err){ - - /*istanbul ignore if*/ - if(err) { - return done(new Error(err)); - } - - var r = request.post(args, function () { - rimraf.sync(tarballPath); - done(); + return new targz().compressAsync(screenshotRoot, tarballPath) + .then(function() { + return request.postAsync(assign({}, args, {formData: {gz: fs.createReadStream(tarballPath)}})) + .then(function() { + return rimraf(tarballPath); }); - - var form = r.form(); - form.append('gz', fs.createReadStream(tarballPath)); - }); }; @@ -106,24 +102,15 @@ var syncUp = function(done) { */ module.exports = function(done) { - var defer = q.defer(); - - if(!this.api) { - return done(new Error('No sync options specified! Please provide an api path and user/key (optional).')); - } - - var sync = this.needToSync ? syncUp : syncDown; - this.needToSync = false; - - sync.call(this, function(err, httpResponse, body) { - - /*istanbul ignore next*/ - if(err || (httpResponse && httpResponse.statusCode !== 200)) { - return defer.reject(new Error(err || body)); + return Promise.try(function() { + if(!this.api) { + throw new Error('No sync options specified! Please provide an api path and user/key (optional).'); } - defer.resolve(); + var sync = this.needToSync ? syncUp : syncDown; + this.needToSync = false; + + return sync(this); + }).nodeify(done); - }); - return defer.promise; }; diff --git a/lib/util/streamToPromise.js b/lib/util/streamToPromise.js new file mode 100644 index 0000000..0492484 --- /dev/null +++ b/lib/util/streamToPromise.js @@ -0,0 +1,8 @@ +var Promise = require('bluebird'); + +module.exports = function streamToPromise(stream) { + return new Promise(function(resolve, reject) { + stream.on("end", resolve); + stream.on("error", reject); + }); +}; diff --git a/lib/viewportScreenshot.js b/lib/viewportScreenshot.js index a10c593..114ee97 100644 --- a/lib/viewportScreenshot.js +++ b/lib/viewportScreenshot.js @@ -6,119 +6,80 @@ * @param {String} filename path of file to be saved */ -var async = require('async'), - gm = require('gm'), - q = require('q'); +var Promise = require('bluebird'); +var gm = require('gm'); +Promise.promisifyAll(gm.prototype); module.exports = function viewportScreenshot(fileName) { var ErrorHandler = this.instance.ErrorHandler; - var defer = q.defer(); /*! * parameter check */ if (typeof fileName !== 'string') { - defer.reject(new ErrorHandler.CommandError('number or type of arguments don\'t agree with saveScreenshot command')); - return defer.promise; + return Promise.reject( + new ErrorHandler.CommandError('number or type of arguments don\'t agree with saveScreenshot command')); } - var self = this.instance, - response = { - execute: [], - screenshot: [] - }; - - var getScrollingPosition = function() { - var position = [0, 0]; - if (typeof window.pageYOffset !== 'undefined') { - position = [ - window.pageXOffset, - window.pageYOffset - ]; - } else if (typeof document.documentElement.scrollTop !== 'undefined' && document.documentElement.scrollTop > 0) { - position = [ - document.documentElement.scrollLeft, - document.documentElement.scrollTop - ]; - } else if (typeof document.body.scrollTop !== 'undefined') { - position = [ - document.body.scrollLeft, - document.body.scrollTop - ]; - } - return position; + var self = this.instance; + var response = { + execute: [], + screenshot: [] }; - async.waterfall([ - - /*! - * get scroll position - */ - function(cb) { - self.execute(getScrollingPosition, null) - .then(function (res) { - cb(null, res); - }) - .catch(function (err) { - cb(err); - }); - }, - - /*! - * get viewport width/height and total width/height - */ - function(res, cb) { - response.execute.push(res); - - self.execute(function() { - return { - screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), - screenHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) - }; - }) - .then(function (res) { - cb(null, res); - }) - .catch(function (err) { - cb(err); - }); - }, - function(res, cb) { - response.execute.push(res); - self.screenshot() - .then(function (screenshotRes) { - cb(null, screenshotRes); - }) - .catch(function (err) { - cb(err); - }); - }, - function(res, cb) { - response.screenshot = res; - - gm(new Buffer(res.value, 'base64')).crop( - // width - response.execute[1].value.screenWidth, - // height - response.execute[1].value.screenHeight, - // top - response.execute[0].value[0], - // left - response.execute[0].value[1] - ).write(fileName, cb); - - } - ], function(err) { - - if (err) { - return defer.reject(err); - } - - defer.resolve(response); - + self.execute(getScrollingPosition, null) + .then(function getWidthHeight() { + /*eslint-disable*/ + return self.execute(function() { + return { + screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), + screenHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) + }; + }); + /*eslint-enable*/ + }) + .then(function(res) { + response.execute.push(res); + return self.screenshot(); + }) + .then(function(res) { + response.screenshot = res; + return gm(new Buffer(res.value, 'base64')) + .crop( + // width + response.execute[1].value.screenWidth, + // height + response.execute[1].value.screenHeight, + // top + response.execute[0].value[0], + // left + response.execute[0].value[1] + ) + .writeAsync(fileName); }); - return defer.promise; +}; +/*eslint-disable*/ +function getScrollingPosition() { + var position = [0, 0]; + if (typeof window.pageYOffset !== 'undefined') { + position = [ + window.pageXOffset, + window.pageYOffset + ]; + } else if (typeof document.documentElement.scrollTop !== 'undefined' && document.documentElement.scrollTop > 0) { + position = [ + document.documentElement.scrollLeft, + document.documentElement.scrollTop + ]; + } else if (typeof document.body.scrollTop !== 'undefined') { + position = [ + document.body.scrollLeft, + document.body.scrollTop + ]; + } + return position; }; +/*eslint-enable*/ diff --git a/lib/webdrivercss.js b/lib/webdrivercss.js index d2ccbcf..65b5075 100644 --- a/lib/webdrivercss.js +++ b/lib/webdrivercss.js @@ -71,7 +71,7 @@ var WebdriverCSS = function(webdriverInstance, options) { */ this.instance.addCommand('saveViewportScreenshot', viewportScreenshot.bind(this), true); this.instance.addCommand('saveDocumentScreenshot', documentScreenshot.bind(this), true); - this.instance.addCommand('webdrivercss', workflow.promise.bind(this), true); + this.instance.addCommand('webdrivercss', workflow.bind(this), true); this.instance.addCommand('sync', syncImages.bind(this), true); return this; diff --git a/lib/workflow.js b/lib/workflow.js index 13aa0f5..9f0ebf1 100644 --- a/lib/workflow.js +++ b/lib/workflow.js @@ -4,27 +4,18 @@ * run regression test */ -var async = require('async'); -var q = require('q'); +var startSession = require('./startSession.js'); +var setScreenWidth = require('./setScreenWidth.js'); +var makeScreenshot = require('./makeScreenshot.js'); +var renameFiles = require('./renameFiles.js'); +var getPageInfo = require('./getPageInfo.js'); +var cropImage = require('./cropImage.js'); +var compareImages = require('./compareImages.js'); +var saveImageDiff = require('./saveImageDiff.js'); +var asyncCallback = require('./asyncCallback.js'); -function wrappedWorkflow(pagename, args) { - var defer = q.defer(); - - function cb(err, result) { - defer.resolve(result); - } - - workflow.call(this, pagename, args, cb); - - return defer.promise; -} function workflow(pagename, args) { - /*! - * make sure that callback contains chainit callback - */ - var cb = arguments[arguments.length - 1]; - this.needToSync = true; /*istanbul ignore next*/ @@ -81,8 +72,7 @@ function workflow(pagename, args) { newScreenSize: 0, pageInfo: null, updateBaseline: (typeof currentArgs.updateBaseline === 'boolean') ? currentArgs.updateBaseline : this.updateBaseline, - screenWidth: currentArgs.screenWidth || [].concat(this.screenWidth), // create a copy of the origin default screenWidth - cb: cb + screenWidth: currentArgs.screenWidth || [].concat(this.screenWidth) // create a copy of the origin default screenWidth }; /** @@ -92,56 +82,31 @@ function workflow(pagename, args) { this.resultObject[currentArgs.name] = []; } - async.waterfall([ - - /** - * initialize session - */ - require('./startSession.js').bind(context), - - /** - * if multiple screen width are given resize browser dimension - */ - require('./setScreenWidth.js').bind(context), - - /** - * make screenshot via [GET] /session/:sessionId/screenshot - */ - require('./makeScreenshot.js').bind(context), - - /** - * check if files with id already exists - */ - require('./renameFiles.js').bind(context), - - /** - * get page informations - */ - require('./getPageInfo.js').bind(context), - - /** - * crop image according to user arguments and its position on screen and save it - */ - require('./cropImage.js').bind(context), - - /** - * compare images - */ - require('./compareImages.js').bind(context), - - /** - * save image diff - */ - require('./saveImageDiff.js').bind(context) - ], - /** - * run workflow again or execute callback function - */ - require('./asyncCallback.js').bind(context) - ); + return startSession.bind(context) + .then(function() { + return setScreenWidth.apply(context, arguments); + }) + .then(function() { + return makeScreenshot.apply(context, arguments); + }) + .then(function() { + return renameFiles.apply(context, arguments); + }) + .then(function() { + return getPageInfo.apply(context, arguments); + }) + .then(function() { + return cropImage.apply(context, arguments); + }) + .then(function() { + return compareImages.apply(context, arguments); + }) + .then(function() { + return saveImageDiff.apply(context, arguments); + }) + .then(function() { + return asyncCallback.apply(context, arguments); + }); } -module.exports = { - workflow: workflow, - promise: wrappedWorkflow -}; \ No newline at end of file +module.exports = workflow; diff --git a/package.json b/package.json index f0a097b..b02449a 100644 --- a/package.json +++ b/package.json @@ -16,15 +16,17 @@ }, "dependencies": { "async": "^0.9.0", + "bluebird": "^3.1.1", "deepmerge": "^0.2.7", + "fs-extra": "^0.18.2", "glob": "^5.0.5", "gm": "^1.17.0", "node-resemble-js": "0.0.4", + "object-assign": "^4.0.1", "q": "^1.4.1", "request": "^2.55.0", "rimraf": "^2.3.2", "tar": "^2.1.0", - "fs-extra": "^0.18.2", "tar.gz": "^1.0.1" }, "devDependencies": { From d82dda6e4638dcf3e3b1d031e16d954877278592 Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Wed, 13 Jan 2016 20:17:17 -0600 Subject: [PATCH 6/7] Full bluebird conversion. --- .eslintrc | 27 ++++++ lib/asyncCallback.js | 6 +- lib/compareImages.js | 21 ++--- lib/cropImage.js | 27 +++--- lib/documentScreenshot.js | 15 ++- lib/endSession.js | 50 +++++----- lib/getPageInfo.js | 188 +++++++++++++++++++------------------- lib/makeScreenshot.js | 52 +++++------ lib/renameFiles.js | 20 ++-- lib/saveImageDiff.js | 71 +++++++------- lib/setScreenWidth.js | 10 +- lib/startSession.js | 108 +++++++++++----------- lib/syncImages.js | 113 ++++++++++++----------- lib/viewportScreenshot.js | 17 ++-- lib/webdrivercss.js | 12 +-- lib/workflow.js | 40 +++----- package.json | 3 +- 17 files changed, 392 insertions(+), 388 deletions(-) create mode 100644 .eslintrc diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..17740dc --- /dev/null +++ b/.eslintrc @@ -0,0 +1,27 @@ +{ + // "parser": "babel-eslint", + "root": true, + "extends": "eslint:recommended", + "rules": { + "strict": 0, + "quotes": [0, "single"], + "comma-dangle": 0, + "curly": [1, "multi-line"], + "no-console": 0, + "no-use-before-define": [1, "nofunc"], + "no-unused-vars": [1, { + "vars": "local", + "varsIgnorePattern": "^_" + }], + "no-underscore-dangle": 0, + "new-cap": 0, + "consistent-return": 0, + "camelcase": 0, + "dot-notation": 0, + "semi": [1, "always"], + }, + env: { + // "es6": true, + "node": true + } +} diff --git a/lib/asyncCallback.js b/lib/asyncCallback.js index 9d3e841..091830f 100644 --- a/lib/asyncCallback.js +++ b/lib/asyncCallback.js @@ -4,11 +4,13 @@ * run workflow again or execute callback function */ -var workflow = require('./workflow.js'); var endSession = require('./endSession.js'); var Promise = require('bluebird'); module.exports = function(err) { + var workflow = require('./workflow'); + var ctx = this; + /** * if error occured don't do another shot (if multiple screen width are set) */ @@ -16,8 +18,6 @@ module.exports = function(err) { if (err) return Promise.reject(err); return Promise.try(function() { - var ctx = this; - /** * on multiple screenWidth or multiple page elements * repeat workflow diff --git a/lib/compareImages.js b/lib/compareImages.js index 62d1450..7e34447 100644 --- a/lib/compareImages.js +++ b/lib/compareImages.js @@ -5,31 +5,29 @@ */ var resemble = require('node-resemble-js'); +var Promise = require('bluebird'); module.exports = function() { - /** - * need to find done function because gm doesn't have node like callbacks (err,res) - */ - var done = arguments[arguments.length - 1]; + var ctx = this; return Promise.try(function() { /** * if there is no need for image comparison or no images gets saved on fs, just continue */ - if(!this.isComparable || !this.self.saveImages) { + if(!ctx.isComparable || !ctx.self.saveImages) { return; } /** * compare images */ - var diff = resemble(this.baselinePath).compareTo(this.regressionPath); + var diff = resemble(ctx.baselinePath).compareTo(ctx.regressionPath); /** * map 'ignore' configuration to resemble options */ - var ignore = this.currentArgs.ignore || ""; + var ignore = ctx.currentArgs.ignore || ""; if (ignore.indexOf("color") === 0) { diff.ignoreColors(); } else if (ignore.indexOf("antialias") === 0) { @@ -39,9 +37,10 @@ module.exports = function() { /** * execute the comparison */ - return Promise.fromCallback(function(cb) { - diff.onComplete(cb.bind(null,null)); + return new Promise(function(resolve, reject) { + diff.onComplete(function(res) { // this callback has bad arity + resolve(res); + }); }); - }) - .nodeify(done); + }); }; diff --git a/lib/cropImage.js b/lib/cropImage.js index ad79dd2..e2e3d3d 100644 --- a/lib/cropImage.js +++ b/lib/cropImage.js @@ -10,19 +10,19 @@ Promise.promisifyAll(gm.prototype); var request = Promise.promisify(require('request'), {multiArg: true}); var exclude = require('./exclude.js'); -module.exports = function(res, done) { +module.exports = function(res) { var ctx = this; var excludeRect = res.excludeRect; - var shot = gm(this.screenshot).quality(100); + var shot = gm(ctx.screenshot).quality(100); var cropDim; return Promise.try(function() { - var x = parseInt(this.currentArgs.x, 10); - var y = parseInt(this.currentArgs.y, 10); - var width = parseInt(this.currentArgs.width, 10); - var height = parseInt(this.currentArgs.height, 10); + var x = parseInt(ctx.currentArgs.x, 10); + var y = parseInt(ctx.currentArgs.y, 10); + var width = parseInt(ctx.currentArgs.width, 10); + var height = parseInt(ctx.currentArgs.height, 10); if (!isNaN(x) && !isNaN(y) && !isNaN(width) && !isNaN(height)) { @@ -59,12 +59,13 @@ module.exports = function(res, done) { } }) .then(function() { - if (!ctx.self.saveImages) return; - - /** - * save image to fs - */ - return shot.writeAsync(ctx.filename || ctx.baselinePath) + return Promise.try(function() { + if (!ctx.self.saveImages) return; + /** + * save image to fs + */ + return shot.writeAsync(ctx.filename || ctx.baselinePath); + }) .then(function() { /** * generate image buffer @@ -92,5 +93,5 @@ module.exports = function(res, done) { } }); }); - }).nodeify(done); + }); }; diff --git a/lib/documentScreenshot.js b/lib/documentScreenshot.js index a00028b..dc337e3 100644 --- a/lib/documentScreenshot.js +++ b/lib/documentScreenshot.js @@ -32,7 +32,7 @@ var rimraf = Promise.promisify(require('rimraf')); var generateUUID = require('./generateUUID.js'); var path = require('path'); -module.exports = function documentScreenshot(fileName, cb) { +module.exports = function documentScreenshot(fileName) { var ErrorHandler = this.instance.ErrorHandler; @@ -83,7 +83,7 @@ module.exports = function documentScreenshot(fileName, cb) { function takeScreenshot() { // Take screenshot - return self.screenshot.bind(self) + return self.screenshot.call(self) // Cache image into tmp dir .then(function cacheImage(res) { var file = tmpDir + '/' + currentXPos + '-' + currentYPos + '.png'; @@ -162,11 +162,9 @@ module.exports = function documentScreenshot(fileName, cb) { * crop screenshot regarding page size */ .then(function cropScreenshot() { - return Promise.fromCallback(function(cb) { - gm(fileName) - .crop(response.execute[0].value.documentWidth, response.execute[0].value.documentHeight, 0, 0) - .write(fileName, cb); - }); + return gm(fileName) + .crop(response.execute[0].value.documentWidth, response.execute[0].value.documentHeight, 0, 0) + .writeAsync(fileName); }) /*! * remove tmp dir @@ -181,8 +179,7 @@ module.exports = function documentScreenshot(fileName, cb) { return self.execute(scrollFn, 0, 0); }) // Return response object. - .return(response) - .nodeify(cb); + .return(response); }; /*eslint-disable*/ diff --git a/lib/endSession.js b/lib/endSession.js index 41ab818..9f55f00 100644 --- a/lib/endSession.js +++ b/lib/endSession.js @@ -4,48 +4,48 @@ var merge = require('deepmerge'); var Promise = require('bluebird'); var request = Promise.promisify(require('request'), {multiArgs: true}); -module.exports = function(done) { +module.exports = function() { - var that = this; + var ctx = this; return Promise.try(function setOriginalResolution() { /** * if screenwidth was set, get back to old resolution */ - if (!that.self.defaultScreenDimension) return; + if (!ctx.self.defaultScreenDimension) return; - return that.instance.windowHandleSize({ - width: that.self.defaultScreenDimension.width, - height: that.self.defaultScreenDimension.height + return ctx.instance.windowHandleSize({ + width: ctx.self.defaultScreenDimension.width, + height: ctx.self.defaultScreenDimension.height }); }) /** * If we have an applitools session open, close it */ .then(function closeApplitools() { - if (!that.self.usesApplitools) return; + if (!ctx.self.usesApplitools) return; // Whether or not we should automatically save this test as baseline. - var updateBaseline = (that.self.isNew && that.applitools.saveNewTests) || - (!that.self.isNew && that.applitools.saveFailedTests); + var updateBaseline = (ctx.self.isNew && ctx.applitools.saveNewTests) || + (!ctx.self.isNew && ctx.applitools.saveFailedTests); return request({ - qs: {apiKey: that.applitools.apiKey, updateBaseline: updateBaseline}, - url: that.self.host + '/api/sessions/running/' + that.self.sessionId, + qs: {apiKey: ctx.applitools.apiKey, updateBaseline: updateBaseline}, + url: ctx.self.host + '/api/sessions/running/' + ctx.self.sessionId, method: 'DELETE', - headers: that.self.headers, - timeout: that.self.reqTimeout + headers: ctx.self.headers, + timeout: ctx.self.reqTimeout + }) + .spread(function clearAndStore(res, body) { + if (!body) return; + + ctx.self.resultObject[ctx.currentArgs.name] = merge({ + id: ctx.self.sessionId, + url: ctx.self.url + }, JSON.parse(body)); + ctx.self.url = undefined; + ctx.self.sessionId = undefined; + ctx.self.isNew = undefined; }); - }) - .spread(function clearAndStore(res, body) { - if (!body) return; - - that.self.resultObject[that.currentArgs.name] = merge({ - id: that.self.sessionId, - url: that.self.url - }, JSON.parse(body)); - that.self.url = undefined; - that.self.sessionId = undefined; - that.self.isNew = undefined; - }).nodeify(done); + }); }; diff --git a/lib/getPageInfo.js b/lib/getPageInfo.js index 0062d42..a89aac5 100644 --- a/lib/getPageInfo.js +++ b/lib/getPageInfo.js @@ -17,7 +17,7 @@ function isNumber(variable) { return typeof variable === 'number'; } -module.exports = function(done) { +module.exports = function() { var that = this; var response = { excludeRect: [], @@ -26,84 +26,23 @@ module.exports = function(done) { var excludeRect = []; var element = that.currentArgs.elem; - return Promise.try(function() { - /*eslint-disable*/ - return that.instance.execute(function() { - /** - * get current scroll position - * @return {Object} x and y coordinates of current scroll position - */ - var getScrollPosition = function() { - var x = 0, - y = 0; - - if (typeof window.pageYOffset === 'number') { - - /* Netscape compliant */ - y = window.pageYOffset; - x = window.pageXOffset; - - } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) { - - /* DOM compliant */ - y = document.body.scrollTop; - x = document.body.scrollLeft; - - } else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) { - - /* IE6 standards compliant mode */ - y = document.documentElement.scrollTop; - x = document.documentElement.scrollLeft; - - } - - return { - x: x, - y: y - }; - }; - - return { - title: document.title, - scrollPos: getScrollPosition(), - screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), - screenHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) - }; - }); - /*eslint-enable*/ - }) + return Promise.resolve(that.instance.execute(getDocumentMeta)) .then(function getElementInformation(res) { response = merge(response, res.value); if(!element) { - return Promise.resolve([{}, {}]); + return {}; } /** * needs to get defined that verbose to make it working in IE driver */ - that.instance.selectorExecute(element, function(elem) { - var boundingRect = elem[0].getBoundingClientRect(); - return { - elemBounding: { - width: boundingRect.width ? boundingRect.width : boundingRect.right - boundingRect.left, - height: boundingRect.height ? boundingRect.height : boundingRect.bottom - boundingRect.top, - top: boundingRect.top, - right: boundingRect.right, - bottom: boundingRect.bottom, - left: boundingRect.left - } - }; - }) - .then(function (elementInfo) { - // ! TODO check response and its schema being correct here - return [elementInfo, response]; - }); + return that.instance.selectorExecute(element, getElemBoundingRect); }) /** * get information about exclude elements */ - .then(function getExcludeInfo(res, responses, done) { + .then(function getExcludeInfo(res) { response = merge(response, res); /** @@ -114,7 +53,7 @@ module.exports = function(done) { var excludeElements = []; if (!that.currentArgs.exclude) { - return done(null, []); + return []; } else if (!(that.currentArgs.exclude instanceof Array)) { that.currentArgs.exclude = [that.currentArgs.exclude]; } @@ -139,38 +78,99 @@ module.exports = function(done) { return []; } - return that.instance.selectorExecute(excludeElements, function() { + return that.instance.selectorExecute(excludeElements, getExcludeRects); + }) + .then(function(excludeElements) { + if(excludeElements && excludeElements.length) { + response.excludeRect = excludeRect.concat(excludeElements); + } + return response; + }); +}; - /** - * excludeElements are elements queried by specific selenium strategy - */ - var excludeElements = Array.prototype.slice.call(arguments), - excludeRect = []; +/*eslint-disable*/ +function getDocumentMeta() { + /** + * get current scroll position + * @return {Object} x and y coordinates of current scroll position + */ + var getScrollPosition = function() { + var x = 0, + y = 0; - excludeElements.forEach(function(elements) { + if (typeof window.pageYOffset === 'number') { - if(!elements) { - return; - } + /* Netscape compliant */ + y = window.pageYOffset; + x = window.pageXOffset; - elements.forEach(function(elem) { - var elemRect = elem.getBoundingClientRect(); - excludeRect.push({ - x0: elemRect.left, - y0: elemRect.top, - x1: elemRect.right, - y1: elemRect.bottom - }); - }); - }); + } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) { + + /* DOM compliant */ + y = document.body.scrollTop; + x = document.body.scrollLeft; + + } else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) { - return excludeRect; + /* IE6 standards compliant mode */ + y = document.documentElement.scrollTop; + x = document.documentElement.scrollLeft; + } + + return { + x: x, + y: y + }; + }; + + return { + title: document.title, + scrollPos: getScrollPosition(), + screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), + screenHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) + }; +} + +function getExcludeRects() { + + /** + * excludeElements are elements queried by specific selenium strategy + */ + var excludeElements = Array.prototype.slice.call(arguments), + excludeRect = []; + + excludeElements.forEach(function(elements) { + + if(!elements) { + return; + } + + elements.forEach(function(elem) { + var elemRect = elem.getBoundingClientRect(); + excludeRect.push({ + x0: elemRect.left, + y0: elemRect.top, + x1: elemRect.right, + y1: elemRect.bottom + }); }); - }) - .then(function(excludeElements) { - if(excludeElements && excludeElements.length) { - response.excludeRect = excludeRect.concat(excludeElements); + }); + + return excludeRect; +} + +function getElemBoundingRect(elem) { + var boundingRect = elem[0].getBoundingClientRect(); + return { + elemBounding: { + width: boundingRect.width ? boundingRect.width : boundingRect.right - boundingRect.left, + height: boundingRect.height ? boundingRect.height : boundingRect.bottom - boundingRect.top, + top: boundingRect.top, + right: boundingRect.right, + bottom: boundingRect.bottom, + left: boundingRect.left } - }).nodeify(done); -}; + }; +} +/*eslint-enable*/ diff --git a/lib/makeScreenshot.js b/lib/makeScreenshot.js index 3eaae25..a0b4f00 100644 --- a/lib/makeScreenshot.js +++ b/lib/makeScreenshot.js @@ -7,35 +7,37 @@ var Promise = require('bluebird'); */ var modifyElements = function(elements, style, value) { if(elements.length === 0) { - return; + return Promise.resolve(); } - return this.instance.selectorExecute(elements, function() { - var args = Array.prototype.slice.call(arguments).filter(function(n){ return !!n; }), - style = args[args.length - 2], - value = args[args.length - 1]; + return this.instance.selectorExecute(elements, applyStyle, style, value); +}; + +function applyStyle() { + var args = Array.prototype.slice.call(arguments).filter(function(n){ return !!n; }), + style = args[args.length - 2], + value = args[args.length - 1]; - args.splice(-2); - for(var i = 0; i < args.length; ++i) { - for(var j = 0; j < args[i].length; ++j) { - args[i][j].style[style] = value; - } + args.splice(-2); + for(var i = 0; i < args.length; ++i) { + for(var j = 0; j < args[i].length; ++j) { + args[i][j].style[style] = value; } + } - }, style, value); -}; +} -module.exports = function(done) { - var that = this; +module.exports = function() { + var ctx = this; /** * take actual screenshot in given screensize just once */ - if(this.self.takeScreenshot === false) { - return done(); + if(ctx.self.takeScreenshot === false) { + return Promise.resolve(); } - this.self.takeScreenshot = false; + ctx.self.takeScreenshot = false; /** * gather all elements to hide @@ -43,7 +45,7 @@ module.exports = function(done) { var hiddenElements = [], removeElements = []; - this.queuedShots.forEach(function(args) { + ctx.queuedShots.forEach(function(args) { if(typeof args.hide === 'string') { hiddenElements.push(args.hide); } @@ -62,24 +64,22 @@ module.exports = function(done) { * hide / remove elements */ return Promise.all([ - modifyElements.call(that, hiddenElements, 'visibility', 'hidden'), - modifyElements.call(that, removeElements, 'display', 'none') + modifyElements.call(ctx, hiddenElements, 'visibility', 'hidden'), + modifyElements.call(ctx, removeElements, 'display', 'none') ]) .then(function () { /** * take 100ms pause to give browser time for rendering */ - return that.instance.pause(100).saveDocumentScreenshot(that.screenshot); + return ctx.instance.pause(100).saveDocumentScreenshot(ctx.screenshot); }) .then(function () { /** * make hidden elements visible again */ return Promise.all([ - modifyElements.call(that, hiddenElements, 'visibility', ''), - modifyElements.call(that, removeElements, 'display', '') + modifyElements.call(ctx, hiddenElements, 'visibility', ''), + modifyElements.call(ctx, removeElements, 'display', '') ]); - }) - .nodeify(done); - + }); }; diff --git a/lib/renameFiles.js b/lib/renameFiles.js index 41c98b6..84f5276 100644 --- a/lib/renameFiles.js +++ b/lib/renameFiles.js @@ -3,32 +3,30 @@ var Promise = require('bluebird'); var glob = Promise.promisify(require('glob')); var fs = require('fs'); +Promise.promisifyAll(fs); module.exports = function() { - var done = arguments[arguments.length - 1]; - var that = this; + var ctx = this; - return glob('{' + this.regressionPath + ',' + this.baselinePath + '}', {}) + return glob('{' + ctx.regressionPath + ',' + ctx.baselinePath + '}', {}) .then(function(files) { /** * if no files were found continue */ if(files.length === 0) { - return done(); + return; } - that.isComparable = true; - that.filename = that.regressionPath; + ctx.isComparable = true; + ctx.filename = ctx.regressionPath; /** * rename existing files */ - if(files.length === 2 && that.updateBaseline && !that.self.usesApplitools) { - return fs.rename(that.regressionPath, that.baselinePath, done); - } else { - return done(); + if(files.length === 2 && ctx.updateBaseline && !ctx.self.usesApplitools) { + return fs.renameAsync(ctx.regressionPath, ctx.baselinePath); } - }).nodeify(done); + }); }; diff --git a/lib/saveImageDiff.js b/lib/saveImageDiff.js index 7732cab..9316b87 100644 --- a/lib/saveImageDiff.js +++ b/lib/saveImageDiff.js @@ -1,74 +1,70 @@ 'use strict'; var Promise = require('bluebird'); -var fs = Promise.promisifyAll(require('fs')); +var fs = require('fs'); +Promise.promisifyAll(fs); var logWarning = require('./logWarning.js'); +var streamToPromise = require('./util/streamToPromise'); -module.exports = function(imageDiff,done) { +module.exports = function(imageDiff) { - var that = this; - var misMatchTolerance = parseFloat(imageDiff.misMatchPercentage,10); + var ctx = this; return Promise.try(function() { - if(typeof imageDiff === 'function') { - this.self.resultObject[this.currentArgs.name].push({ - baselinePath: this.baselinePath, - message: 'first image of module "' + this.currentArgs.name + '" from page "' + this.pagename + '" successfully taken', + if(!imageDiff) { + ctx.self.resultObject[ctx.currentArgs.name].push({ + baselinePath: ctx.baselinePath, + message: 'first image of module "' + ctx.currentArgs.name + '" from page "' + ctx.pagename + '" successfully taken', misMatchPercentage: 0, isExactSameImage: true, isSameDimensions: true, isWithinMisMatchTolerance: true, - properties: this.currentArgs + properties: ctx.currentArgs }); - return imageDiff(); + return; } /** * if set misMatchTolerance is smaller then compared misMatchTolerance * make image diff */ - if(this.misMatchTolerance < misMatchTolerance) { + var misMatchTolerance = parseFloat(imageDiff.misMatchPercentage,10); + if(ctx.misMatchTolerance < misMatchTolerance) { /*istanbul ignore next*/ if(!imageDiff.isSameDimensions) { - logWarning.call(this.instance, 'DimensionWarning'); + logWarning.call(ctx.instance, 'DimensionWarning'); } - this.self.resultObject[this.currentArgs.name].push({ - baselinePath: this.baselinePath, - regressionPath: this.regressionPath, - diffPath: this.diffPath, - message: 'mismatch tolerance exceeded (+' + (misMatchTolerance - this.misMatchTolerance) + '), image-diff created', + ctx.self.resultObject[ctx.currentArgs.name].push({ + baselinePath: ctx.baselinePath, + regressionPath: ctx.regressionPath, + diffPath: ctx.diffPath, + message: 'mismatch tolerance exceeded (+' + (misMatchTolerance - ctx.misMatchTolerance) + '), image-diff created', misMatchPercentage: misMatchTolerance, isExactSameImage: false, isSameDimensions: imageDiff.isSameDimensions, isWithinMisMatchTolerance: false, - properties: this.currentArgs + properties: ctx.currentArgs }); - imageDiff.getDiffImage().pack() - .on('end', done.bind(null, null, this.resultObject)) - .pipe(fs.createWriteStream(this.diffPath)); + var stream = imageDiff.getDiffImage(); + imageDiff.getDiffImage().pack().pipe(fs.createWriteStream(ctx.diffPath)); + return streamToPromise(stream); } /** * otherwise delete diff */ else { - /** * check if diff shot exists */ - return Promise.try(function checkDiffShot() { - return fs.existsAsync(that.diffPath); - }) - /** - * remove diff if yes - */ - .then(function unlinkIfExists(exists) { - if (exists) return fs.unlinkAsync(that.diffPath); + return fs.unlinkAsync(ctx.diffPath) + .catch(function(ignoredErr) { + return; // ignore }) /** * Save a new baseline image, if one doesn't already exist. @@ -76,19 +72,20 @@ module.exports = function(imageDiff,done) { * If one does exist, we delete the temporary regression. */ .then(function saveNewBaseline() { - return fs.existsAsync(that.baselinePath) + return fs.statAsync(ctx.baselinePath) .then(function(exists) { - return exists ? - fs.unlinkAsync(that.regressionPath) : - fs.renameAsync(that.regressionPath, that.baselinePath); + return fs.unlinkAsync(ctx.regressionPath); + }) + .catch(function(e) { + return fs.renameAsync(ctx.regressionPath, ctx.baselinePath); }); }) .then(function setResults() { /** * return result object to WebdriverIO instance */ - that.self.resultObject[that.currentArgs.name].push({ - baselinePath: that.baselinePath, + ctx.self.resultObject[ctx.currentArgs.name].push({ + baselinePath: ctx.baselinePath, message: 'mismatch tolerance not exceeded (~' + misMatchTolerance + '), baseline didn\'t change', misMatchPercentage: misMatchTolerance, isExactSameImage: misMatchTolerance === 0, @@ -97,6 +94,6 @@ module.exports = function(imageDiff,done) { }); }); } - }).nodeify(done); + }) }; diff --git a/lib/setScreenWidth.js b/lib/setScreenWidth.js index 72234b1..89ed844 100644 --- a/lib/setScreenWidth.js +++ b/lib/setScreenWidth.js @@ -7,7 +7,7 @@ var takenScreenSizes = {}; var Promise = require('bluebird'); -module.exports = function(done) { +module.exports = function() { var that = this; this.newScreenSize = {}; @@ -53,10 +53,7 @@ module.exports = function(done) { } /** - * resize browser resolution - */ - - /** + * resize browser resolution. * if shot will be taken in a specific screenWidth, rename file and append screen width * value in filename */ @@ -68,6 +65,5 @@ module.exports = function(done) { return that.instance.setViewportSize({width: that.newScreenSize.width, height: that.newScreenSize.height}) .pause(100); - - }).nodeify(done); + }); }; diff --git a/lib/startSession.js b/lib/startSession.js index 3a9ba46..6f497a3 100644 --- a/lib/startSession.js +++ b/lib/startSession.js @@ -1,75 +1,79 @@ 'use strict'; -var Promise = require('bluebird'); var pkg = require('../package.json'); -var request = Promise.promisify(require('request'), {multiArg: true}); +var Promise = require('bluebird'); +var request = Promise.promisify(require('request')); +Promise.promisifyAll(request); var WebdriverIO = require('webdriverio'); module.exports = function() { var ctx = this; - var done = arguments[arguments.length - 1]; - - /** - * skip when not using applitools - */ - if(!this.self.usesApplitools || this.self.sessionId) { - return Promise.resolve().nodeify(done); - } - /** - * get meta information of current session - */ return Promise.try(function() { - /*eslint-disable*/ - ctx.instance.execute(function() { - return { - useragent: navigator.userAgent, - screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), - documentHeight: document.documentElement.scrollHeight - }; - }) - /*eslint-enable*/ + /** + * skip when not using applitools + */ + if(!ctx.self.usesApplitools || ctx.self.sessionId) { + return; + } + + return Promise.resolve(ctx.instance.execute(getNavigator)) .then(function(res) { ctx.useragent = res.value.useragent; ctx.displaySize = { width: ctx.screenWidth && ctx.screenWidth.length ? ctx.screenWidth[0] : res.value.screenWidth, height: res.value.documentHeight }; - }); - }) - .then(function initializeApplitools(){ - return request({ - url: ctx.self.host + '/api/sessions/running', - qs: {apiKey: ctx.applitools.apiKey}, - method: 'POST', - json: { - startInfo: { - appIdOrName: ctx.applitools.appName, - scenarioIdOrName: ctx.currentArgs.name, - batchInfo: { - id: ctx.applitools.batchId, - name: ctx.pagename, - startedAt: new Date().toISOString() - }, - environment: { - displaySize: ctx.displaySize, - inferred: 'useragent:' + ctx.useragent - }, - matchLevel: 'Strict', - agentId: pkg.name + '/' + pkg.version - } - }, - headers: ctx.self.headers, - timeout: ctx.self.reqTimeout }) - .spread(function(res, body) { + .then(function() { + return request({ + url: ctx.self.host + '/api/sessions/running', + qs: {apiKey: ctx.applitools.apiKey}, + method: 'POST', + json: { + 'startInfo': { + 'appIdOrName': ctx.applitools.appName, + 'scenarioIdOrName': ctx.currentArgs.name, + 'batchInfo': { + 'id': ctx.applitools.batchId, + 'name': ctx.pagename, + 'startedAt': new Date().toISOString() + }, + 'environment': { + 'displaySize': ctx.displaySize, + 'inferred': 'useragent:' + ctx.useragent + }, + 'matchLevel': 'Strict', + 'agentId': pkg.name + '/' + pkg.version + } + }, + headers: ctx.self.headers, + timeout: ctx.self.reqTimeout + }); + }) + .then(function(res) { + if (res.statusCode !== 200 && res.statusCode !== 201) { + throw new Error(); + } + var body = res.body; + ctx.self.sessionId = body.id; ctx.self.url = body.url; ctx.self.isNew = res.statusCode === 201; }) - .catch(function() { - throw new WebdriverIO.ErrorHandler.CommandError('Couldn\'t start applitools session'); + .catch(function(err) { + throw new WebdriverIO.ErrorHandler.CommandError('Couldn\'t start applitools session: ' + err.stack); }); - }).nodeify(done); + }); }; + +/*eslint-disable*/ +function getNavigator() { + return { + useragent: navigator.userAgent, + screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), + documentHeight: document.documentElement.scrollHeight + }; +} +/*eslint-enable*/ diff --git a/lib/syncImages.js b/lib/syncImages.js index 6ba9023..bab9ae4 100644 --- a/lib/syncImages.js +++ b/lib/syncImages.js @@ -3,10 +3,12 @@ var tar = require('tar'); var zlib = require('zlib'); var Promise = require('bluebird'); -var targz = Promise.promisify(require('tar.gz')); +var targz = require('tar.gz'); Promise.promisifyAll(targz.prototype); -var fs = Promise.promisifyAll(require('fs-extra')); -var request = Promise.promisifyAll(require('request'), {multiArg: true}); +var fs = require('fs-extra'); +Promise.promisifyAll(fs); +var request = require('request'); +Promise.promisifyAll(request); var rimraf = Promise.promisify(require('rimraf')); var streamToPromise = require('./util/streamToPromise'); var assign = require('object-assign'); @@ -17,54 +19,54 @@ var assign = require('object-assign'); * * @param {Function} done callback to be called after sync finishes */ -var syncDown = function(ctx) { +function syncDown(ctx) { - var args = { - url: ctx.api + (ctx.api.substr(-1) !== '/' ? '/' : '') + ctx.screenshotRoot + '.tar.gz', - headers: { 'accept-encoding': 'gzip,deflate' }, - }; + return Promise.fromCallback(function(done) { + var args = { + url: ctx.api + (ctx.api.substr(-1) !== '/' ? '/' : '') + ctx.screenshotRoot + '.tar.gz', + headers: { 'accept-encoding': 'gzip,deflate' }, + }; - if(typeof ctx.user === 'string' && typeof ctx.key === 'string') { - args.auth = { - user: ctx.user, - pass: ctx.key - }; - } + if(typeof ctx.user === 'string' && typeof ctx.key === 'string') { + args.auth = { + user: ctx.user, + pass: ctx.key + }; + } - return request.getAsync(args) - .then(function(resp) { - /*! - * no error if repository doesn't exists - */ - /*istanbul ignore if*/ - if(resp.statusCode === 404) { - return; - } + var r = request.get(args); - /*istanbul ignore next*/ - if(resp.statusCode !== 200 || resp.headers['content-type'] !== 'application/octet-stream') { - throw new Error('unexpected statusCode (' + resp.statusCode + ' != 200) or content-type (' + - resp.headers['content-type'] + ' != application/octet-stream)'); - } + r.on('error', done); + r.on('response', function(resp) { - /** - * check if repository directory already exists and - * clear it if yes - */ - return fs.existsAsync(ctx.screenshotRoot) - .then(function(exists) { - if (exists) { - return rimraf(ctx.screenshotRoot) - .then(function() { - fs.mkdirsAsync(ctx.screenshotRoot, '0755', true); - }); - } - }) - .then(function() { - return streamToPromise(resp.pipe(zlib.Gunzip()).pipe(tar.Extract({ path: '.' }))); - }); - }); -}; + /*! + * no error if repository doesn't exists + */ + /*istanbul ignore if*/ + if(resp.statusCode === 404) { + return done(); + } + + /*istanbul ignore next*/ + if(resp.statusCode !== 200 || resp.headers['content-type'] !== 'application/octet-stream') { + return done(new Error('unexpected statusCode (' + resp.statusCode + ' != 200) or content-type (' + + resp.headers['content-type'] + ' != application/octet-stream)')); + } + + /** + * check if repository directory already exists and + * clear it if yes + */ + if(fs.existsSync(ctx.screenshotRoot)) { + rimraf.sync(ctx.screenshotRoot); + fs.mkdirsSync(ctx.screenshotRoot, '0755', true); + } + + resp.pipe(zlib.Gunzip()).pipe(tar.Extract({ path: '.' })).on('end', done); + + }); + }); + } /** * sync up @@ -72,11 +74,12 @@ var syncDown = function(ctx) { * * @param {Function} done callback to be called after tarball was uploaded */ -var syncUp = function(ctx) { +function syncUp(ctx) { var screenshotRoot = ctx.screenshotRoot, args = { url: ctx.api }, tarballPath = screenshotRoot + '.tar.gz'; + if(typeof ctx.user === 'string' && typeof ctx.key === 'string') { args.auth = { user: ctx.user, @@ -91,7 +94,7 @@ var syncUp = function(ctx) { return rimraf(tarballPath); }); }); -}; +} /** * sync command @@ -100,17 +103,19 @@ var syncUp = function(ctx) { * @param {Function} done callback to be called after syncing * @return {Object} WebdriverCSS instance */ -module.exports = function(done) { +module.exports = function() { + + var ctx = this; return Promise.try(function() { - if(!this.api) { + if(!ctx.api) { throw new Error('No sync options specified! Please provide an api path and user/key (optional).'); } - var sync = this.needToSync ? syncUp : syncDown; - this.needToSync = false; + var sync = ctx.needToSync ? syncUp : syncDown; + ctx.needToSync = false; - return sync(this); - }).nodeify(done); + return sync(ctx); + }); }; diff --git a/lib/viewportScreenshot.js b/lib/viewportScreenshot.js index 114ee97..5d6049a 100644 --- a/lib/viewportScreenshot.js +++ b/lib/viewportScreenshot.js @@ -30,14 +30,7 @@ module.exports = function viewportScreenshot(fileName) { self.execute(getScrollingPosition, null) .then(function getWidthHeight() { - /*eslint-disable*/ - return self.execute(function() { - return { - screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), - screenHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) - }; - }); - /*eslint-enable*/ + return self.execute(getScreenDimensions); }) .then(function(res) { response.execute.push(res); @@ -58,10 +51,16 @@ module.exports = function viewportScreenshot(fileName) { ) .writeAsync(fileName); }); - }; /*eslint-disable*/ +function getScreenDimensions() { + return { + screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), + screenHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0) + }; +} + function getScrollingPosition() { var position = [0, 0]; if (typeof window.pageYOffset !== 'undefined') { diff --git a/lib/webdrivercss.js b/lib/webdrivercss.js index 65b5075..e1ddd72 100644 --- a/lib/webdrivercss.js +++ b/lib/webdrivercss.js @@ -4,12 +4,12 @@ * WebdriverCSS */ -var fs = require('fs-extra'), - workflow = require('./workflow.js'), - viewportScreenshot = require('./viewportScreenshot.js'), - documentScreenshot = require('./documentScreenshot.js'), - generateUUID = require('./generateUUID.js'), - syncImages = require('./syncImages'); +var fs = require('fs-extra'); +var workflow = require('./workflow.js'); +var viewportScreenshot = require('./viewportScreenshot.js'); +var documentScreenshot = require('./documentScreenshot.js'); +var generateUUID = require('./generateUUID.js'); +var syncImages = require('./syncImages'); /** * initialise plugin diff --git a/lib/workflow.js b/lib/workflow.js index 9f0ebf1..7a3c964 100644 --- a/lib/workflow.js +++ b/lib/workflow.js @@ -15,7 +15,7 @@ var saveImageDiff = require('./saveImageDiff.js'); var asyncCallback = require('./asyncCallback.js'); -function workflow(pagename, args) { +module.exports = function workflow(pagename, args) { this.needToSync = true; /*istanbul ignore next*/ @@ -82,31 +82,13 @@ function workflow(pagename, args) { this.resultObject[currentArgs.name] = []; } - return startSession.bind(context) - .then(function() { - return setScreenWidth.apply(context, arguments); - }) - .then(function() { - return makeScreenshot.apply(context, arguments); - }) - .then(function() { - return renameFiles.apply(context, arguments); - }) - .then(function() { - return getPageInfo.apply(context, arguments); - }) - .then(function() { - return cropImage.apply(context, arguments); - }) - .then(function() { - return compareImages.apply(context, arguments); - }) - .then(function() { - return saveImageDiff.apply(context, arguments); - }) - .then(function() { - return asyncCallback.apply(context, arguments); - }); -} - -module.exports = workflow; + return startSession.call(context) + .then(setScreenWidth.bind(context)) + .then(makeScreenshot.bind(context)) + .then(renameFiles.bind(context)) + .then(getPageInfo.bind(context)) + .then(cropImage.bind(context)) + .then(compareImages.bind(context)) + .then(saveImageDiff.bind(context)) + .then(asyncCallback.bind(context)); +}; diff --git a/package.json b/package.json index b02449a..67ea198 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "url": "git://github.com/webdriverio/webdrivercss.git" }, "dependencies": { - "async": "^0.9.0", "bluebird": "^3.1.1", "deepmerge": "^0.2.7", "fs-extra": "^0.18.2", @@ -23,13 +22,13 @@ "gm": "^1.17.0", "node-resemble-js": "0.0.4", "object-assign": "^4.0.1", - "q": "^1.4.1", "request": "^2.55.0", "rimraf": "^2.3.2", "tar": "^2.1.0", "tar.gz": "^1.0.1" }, "devDependencies": { + "async": "^0.9.0", "bower": "^1.7.2", "chai": "^2.3.0", "coveralls": "~2.11.2", From 9664817cf3a307c22ace37d3257e6826643d72ac Mon Sep 17 00:00:00 2001 From: Samuel Reed Date: Wed, 13 Jan 2016 21:13:00 -0600 Subject: [PATCH 7/7] Add long stack traces to test. --- test/test-run.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test-run.sh b/test/test-run.sh index 13150dd..91a959c 100644 --- a/test/test-run.sh +++ b/test/test-run.sh @@ -1,6 +1,6 @@ #!/bin/bash -set -ex +set -e finish() { pkill -P $$ # kills all processes that have this pid - $$ - as the parent @@ -48,7 +48,7 @@ if [[ $WEBDRIVERCSS_COVERAGE == '1' ]]; then fi # Run tests -$NODE_BIN/_mocha -R $MOCHA_REPORTERS +BLUEBIRD_LONG_STACK_TRACES=1 $NODE_BIN/_mocha -R $MOCHA_REPORTERS STATUS=$? # Echo coverage information