Writing manifests
Test-runner integrations use ManifestWriter: a builder bound to a file that rewrites it atomically on every change. For runs with thousands of screenshots pass { writeDelayMs: 200 } as the third argument to coalesce the rewrites; pending changes are written on flush() and when the process exits (not if it is killed outright, which is why the default is 0). A passed entry never lists .actual or .diff files, even when leftovers of an earlier run sit next to it, and test files are recorded normalised (./a.spec.ts and a.spec.ts are one file).
import {
ManifestWriter,
getManifestFileName,
} from 'visual-regression-manifest';
const writer = new ManifestWriter(
`output/${getManifestFileName('my-runner')}`,
{
projectRoot: process.cwd(),
tool: { name: 'my-visual-plugin', version: '1.0.0' }, // what writes the file
runner: { name: 'my-runner', version: '2.0.0' }, // what runs the tests; add any runner-specific keys
options: { threshold: 0.01 }, // your global options, verbatim
// platform and ci are detected (GitHub Actions, GitLab CI) unless given
},
);
writer.record({
actualPath: 'shots/home.actual.png', // also the key: recording it again replaces the entry
baselinePath: 'shots/home.png',
// diffPath defaults to the `.diff` sibling; pass `null` when you write none
testFile: 'e2e/home.spec.ts',
titlePath: ['home', 'renders'],
retry: 0, // a retry replaces the entries of the earlier attempts of that test
status: 'failed',
diffRatio: 0.02,
threshold: 0.01,
baselineWritten: false,
duration: 840,
actualSize: { width: 1280, height: 720 },
platform: {
osPlatform: process.platform,
browser: { name: 'chromium', version: '130' },
},
attachments: [
{ name: 'trace', contentType: 'application/zip', path: 'trace.zip' },
],
message: '2% of pixels differ',
});
writer.approve({ actualPath: 'shots/home.actual.png' }); // after moving the actual over the baseline
writer.dropTestFile('e2e/home.spec.ts'); // before re-running a file in an interactive runner
writer.reset(); // start of a run: forget everything, delete the file
ManifestBuilder is the same without the file (toJSON() gives the manifest), and writeManifestFile is the atomic write on its own. The writer computes project-relative paths, nulls images that do not exist, hashes the ones that do, derives a native renderer from platform, keeps summary up to date and sorts entries by test file and name.