UNPKG

protractor-screenshot-extension

Version:

An extension for Protractor that allows testing visual screenshots

162 lines (159 loc) 7.32 kB
import { __awaiter } from 'tslib'; import { existsSync, createWriteStream, readFileSync } from 'fs'; import { PNG } from 'pngjs'; import { browser } from 'protractor'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ /** @type {?} */ const pixelmatch = require('pixelmatch'); /** @type {?} */ const shell = require('shelljs'); /** @type {?} */ const actualDirectory = 'actual'; /** @type {?} */ const diffDirectory = 'diff'; /** @type {?} */ const baselineDirectory = 'baseline'; class ProtractorScreenshotExtension { /** * @param {?} screenshotDirectory */ constructor(screenshotDirectory) { this._screenshotDirectory = screenshotDirectory; if (this._screenshotDirectory.slice(-1) === '/') { this._screenshotDirectory = this._screenshotDirectory.slice(0, -1); } // Create all 3 subfolders. shell.mkdir('-p', `${screenshotDirectory}/${actualDirectory}`); shell.mkdir('-p', `${screenshotDirectory}/${diffDirectory}`); shell.mkdir('-p', `${screenshotDirectory}/${baselineDirectory}`); } /** * @param {?} element * @param {?} tag * @param {?=} options * @return {?} */ checkElementScreenshot(element, tag, options) { return __awaiter(this, void 0, void 0, function* () { /** @type {?} */ const baselineImagePath = `${this._screenshotDirectory}/${baselineDirectory}/${tag}.png`; if (!existsSync(baselineImagePath)) { console.log(`Saving baseline image: ${baselineImagePath}`); /** @type {?} */ let screenshot = Buffer.from(yield element.takeScreenshot(), 'base64'); screenshot = yield this._addBlackoutRectangles(screenshot, options, element); /** @type {?} */ const stream = createWriteStream(baselineImagePath); stream.write(screenshot); stream.end(); return 0; } /** @type {?} */ let testScreenshotBuffer = Buffer.from(yield element.takeScreenshot(), 'base64'); testScreenshotBuffer = yield this._addBlackoutRectangles(testScreenshotBuffer, options, element); /** @type {?} */ const testScreenshotPng = PNG.sync.read(testScreenshotBuffer); /** @type {?} */ const baselineScreenshotPng = PNG.sync.read(readFileSync(baselineImagePath)); /** @type {?} */ const diffPng = new PNG({ width: baselineScreenshotPng.width, height: baselineScreenshotPng.height }); /** @type {?} */ const numberOfPixelsDifferent = pixelmatch(testScreenshotPng.data, baselineScreenshotPng.data, diffPng.data, baselineScreenshotPng.width, baselineScreenshotPng.height, { threshold: options ? options.threshold : undefined, includeAA: options ? options.includeAA : undefined }); if (!!numberOfPixelsDifferent) { /** @type {?} */ const diffImagePath = `${this._screenshotDirectory}/${diffDirectory}/${tag}.png`; /** @type {?} */ const actualImagePath = `${this._screenshotDirectory}/${actualDirectory}/${tag}.png`; createWriteStream(actualImagePath).write(testScreenshotBuffer); diffPng.pack().pipe(createWriteStream(diffImagePath)); } return numberOfPixelsDifferent; }); } /** * @param {?} tag * @param {?=} options * @return {?} */ checkPageScreenshot(tag, options) { return this.checkElementScreenshot(browser, tag, options); } /** * @private * @param {?} image * @param {?} x * @param {?} y * @param {?} w * @param {?} h * @return {?} */ _blackoutRectangle(image, x, y, w, h) { // Create a black image that will be used for blacking out regions to be ignored. /** @type {?} */ const blackPng = new PNG({ width: w, height: h }); for (let i = 0; i < blackPng.height; i++) { for (let j = 0; j < blackPng.width; j++) { /** @type {?} */ const idx = (blackPng.width * i + j) * 4; blackPng.data[idx] = 0; // red blackPng.data[idx + 1] = 0; // blue blackPng.data[idx + 2] = 0; // green blackPng.data[idx + 3] = 255; // alpha } } blackPng.bitblt(image, 0, 0, w, h, x, y); } /** * @private * @param {?} imageBuffer * @param {?=} options * @param {?=} parentElement * @return {?} */ _addBlackoutRectangles(imageBuffer, options, parentElement) { return __awaiter(this, void 0, void 0, function* () { if (options && (options.ignoreRectangles || options.ignoreElements)) { /** @type {?} */ const imagePng = PNG.sync.read(imageBuffer); if (options.ignoreRectangles && options.ignoreRectangles.length) { options.ignoreRectangles.forEach((rect) => { this._blackoutRectangle(imagePng, rect.x, rect.y, rect.w, rect.h); }); } if (options.ignoreElements && options.ignoreElements.length) { // Using `map` instead of `forEach` in order to await all async calls. yield Promise.all(options.ignoreElements.map((ignoreElement) => __awaiter(this, void 0, void 0, function* () { /** @type {?} */ let parentLocation = { x: 0, y: 0 }; if (!!parentElement.getLocation) { // If an ElementFinder was passed in, call `getLocation()`, otherwise assume the whole browser was passed in. parentLocation = yield parentElement.getLocation(); } /** @type {?} */ const ignoreLocation = yield ignoreElement.getLocation(); /** @type {?} */ const ignoreSize = yield ignoreElement.getSize(); // Adjust coordinates, as the screenshot can be twice the viewport size for high resolution displays. /** @type {?} */ const parentSize = parentElement.driver ? yield parentElement.driver.manage().window().getSize() : yield parentElement.getSize(); /** @type {?} */ const conversionFactor = imagePng.width / parentSize.width; this._blackoutRectangle(imagePng, (ignoreLocation.x - parentLocation.x) * conversionFactor, (ignoreLocation.y - parentLocation.y) * conversionFactor, ignoreSize.width * conversionFactor, ignoreSize.height * conversionFactor); }))); } return PNG.sync.write(imagePng); } return imageBuffer; }); } } export { ProtractorScreenshotExtension }; //# sourceMappingURL=protractor-screenshot-extension.js.map