UNPKG

wdio-visual-regression-tracker-service

Version:

WDIO Service for the Visual Regression Tracker

173 lines (145 loc) 6.47 kB
import * as fs from 'fs'; import logger from '@wdio/logger'; import { VisualRegressionTracker, Config, TestStatus, BuildResponse } from '@visual-regression-tracker/sdk-js'; import TestRunResult from '@visual-regression-tracker/sdk-js/dist/testRunResult'; import VrtOptions from './VrtOptions'; export interface VrtTrackOptions { diffTolerancePercent?: number; os?: string; browser?: string; viewport?: string; device?: string; ignoreAreas?: [ { x: number; y: number; width: number; height: number; }, ]; captureFullPage?: boolean; } export default class WDIOServiceService { private options: VrtOptions; private vrt: VisualRegressionTracker; private log: { info: (msg: string) => void; debug: (msg: string) => void; error: (msg: string) => void; warn: (msg: string) => void; }; private browser: WebdriverIO.Browser; private vrtCiName: string; constructor(serviceOptions: VrtOptions, _caps, config: Partial<{ vrtCiName: string }>) { this.options = serviceOptions; this.log = logger('visual-regression-tracker-service'); this.log.info('Visual Regression Tracker Service Added'); this.log.debug(`Using Visual Regression Tracker config ${this.options as Config}`); this.vrtCiName = config.vrtCiName; } async takeScreenshotFullPage(): Promise<string> { // Full credits go to https://www.automatetheplanet.com/full-page-screenshots-webdriver-html2canvas/ // for this implementation const isHtml2CanvasRegistered = async (): Promise<boolean> => { try { const result: boolean = await browser.execute('return typeof html2canvas !== "undefined"'); return result; } catch { return false; } }; if (!(await isHtml2CanvasRegistered())) { const html2canvasjs = fs .readFileSync(require.resolve('html2canvas').replace('html2canvas.js', 'html2canvas.min.js')) .toString(); await browser.execute(html2canvasjs); } await browser.execute(` function genScreenshot() { let canvasImgContentDecoded; html2canvas(document.body).then(function (canvas) { window.canvasImgContentDecoded = canvas.toDataURL('image/png'); }); } genScreenshot(); `); await browser.waitUntil(async () => { try { const result: boolean = await browser.execute('return typeof canvasImgContentDecoded !== "undefined"'); return result; } catch { return false; } }); const image: string = await browser.execute('return canvasImgContentDecoded'); const validImageBase64 = image.replace('data:image/png;base64,', ''); return validImageBase64; } async before(_caps: unknown, _specs: string[], browser: WebdriverIO.Browser): Promise<BuildResponse> { this.browser = browser; this.log.debug('Connecting to Visual Regression Tracker'); this.vrt = new VisualRegressionTracker({ ...this.options, ciBuildId: this.vrtCiName }); this.log.debug('Adding vrtSnapshotPage command to browser'); this.browser.addCommand( 'vrtTrackPage', async (name: string, options?: VrtTrackOptions): Promise<TestRunResult> => { const imageBase64 = options?.captureFullPage ? await this.takeScreenshotFullPage() : await browser.takeScreenshot(); this.log.debug(`Uploading snapshot for test ${name}`); const result: TestRunResult = await this.vrt.track({ name, imageBase64, diffTollerancePercent: options?.diffTolerancePercent || this.options.diffTolerancePercent, os: options?.os, browser: options?.browser, viewport: options?.viewport, device: options?.device, ignoreAreas: options?.ignoreAreas, }); if (result.testRunResponse.status === TestStatus.unresolved) { this.log.warn( `Snapshot for ${name} showed ${result.testRunResponse.pixelMisMatchCount} pixel mismatch count.\nView the result at ${result.testRunResponse.url}`, ); } else if (result.testRunResponse.status === TestStatus.new) { this.log.warn( `Snapshot for ${name} has no baseline image set\nUpdate the baseline at ${result.testRunResponse.url}`, ); } this.log.debug(`Test ${name} returned with status ${result.testRunResponse.status}`); return result; }, ); this.browser.addCommand( 'vrtTrackElement', async function (name: string, options?: VrtTrackOptions): Promise<TestRunResult> { const vrt = await (browser as any).vrtInstance(); if (!this.isExisting()) { throw new Error(`Unable to find element ${this} for snapshot test ${name}`); } const imageBase64 = await browser.takeElementScreenshot(this.elementId); const result: TestRunResult = await vrt.track({ name, imageBase64, diffTollerancePercent: options?.diffTolerancePercent || this.options.diffTolerancePercent, os: options?.os, browser: options?.browser, viewport: options?.viewport, device: options?.device, ignoreAreas: options?.ignoreAreas, }); return result; }, true, ); this.browser.addCommand('vrtInstance', (): VisualRegressionTracker => { return this.vrt; }); this.log.info('Starting Visual Regression Tracker'); return this.vrt.start(); } async after(): Promise<void> { this.log.info('Stopping Visual Regression Tracker'); return this.vrt.stop(); } }