import { createDiff as createDiffStub } from 'looks-same'; import { Image, colorToString, } from './image-utils'; import { ScreenshotLocations } from './file-utils'; import { IComparerOptions, IRGBColor, SelectorType } from './options'; import Diagnostics from './diagnostics'; import { takeScreenshot } from './take-screenshot'; function createDiff(options: Omit[0], 'reference' | 'current'> & { reference: Image, current: Image }): Promise { return new Promise((resolve, reject) => { createDiffStub({ ...options, reference: options.reference.getBuffer(), current: options.current.getBuffer(), }, (error, buffer) => { if (error) { reject(error); } else { resolve(new Image(buffer)); } }); }); } function setForeground(t: TestController): Promise { return t.eval(() => new Promise((resolve) => { const style = document.createElement('style'); style.id = '86049492-4199-4d8b-8596-12400c9d1dbd'; style.innerHTML = `* { color: #FF00FF !important; font-smooth: never; -webkit-font-smoothing: none; } svg text { fill: #FF00FF !important; }`; document.getElementsByTagName('head')[0].appendChild(style); setTimeout(resolve, 200); })); } function resetForeground(t: TestController): Promise { return t.eval(() => new Promise((resolve) => { document.getElementById('86049492-4199-4d8b-8596-12400c9d1dbd').remove(); setTimeout(resolve, 200); })); } export async function buildMask( ethalon: Image, current: Image, imageForMask: Image, options: IComparerOptions['looksSameComparisonOptions'], color: IRGBColor, radius: number, ): Promise<{ mask: Image, diff: Image }> { const diff = await createDiff({ ...options, reference: ethalon, current, highlightColor: colorToString(color), }); const maskDiff = await createDiff({ ...options, reference: ethalon, current: imageForMask, highlightColor: colorToString(color), }); const mask = maskDiff .as((x) => x.filter(color)) .do((x) => x.spreadBlack(radius)); return { mask, diff }; } export async function buildTextMask(options: IComparerOptions, attempt: number, t: TestController, element: SelectorType, locations: ScreenshotLocations, diag: Diagnostics, currentScreenshot: Image, emptyImage: Image, sourceScreenshot: Image) { if (!options.enableTextMask || attempt === 0) { return null; } await setForeground(t); await takeScreenshot(t, element, locations.tempText.filePath); await resetForeground(t); const coloredScreenshot = locations.tempText.read('file'); diag.log('1 temp foreground ', coloredScreenshot); const textMask = coloredScreenshot .as((x) => x.xor(currentScreenshot), (x) => diag.log('1.1-xor', x)) .do((x) => x.normalize(), (x) => diag.log('1.2-normalize', x)) .do((x) => x.multiply(255), (x) => diag.log('1.3-multiply', x)) .do((x) => x.invert(), (x) => diag.log('1.4-invert', x)) .do((x) => x.spreadBlack(options.textMaskRadius), (x) => diag.log('1.5-spread', x)) .do((x) => x, (x) => diag.log('2 text mask', x)); textMask.write(locations.tempText.maskPath); const invertedMask = textMask.as((x) => x.invert(), (x) => diag.log('3 inverted mask', x)); const tempTextArea = currentScreenshot.as((x) => x.replace(emptyImage, invertedMask), (x) => diag.log('4 temp text area', x)); const sourceTextArea = sourceScreenshot.as((x) => x.replace(emptyImage, invertedMask), (x) => diag.log('5 source text area', x)); const { mask: textAreaDiff } = await buildMask( tempTextArea, sourceTextArea, sourceTextArea, options.textComparisonOptions, { r: 255, g: 0, b: 255 }, 0, ); diag.log('6 text area diff', textAreaDiff); textAreaDiff.invert(); diag.log('7 inverted text area diff', textAreaDiff); const totalTextPixels = invertedMask.dataClone().reduce((a, b) => a + b, 0) - (sourceScreenshot.length / 4) * 255; const diffTextPixels = textAreaDiff.dataClone().reduce((a, b) => a + b, 0) - (sourceScreenshot.length / 4) * 255; const percentage = diffTextPixels / totalTextPixels; const treshold = options.textDiffTreshold; diag.log('8 properties', { totalTextPixels, diffTextPixels, percentage, treshold, }); if (percentage < treshold) { return textMask; } return null; }