diff --git a/index.js b/index.js index d511882..a6eea76 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,6 @@ -'use strict'; - -// use any assertion library you like const resemble = require("resemblejs"); const fs = require('fs'); -let assert = require('assert'); +const assert = require('assert'); const mkdirp = require('mkdirp'); const getDirName = require('path').dirname; @@ -14,226 +11,207 @@ const getDirName = require('path').dirname; class ResembleHelper extends Helper { - constructor(config) { - super(config); - } - - /** - * Compare Images - * @param image1 - * @param image2 - * @param diffImage - * @param options - * @returns {Promise} - */ - async _compareImages (image1, image2, diffImage, options) { - image1 = this.config.baseFolder + image1; - image2 = this.config.screenshotFolder + image2; - if(typeof this.config.consoleOutput == 'undefined') - { - this.config.consoleOutput = true - } - return new Promise((resolve, reject) => { - if (options.boundingBox !== undefined) - { - resemble.outputSettings({ - boundingBox: options.boundingBox - }); - } - - if (options.tolerance !== undefined) - { - if(this.config.consoleOutput){ - console.log("Tolerance Level Provided " + options.tolerance); - } - var tolerance = options.tolerance; - } - resemble.compare(image1, image2, options, (err, data) => { - if (err) { - reject(err); - } else { - resolve(data); - if (data.misMatchPercentage >= tolerance) { - mkdirp(getDirName(this.config.diffFolder + diffImage), function (err) { - if (err) return cb(err); - }); - fs.writeFile(this.config.diffFolder + diffImage + '.png', data.getBuffer(), (err, data) => { - if (err) { - throw new Error(this.err); - } - }); - } - } - }); - }).catch((error) => { - console.log('caught', error.message); + /** + * Compare Images + * + * @param image1 + * @param image2 + * @param diffImage + * @param options + * @returns {Promise} + */ + async _compareImages(image1, image2, diffImage, options) { + image1 = this.config.baseFolder + image1; + image2 = this.config.screenshotFolder + image2; + + return new Promise((resolve, reject) => { + if (options.boundingBox !== undefined) { + resemble.outputSettings({ + boundingBox: options.boundingBox }); + } + + this.debug("Tolerance Level Provided " + options.tolerance); + const tolerance = options.tolerance; + + resemble.compare(image1, image2, options, (err, data) => { + if (err) { + reject(err); + } else { + resolve(data); + if (data.misMatchPercentage >= tolerance) { + mkdirp(getDirName(this.config.diffFolder + diffImage), function (err) { + if (err) return cb(err); + }); + fs.writeFile(this.config.diffFolder + diffImage + '.png', data.getBuffer(), (err, data) => { + if (err) { + throw new Error(this.err); + } + }); + } + } + }); + }); + } + + /** + * + * @param image1 + * @param options + * @returns {Promise<*>} + */ + async _fetchMisMatchPercentage(image1, options) { + const image2 = image1; + const diffImage = "Diff_" + image1.split(".")[0]; + const result = this._compareImages(image1, image2, diffImage, options); + const data = await Promise.resolve(result); + return data.misMatchPercentage; + } + + /** + * Check Visual Difference for Base and Screenshot Image + * @param baseImage Name of the Base Image (Base Image path is taken from Configuration) + * @param options Options ex {prepareBaseImage: true, tolerance: 5} along with Resemble JS Options, read more here: https://github.com/rsmbl/Resemble.js + * @returns {Promise} + */ + async seeVisualDiff(baseImage, options) { + if (options == undefined) { + options = {}; + options.tolerance = 0; } - /** - * - * @param image1 - * @param options - * @returns {Promise<*>} - */ - async _fetchMisMatchPercentage (image1, options) { - var image2 = image1; - var diffImage = "Diff_" + image1.split(".")[0]; - var result = this._compareImages(image1, image2, diffImage, options); - var data = await Promise.resolve(result); - return data.misMatchPercentage; + if (options.prepareBaseImage !== undefined && options.prepareBaseImage) { + await this._prepareBaseImage(baseImage); } - /** - * Check Visual Difference for Base and Screenshot Image - * @param baseImage Name of the Base Image (Base Image path is taken from Configuration) - * @param options Options ex {prepareBaseImage: true, tolerance: 5} along with Resemble JS Options, read more here: https://github.com/rsmbl/Resemble.js - * @returns {Promise} - */ - async seeVisualDiff(baseImage, options) { - if (options == undefined) - { - options = {}; - options.tolerance = 0; - } - - if (options.prepareBaseImage !== undefined && options.prepareBaseImage) - { - await this._prepareBaseImage(baseImage); - } - - var misMatch = await this._fetchMisMatchPercentage(baseImage, options); - if(this.config.consoleOutput){ - console.log("MisMatch Percentage Calculated is " + misMatch); - } - assert(misMatch <= options.tolerance, "MissMatch Percentage " + misMatch); + const misMatch = await this._fetchMisMatchPercentage(baseImage, options); + this.debug("MisMatch Percentage Calculated is " + misMatch); + assert(misMatch <= options.tolerance, "MissMatch Percentage " + misMatch); + } + + /** + * See Visual Diff for an Element on a Page + * + * @param selector Selector which has to be compared expects these -> CSS|XPath|ID + * @param baseImage Base Image for comparison + * @param options Options ex {prepareBaseImage: true, tolerance: 5} along with Resemble JS Options, read more here: https://github.com/rsmbl/Resemble.js + * @returns {Promise} + */ + async seeVisualDiffForElement(selector, baseImage, options) { + + if (options == undefined) { + options = {}; + options.tolerance = 0; } - /** - * See Visual Diff for an Element on a Page - * - * @param selector Selector which has to be compared expects these -> CSS|XPath|ID - * @param baseImage Base Image for comparison - * @param options Options ex {prepareBaseImage: true, tolerance: 5} along with Resemble JS Options, read more here: https://github.com/rsmbl/Resemble.js - * @returns {Promise} - */ - async seeVisualDiffForElement(selector, baseImage, options){ - - if (selector !== undefined) - { - if (options == undefined) - { - options = {}; - options.tolerance = 0; - } - - if (options.prepareBaseImage !== undefined && options.prepareBaseImage) - { - await this._prepareBaseImage(baseImage); - } - - options.boundingBox = await this._getBoundingBox(selector); - var misMatch = await this._fetchMisMatchPercentage(baseImage, options); - if(this.config.consoleOutput) - { - console.log("MisMatch Percentage Calculated is " + misMatch); - } - assert(misMatch <= options.tolerance, "MissMatch Percentage " + misMatch); - } - else { - return null; - } + if (options.prepareBaseImage !== undefined && options.prepareBaseImage) { + await this._prepareBaseImage(baseImage); } - /** - * Function to prepare Base Images from Screenshots - * - * @param screenShotImage Name of the screenshot Image (Screenshot Image Path is taken from Configuration) - */ - async _prepareBaseImage(screenShotImage) { - var configuration = this.config; - - await this._createDir(configuration.baseFolder + screenShotImage); - - fs.access(configuration.screenshotFolder + screenShotImage, fs.constants.F_OK | fs.constants.W_OK, (err) => { - if (err) { - console.error( - `${configuration.screenshotFolder + screenShotImage} ${err.code === 'ENOENT' ? 'does not exist' : 'is read-only'}`); - } - }); - - fs.access(configuration.baseFolder, fs.constants.F_OK | fs.constants.W_OK, (err) => { - if (err) { - console.error( - `${configuration.baseFolder} ${err.code === 'ENOENT' ? 'does not exist' : 'is read-only'}`); - } - }); - - fs.copyFileSync(configuration.screenshotFolder + screenShotImage, configuration.baseFolder + screenShotImage); + options.boundingBox = await this._getBoundingBox(selector); + const misMatch = await this._fetchMisMatchPercentage(baseImage, options); + this.debug("MisMatch Percentage Calculated is " + misMatch); + assert(misMatch <= options.tolerance, "MissMatch Percentage " + misMatch); + } + + /** + * Function to prepare Base Images from Screenshots + * + * @param screenShotImage Name of the screenshot Image (Screenshot Image Path is taken from Configuration) + */ + async _prepareBaseImage(screenShotImage) { + const configuration = this.config; + + await this._createDir(configuration.baseFolder + screenShotImage); + + fs.access(configuration.screenshotFolder + screenShotImage, fs.constants.F_OK | fs.constants.W_OK, (err) => { + if (err) { + throw new Error( + `${configuration.screenshotFolder + screenShotImage} ${err.code === 'ENOENT' ? 'does not exist' : 'is read-only'}`); + } + }); + + fs.access(configuration.baseFolder, fs.constants.F_OK | fs.constants.W_OK, (err) => { + if (err) { + throw new Error( + `${configuration.baseFolder} ${err.code === 'ENOENT' ? 'does not exist' : 'is read-only'}`); + } + }); + + fs.copyFileSync(configuration.screenshotFolder + screenShotImage, configuration.baseFolder + screenShotImage); + } + + /** + * Function to create Directory + * @param directory + * @returns {Promise} + * @private + */ + _createDir(directory) { + mkdirp.sync(getDirName(directory)); + } + + /** + * Function to fetch Bounding box for an element, fetched using selector + * + * @param selector CSS|XPath|ID selector + * @returns {Promise<{boundingBox: {left: *, top: *, right: *, bottom: *}}>} + */ + async _getBoundingBox(selector) { + const helper = this._getHelper(); + await helper.waitForVisible(selector); + const els = await helper._locate(selector); + if (!els.length) throw new Error(`Element ${selector} couldn't be located`); + const el = els[0]; + + let location, size; + + if (this.helpers['Puppeteer']) { + const box = await el.boundingBox(); + size = location = box; } - /** - * Function to create Directory - * @param directory - * @returns {Promise} - * @private - */ - async _createDir (directory) { - mkdirp.sync(getDirName(directory)); + if (this.helpers['WebDriver'] || this.helpers['Appium']) { + location = await ele.getLocation(); + size = await ele.getSize(); + } + + if (this.helpers['WebDriverIO']) { + location = await helper.browser.getLocation(selector); + size = await helper.browser.getElementSize(selector); } - /** - * Function to fetch Bounding box for an element, fetched using selector - * - * @param selector CSS|XPath|ID selector - * @returns {Promise<{boundingBox: {left: *, top: *, right: *, bottom: *}}>} - */ - async _getBoundingBox(selector){ - const browser = this._getBrowser(); - - if (this.helpers['WebDriver']) { - const ele = await browser.$(selector); - var location = await ele.getLocation(); - var size = await ele.getSize(); - } - else { - var ele = await browser.element(selector) - .then((res) => { - return res; - }) - .catch((err) => { - // Catch the error because webdriver.io throws if the element could not be found - // Source: https://github.com/webdriverio/webdriverio/blob/master/lib/protocol/element.js - return null; - }); - var location = await browser.getLocation(selector); - var size = await browser.getElementSize(selector); - } + const bottom = size.height + location.y; + const right = size.width + location.x; + const boundingBox = { + left: location.x, + top: location.y, + right: right, + bottom: bottom + }; - var bottom = size.height + location.y; - var right = size.width + location.x; - var boundingBox = { - left: location.x, - top: location.y, - right: right, - bottom: bottom - }; + this.debugSection('Area', JSON.stringify(boundingBox)); - return boundingBox; + return boundingBox; + } + + _getHelper() { + if (this.helpers['Puppeteer']) { + return this.helpers['Puppeteer']; } - _getBrowser() { - if (this.helpers['WebDriver']) { - return this.helpers['WebDriver'].browser; - } - if (this.helpers['Appium']) { - return this.helpers['Appium'].browser; - } - if (this.helpers['WebDriverIO']) { - return this.helpers['WebDriverIO'].browser; - } - throw new Error('No matching helper found. Supported helpers: WebDriver/Appium/WebDriverIO'); + if (this.helpers['WebDriver']) { + return this.helpers['WebDriver']; } + if (this.helpers['Appium']) { + return this.helpers['Appium']; + } + if (this.helpers['WebDriverIO']) { + return this.helpers['WebDriverIO']; + } + + throw new Error('No matching helper found. Supported helpers: WebDriver/Appium/Puppeteer'); + } } module.exports = ResembleHelper;