Skip to content
This repository has been archived by the owner on Jul 21, 2022. It is now read-only.

Commit

Permalink
support for visual diffiing - using pixelmatch
Browse files Browse the repository at this point in the history
  • Loading branch information
Roman Samec committed Jan 30, 2019
1 parent cfb018f commit dd4433e
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 9 deletions.
135 changes: 131 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ var path = require('path')
var puppeteer = require('puppeteer-core');
var fs = require('fs');
var DEFAULT_CMD = require('./chrome');
var PNG = require('pngjs').PNG;
var pixelmatch = require('pixelmatch');
var dynamicpixelmatch = require('dynamicpixelmatch');

function isJSFlags(flag) {
return flag.indexOf('--js-flags=') === 0
Expand Down Expand Up @@ -45,7 +48,7 @@ var ChromeBrowser = function (baseBrowserDecorator, args) {
}
}

var ensureExists = function (path, mask) {
const ensureExists = function (path, mask) {
return new Promise((resolve, reject) => {
if (typeof mask == 'function') { // allow the `mask` parameter to be optional
//cb = mask;
Expand All @@ -59,9 +62,82 @@ var ensureExists = function (path, mask) {
});
})
}
const ensureExistsFromRoot = async (root, pathes) => {
var rootPath = root;
await ensureExists(rootPath);
for (var i = 0; i < pathes.length - 1; i++) {
rootPath = path.resolve(rootPath, pathes[i]);
await ensureExists(rootPath);
}
}
const fileExists = (path) => {
return new Promise((resolve, reject) => {
fs.access(path, fs.F_OK, (err) => {
if (err) {
resolve(false)
}
//file exists
console.log(path + ' ----------> ' + true)
resolve(true);
})
})
}
const DEFAULT_TARGET_DIR = 'target';
const DEFAULT_SCREENSHOT_GOLDER_DIR = 'golden';
const DEFAULT_SCREENSHOT_DIR = 'screenshots';
const DEFAULT_SCREENSHOT_DIFF_DIR = 'diff';

const screenshotRootPathes = ["./", DEFAULT_TARGET_DIR, DEFAULT_SCREENSHOT_DIR];
const goldenRootPathes = ["./", DEFAULT_TARGET_DIR, DEFAULT_SCREENSHOT_GOLDER_DIR];
const diffRootPathes = ["./", DEFAULT_TARGET_DIR, DEFAULT_SCREENSHOT_DIFF_DIR];

const getGoldenScreenshotPath = (pathes) => path.resolve(...goldenRootPathes.concat(pathes));
const getScrenshotPath = (pathes) => path.resolve(...screenshotRootPathes.concat(pathes));
const getDiffScrenshotPath = (pathes) => path.resolve(...diffRootPathes.concat(pathes));

const compareScreenshots = async (pathes, exluded) => {
return new Promise(async (resolve, reject) => {
doneReading = async () => {
// Wait until both files are read.
if (++filesRead < 2) return;

// The files should be the same size.

if (img1.width != img2.width) {
console.error("image widths are not the same");
resolve(false)
}
if (img1.height != img2.height) {
console.error("image heights are not the same");
resolve(false)
}

// Do the visual diff.
const diff = new PNG({ width: img1.width, height: img2.height });
const matchOptions = { threshold: 0.1 };
const numDiffPixels = (exluded != null && exluded.length != 0) ?
dynamicpixelmatch(img1.data, img2.data, diff.data, img1.width, img1.height, matchOptions, exluded) :
pixelmatch(img1.data, img2.data, diff.data, img1.width, img1.height, matchOptions);


// The files should look the same.
if (numDiffPixels != 0) {
console.error("number of different pixels are not 0");

await ensureExistsFromRoot(path.resolve(...diffRootPathes), pathes)
diff.pack().pipe(fs.createWriteStream(getDiffScrenshotPath(pathes)));

resolve(false)
}
resolve(true);
}
const img1 = fs.createReadStream(getGoldenScreenshotPath(pathes)).pipe(new PNG()).on('parsed', await doneReading);
const img2 = fs.createReadStream(getScrenshotPath(pathes)).pipe(new PNG()).on('parsed', await doneReading);

let filesRead = 0;

})
}
var PuppeteerBrowser = function (baseBrowserDecorator, args) {
ChromeBrowser.apply(this, arguments)
console.log(args);
Expand All @@ -75,9 +151,9 @@ var PuppeteerBrowser = function (baseBrowserDecorator, args) {
console.log(url);
await ensureExists(path.resolve("./", DEFAULT_TARGET_DIR))
await ensureExists(path.resolve("./", DEFAULT_TARGET_DIR, DEFAULT_SCREENSHOT_DIR))

browser = await puppeteer.launch({
headless: flags.indexOf("--headless")!=-1 ,
headless: flags.indexOf("--headless") != -1,
args: flags,
executablePath: DEFAULT_CMD[process.platform]
});
Expand Down Expand Up @@ -109,7 +185,58 @@ var PuppeteerBrowser = function (baseBrowserDecorator, args) {
const element = await frame.$(selector);
await element.screenshot({ path: filename });
})


const screenshotPage = async (pathes, golden, clip) => {
await ensureExistsFromRoot(golden ? path.resolve(...goldenRootPathes) : path.resolve(...screenshotRootPathes), pathes)
const filename = golden ? getGoldenScreenshotPath(pathes) : getScrenshotPath(pathes);
console.log('[Node]', 'Save 🎨 to', filename);
return page.screenshot(clip !== undefined ? { path: filename, clip: clip } : { path: filename, fullPage: true });
}
const screenshotElement = async (pathes, selector, golden) => {
await ensureExistsFromRoot(golden ? path.resolve(...goldenRootPathes) : path.resolve(...screenshotRootPathes), pathes)
const filename = golden ? getGoldenScreenshotPath(pathes) : getScrenshotPath(pathes);
const frame = await page.frames().find(f => f.name() === 'context');
const element = await frame.$(selector);
await element.screenshot({ path: filename });
}

await page.exposeFunction('matchPageSnapshot', (pathes, clip, exluded) => {
return new Promise(async (resolve, reject) => {
var update = flags.indexOf("--snapshot-update") != -1;
if (update) {
await screenshotPage(pathes, true, clip);
resolve(true)
} else {
const snapshotExists = await fileExists(getGoldenScreenshotPath(pathes));
if (!snapshotExists) {
await screenshotPage(pathes, true, clip)
resolve(true)
} else {
await screenshotPage(pathes, false, clip)
resolve(await compareScreenshots(pathes, exluded));
}
}
})
})
await page.exposeFunction('matchElementSnapshot', (pathes, selector, excluded) => {
return new Promise(async (resolve, reject) => {
var update = flags.indexOf("--snapshot-update") != -1;
if (update) {
await screenshotElement(pathes, selector, true)
resolve(true)
} else {
const snapshotExists = await fileExists(getGoldenScreenshotPath(pathes));
if (!snapshotExists) {
await screenshotElement(pathes, selector, true)
resolve(true)
} else {
await screenshotElement(pathes, selector, false)
resolve(await compareScreenshots(pathes, excluded))
}
}
})
})

await page.goto(url);

}
Expand Down
12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "karma-puppeteer-launcher",
"version": "1.0.3",
"version": "1.0.4",
"description": "A Karma plugin. Launcher for Puppeteer Chrome.",
"main": "index.js",
"repository": {
Expand All @@ -15,11 +15,13 @@
],
"author": "Roman Samec <roman.samec2@gmail.com>",
"dependencies": {
"dynamicpixelmatch": "0.0.2",
"fs-access": "^1.0.0",
"which": "^1.2.1",
"puppeteer-core": "^1.11.0"
"pixelmatch": "^4.0.2",
"pngjs": "^3.3.3",
"puppeteer-core": "^1.11.0",
"which": "^1.2.1"
},
"license": "MIT",
"devDependencies": {
}
"devDependencies": {}
}

0 comments on commit dd4433e

Please sign in to comment.