diff --git a/src/commands.ts b/src/commands.ts index ef9f670f..060a031a 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -1,6 +1,7 @@ import { FILE_SUFFIX, LINK_PREFIX, TASK } from "./constants"; import type pixelmatch from "pixelmatch"; import * as Base64 from "@frsource/base64"; +import type { CompareImagesTaskReturn } from "./types"; declare global { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -116,7 +117,7 @@ Cypress.Commands.add( }) .then((imgPath) => cy - .task( + .task( TASK.compareImages, { scaleFactor, @@ -129,12 +130,7 @@ Cypress.Commands.add( { log: false } ) .then((res) => ({ - res: res as null | { - error?: boolean; - message?: string; - imgDiff?: number; - maxDiffThreshold?: number; - }, + res, imgPath, })) ) @@ -150,22 +146,29 @@ Cypress.Commands.add( throw constructCypressError(log, new Error("Unexpected error!")); } + log.set( + "message", + `${res.message}${ + res.imgDiffBase64 && res.imgNewBase64 && res.imgOldBase64 + ? `\n[See comparison](${LINK_PREFIX}${Base64.encode( + encodeURIComponent( + JSON.stringify({ + title, + imgPath, + imgDiffBase64: res.imgDiffBase64, + imgNewBase64: res.imgNewBase64, + imgOldBase64: res.imgOldBase64, + error: res.error + }) + ) + )})` + : '' + }` + ); + if (res.error) { - log.set( - "message", - `${res.message}\n[See comparison](${LINK_PREFIX}${Base64.encode( - encodeURIComponent( - JSON.stringify({ - title, - imgPath, - }) - ) - )})` - ); log.set("consoleProps", () => res); throw constructCypressError(log, new Error(res.message)); - } else { - log.set("message", res.message); } }); } diff --git a/src/support.ts b/src/support.ts index 9eb54517..2e2a6e09 100644 --- a/src/support.ts +++ b/src/support.ts @@ -22,21 +22,23 @@ export const generateOverlayTemplate = ({ imgOldBase64, imgDiffBase64, wasImageNotUpdatedYet, + error, }: { title: string; imgNewBase64: string; imgOldBase64: string; imgDiffBase64: string; wasImageNotUpdatedYet: boolean; + error: boolean }) => `
`; -export function cachedReadFile( - imageCache: Record, - path: string, - encoding: Cypress.Encodings -): Cypress.Chainable { - if (imageCache[path]) - return Cypress.Promise.resolve( - imageCache[path] - ) as unknown as Cypress.Chainable; - - return cy - .readFile(path, encoding, { log: false }) - .then((file) => (imageCache[path] = file)); -} - /* c8 ignore start */ before(() => { if (!top) return null; @@ -86,8 +73,6 @@ before(() => { }); after(() => { - const imageCache: Record = {}; - if (!top) return null; Cypress.$(top.document.body).on( @@ -97,7 +82,7 @@ after(() => { e.preventDefault(); if (!top) return false; - const { title, imgPath } = JSON.parse( + const { title, imgPath, imgDiffBase64, imgNewBase64, imgOldBase64, error } = JSON.parse( decodeURIComponent( Base64.decode( e.currentTarget.getAttribute("href").substring(LINK_PREFIX.length) @@ -106,39 +91,10 @@ after(() => { ); queueClear(); - cachedReadFile(imageCache, imgPath, "base64") - .then((imgNew) => - cachedReadFile( - imageCache, - imgPath.replace(FILE_SUFFIX.actual, ""), - "base64" - ).then((imgOld) => - cachedReadFile( - imageCache, - imgPath.replace(FILE_SUFFIX.actual, FILE_SUFFIX.diff), - "base64" - ).then((imgDiff) => - cy - .task(TASK.doesFileExist, { path: imgPath }, { log: false }) - .then( - (wasImageNotUpdatedYet) => - [ - imgNew, - imgOld, - imgDiff, - wasImageNotUpdatedYet as boolean, - ] as const - ) - ) - ) - ) + cy + .task(TASK.doesFileExist, { path: imgPath }, { log: false }) .then( - ([ - imgNewBase64, - imgOldBase64, - imgDiffBase64, - wasImageNotUpdatedYet, - ]) => { + (wasImageNotUpdatedYet) => { if (!top) return false; queueClear(); @@ -148,6 +104,7 @@ after(() => { imgNewBase64, imgOldBase64, imgDiffBase64, + error, wasImageNotUpdatedYet, }) ).appendTo(top.document.body); diff --git a/src/task.hook.ts b/src/task.hook.ts index 5bdd89b0..a428e16e 100644 --- a/src/task.hook.ts +++ b/src/task.hook.ts @@ -6,6 +6,7 @@ import moveFile from "move-file"; import sanitize from "sanitize-filename"; import { FILE_SUFFIX, IMAGE_SNAPSHOT_PREFIX, TASK } from "./constants"; import { alignImagesToSameSize, importAndScaleImage } from "./image.utils"; +import type { CompareImagesTaskReturn } from "./types"; export type CompareImagesCfg = { scaleFactor: number; @@ -54,14 +55,10 @@ export const approveImageTask = ({ img }: { img: string }) => { export const compareImagesTask = async ( cfg: CompareImagesCfg -): Promise => { +): Promise => { const messages = [] as string[]; let imgDiff: number | undefined; + let imgNewBase64: string, imgOldBase64: string, imgDiffBase64: string; let error = false; if (fs.existsSync(cfg.imgOld) && !cfg.updateImages) { @@ -107,15 +104,23 @@ export const compareImagesTask = async ( error = true; } + const diffBuffer = PNG.sync.write(diff); + imgNewBase64 = PNG.sync.write(imgNew).toString('base64'); + imgDiffBase64 = diffBuffer.toString('base64'); + imgOldBase64 = PNG.sync.write(imgOld).toString('base64'); + if (error) { fs.writeFileSync( cfg.imgNew.replace(FILE_SUFFIX.actual, FILE_SUFFIX.diff), - PNG.sync.write(diff) + diffBuffer ); return { error, message: messages.join("\n"), imgDiff, + imgNewBase64, + imgDiffBase64, + imgOldBase64, maxDiffThreshold: cfg.maxDiffThreshold, }; } else { @@ -125,6 +130,9 @@ export const compareImagesTask = async ( } else { // there is no "old screenshot" or screenshots should be immediately updated imgDiff = 0; + imgNewBase64 = ''; + imgDiffBase64 = ''; + imgOldBase64 = ''; moveFile.sync(cfg.imgNew, cfg.imgOld); } @@ -139,6 +147,9 @@ export const compareImagesTask = async ( return { message: messages.join("\n"), imgDiff, + imgNewBase64, + imgDiffBase64, + imgOldBase64, maxDiffThreshold: cfg.maxDiffThreshold, }; } diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 00000000..845eb0ec --- /dev/null +++ b/src/types.ts @@ -0,0 +1,9 @@ +export type CompareImagesTaskReturn = null | { + error?: boolean; + message?: string; + imgDiff?: number; + imgNewBase64?: string; + imgDiffBase64?: string; + imgOldBase64?: string; + maxDiffThreshold?: number; +};