Skip to content

Commit

Permalink
Merge pull request #1 from puneet0191/master
Browse files Browse the repository at this point in the history
PMM-3410 Moving the component
  • Loading branch information
puneet0191 authored Jan 14, 2019
2 parents 8528762 + d50dbef commit 1b4ed37
Show file tree
Hide file tree
Showing 4 changed files with 382 additions and 0 deletions.
61 changes: 61 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# next.js build output
.next
101 changes: 101 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# codeceptjs-resemblehelper
Helper for resemble.js, used for Image comparison in Tests with WebdriverIO

codeceptjs-resemblehelper is [CodeceptJS](https://codecept.io/) helper which can be used to compare screenshots and make the tests fail/pass based on the tolerance allowed

If two screenshot comparisons have difference greater then the tolerance provided, the test will fail.

NPM package: https://www.npmjs.com/package/codeceptjs-resemblehelper

To install the package, just run `npm install codeceptjs-resemblehelper`

### Configuration

This helper should be added in codecept.json/codecept.conf.js

Example:

```json
{
"helpers": {
"ResembleHelper" : {
"require": "codeceptjs-resemblehelper",
"screenshotFolder" : "./tests/output/",
"baseFolder": "./tests/screenshots/base/",
"diffFolder": "./tests/screenshots/diff/"
}
}
}
```
To use the Helper, users must provide the three parameters:
`screenshotFolder` : This will always have the same value as `output` in Codecept configuration, this is the folder where webdriverIO
saves a screenshot when using `I.saveScreenshot` method

`baseFolder`: This is the folder for base images, which will be used with screenshot for comparison

`diffFolder`: This will the folder where resemble would try to store the difference image, which can be viewed later,
Please remember to create empty folder if you don't have one already

Usage, these are major functions that help in visual testing

First one is the `verifyMisMatchPercentage` which basically takes several parameters including tolerance and PrepareBase
```js
/**
* Mis Match Percentage Verification
* @param baseImage Name of the Base Image (Base Image path is taken from Configuration)
* @param screenShotImage Name of the screenshot Image (Screenshot Image Path is taken from Configuration)
* @param diffImageName Name of the Diff Image which will be saved after comparison (Diff Image path is taken from Configuration)
* @param tolerance Tolerance Percentage, default value 10
* @param prepareBase True | False, depending on the requirement if the base images are missing
* @param selector CSS|XPath|id, If provided locator will be used to fetch Bounding Box of the element and only that element is compared on two images
* @param options Resemble JS Options, read more here: https://github.com/rsmbl/Resemble.js
* @returns {Promise<void>}
*/
async verifyMisMatchPercentage(baseImage, screenShotImage, diffImageName, tolerance = 10, prepareBase = false, selector, options){
```
Second one is the `PrepareBase` which basically prepares all the base images in case they are not available
```js
/**
* Function to prepare Base Images from Screenshots
*
* @param baseImage Name of the Base Image (Base Image path is taken from Configuration)
* @param screenShotImage Name of the screenshot Image (Screenshot Image Path is taken from Configuration)
*/
prepareBaseImage(baseImage, screenShotImage) {}
```
Third function is to fetch the boundingBox of an element using selector, this boundingBox is then provided to resemble
so that only that element is compared on the images.
```js
/**
* Function to fetch Bounding box for an element, fetched using selector
*
* @param selector CSS|XPath|ID locators
* @returns {Promise<{boundingBox: {left: *, top: *, right: *, bottom: *}}>}
*/
async getBoundingBox(selector){
```
Users can make use of the boundingBox feature by providing a selector to `verifyMisMatchPercentage` function, it will internally
check if a locator is provided, fetch it's bounding-box and compare only that element on both the images.
Finally to use the helper in your test, you can write something like this:
```
Feature('to verify monitoried Remote Db instances');

Scenario('Open the System Overview Dashboard', async (I, adminPage, loginPage) => {
adminPage.navigateToDashboard("OS", "System Overview");
adminPage.applyTimer("1m");
adminPage.viewMetric("CPU Usage");
I.saveScreenshot("System_Overview_CPU_Usage.png");
});

Scenario('Compare CPU Usage Images', async (I) => {

// passing TRUE to let the helper know to prepare base images
I.verifyMisMatchPercentage("System_Overview_CPU_Usage.png", "System_Overview_CPU_Usage.png", "DiffImage_SystemOverview_CPU_USAGE_Dashboard", 10, true);

// passing a selector, to only compare that element on both the images now
I.verifyMisMatchPercentage("System_Overview_CPU_Usage.png", "System_Overview_CPU_Usage.png", "DiffImage_SystemOverview_CPU_USAGE_Panel", 10, false, "//div[@class='panel-container']");
});
```
195 changes: 195 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
'use strict';

// use any assertion library you like
const resemble = require("resemblejs");
const fs = require('fs');
let assert = require('assert');
const mkdirp = require('mkdirp');
const getDirName = require('path').dirname;
/**
* Resemble.js helper class for CodeceptJS, this allows screen comparison
* @author Puneet Kala
*/
class ResembleHelper extends Helper {

constructor(config) {
super(config);
}

/**
*
* @param image1
* @param image2
* @param diffImage
* @param tolerance
* @param options
* @returns {Promise<any | never>}
*/
async _compareImages (image1, image2, diffImage, tolerance, options) {
image1 = this.config.baseFolder + image1;
image2 = this.config.screenshotFolder + image2;

return new Promise((resolve, reject) => {
if (options !== undefined)
{
resemble.outputSettings({
boundingBox: options.boundingBox
});
}
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);
});
}

/**
*
* @param image1
* @param image2
* @param diffImage
* @param tolerance
* @param options
* @returns {Promise<*>}
*/
async _fetchMisMatchPercentage (image1, image2, diffImage, tolerance, options) {
var result = this._compareImages(image1, image2, diffImage, tolerance, options);
var data = await Promise.resolve(result);
return data.misMatchPercentage;
}

/**
* Mis Match Percentage Verification
* @param baseImage Name of the Base Image (Base Image path is taken from Configuration)
* @param screenShotImage Name of the screenshot Image (Screenshot Image Path is taken from Configuration)
* @param diffImageName Name of the Diff Image which will be saved after comparison (Diff Image path is taken from Configuration)
* @param tolerance Tolerance Percentage, default value 10
* @param prepareBase True | False, depending on the requirement if the base images are missing
* @param selector If set, passed selector will be used to fetch Bouding Box and compared on two images
* @param options Resemble JS Options, read more here: https://github.com/rsmbl/Resemble.js
* @returns {Promise<void>}
*/
async verifyMisMatchPercentage(baseImage, screenShotImage, diffImageName, tolerance = 10, prepareBase = false, selector, options){
if (prepareBase)
{
await this.prepareBaseImage(baseImage, screenShotImage);
}

if (selector !== undefined)
{
if (options !== undefined)
{
options.boundingBox = await this.getBoundingBox(selector);
}
else
{
var options = {};
options.boundingBox = await this.getBoundingBox(selector);
}
}

var misMatch = await this._fetchMisMatchPercentage(baseImage, screenShotImage, diffImageName, tolerance, options);
console.log("MisMatch Percentage Calculated is " + misMatch);
assert.ok(misMatch < tolerance, "MissMatch Percentage " + misMatch);
}

/**
* Function to prepare Base Images from Screenshots
*
* @param baseImage Name of the Base Image (Base Image path is taken from Configuration)
* @param screenShotImage Name of the screenshot Image (Screenshot Image Path is taken from Configuration)
*/
async prepareBaseImage(baseImage, screenShotImage) {
var configuration = this.config;

await this._createDir(configuration.baseFolder + baseImage);

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 + baseImage);
}

/**
* Function to create Directory
* @param directory
* @returns {Promise<void>}
* @private
*/
async _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 browser = this._getBrowser();

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);
var bottom = size.height + location.y;
var right = size.width + location.x;
var boundingBox = {
left: location.x,
top: location.y,
right: right,
bottom: bottom
};

return boundingBox;
}

_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');
}
}

module.exports = ResembleHelper;
25 changes: 25 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "codeceptjs-resemblehelper",
"version": "1.2.1",
"description": "Resemble Js helper for CodeceptJS, with WebdriverIO",
"repository": {
"type": "git",
"url": "[email protected]:Percona-Lab/codeceptjs-resemblehelper.git"
},
"dependencies": {
"assert": "^1.4.1",
"canvas": "^2.2.0",
"mz": "^2.7.0",
"resemblejs": "^3.0.0",
"mkdirp": "^0.5.1",
"path": "^0.12.7"
},
"keywords": [
"codeceptJS",
"codeceptjs",
"resemblejs",
"codeceptjs-resemble"
],
"author": "Puneet Kala <[email protected]>",
"license": "MIT"
}

0 comments on commit 1b4ed37

Please sign in to comment.