/** * How much two screenshots differ, and where. * * A ruling used to know only whether the pixels moved: `sha256(before) !== * sha256(after)`, a boolean in which a one-pixel nudge and a whole re-layout * are the same answer. That is enough to hold the guard that says nothing may * pass on unchanged pixels, and not enough to say anything to the person * reading the verdict about what the fix actually did. * * The comparison is exact: every channel, no tolerance, alpha ignored. lookout * captures with animations disabled at a fixed device scale, so two runs of an * unchanged page are byte-identical; a tolerance would only let a real change * hide under it. That is also why no perceptual metric is used here: SSIM and * its relatives score a one-pixel misalignment at ~1.0, which is exactly the * defect class this repository exists to file. * * The algorithm was `tools/ui-check`'s, where it found three real bugs that * every other gate passed over. It is promoted here rather than copied, and * that tool now imports it. */ export interface PixelBox { x: number; y: number; w: number; h: number; } export interface PixelDiff { /** Pixels differing, including every pixel one image has and the other does not. */ changed: number; /** Pixels in the union of both images: max width x max height. */ total: number; /** changed / total, 0 to 1. */ fraction: number; /** The smallest rectangle containing every difference; null when there are none. */ box: PixelBox | null; /** * changed / box area. Near 1 means one solid region moved; small means the * change is scattered, which is what a re-layout looks like. */ density: number; before: { width: number; height: number; }; after: { width: number; height: number; }; /** The images are not the same size, which is itself a change. */ sizeChanged: boolean; } /** * Compare two PNGs. Never throws: an image that will not decode returns null, * which callers read as "changed, and by how much was not measured" rather * than as "unchanged". * * Different dimensions are the common re-layout case, not an error. The * overlapping region is compared pixel by pixel and everything outside it * counts as changed, so a page that grew reports the growth rather than * refusing to answer. */ export declare function diffPng(before: Buffer | string, after: Buffer | string): Promise; /** * What changed, as a sentence for the verdict. Says the shape of the change, * not just its size: one region moving and the same count scattered across the * frame mean different things to whoever is reading the ruling. */ export declare function changeSaid(d: PixelDiff): string; /** * The changed region of the after image, cropped and enlarged. Best effort: * returns null rather than costing a ruling its verdict, because a picture of * the change is a convenience and the measurement is the evidence. */ export declare function writeDiffCrop(afterPng: string, box: PixelBox, outPath: string): Promise;