|
| 1 | +import assert from "node:assert/strict" |
| 2 | +import { once } from "node:events" |
| 3 | +import { readFile, writeFile } from "node:fs/promises" |
| 4 | +import { createServer } from "node:http" |
| 5 | +import { join } from "node:path" |
| 6 | + |
| 7 | +import { PNG } from "pngjs" |
| 8 | + |
| 9 | +import { runVisualCompareCommand } from "../packages/runtime-playground/dist/browser-visual-compare.js" |
| 10 | +import { withTempDir } from "../scripts/test-kit.js" |
| 11 | + |
| 12 | +const snapshot = (url: string, title: string) => ({ |
| 13 | + url, |
| 14 | + title, |
| 15 | + elementCount: 1, |
| 16 | + capturedElements: [{ path: "main", tag: "main", text: title, attributes: {}, boundingBox: { x: 0, y: 0, width: 1, height: 1 }, styles: { display: "block" } }], |
| 17 | + truncated: false, |
| 18 | +}) |
| 19 | + |
| 20 | +const domSnapshotArtifact = (url: string, title: string) => ({ |
| 21 | + schema: "wp-codebox/browser-dom-snapshot/v1" as const, |
| 22 | + command: "wordpress.browser-actions" as const, |
| 23 | + screenshot: "input.png", |
| 24 | + finalUrl: url, |
| 25 | + viewport: null, |
| 26 | + capturedAt: "2026-01-01T00:00:00.000Z", |
| 27 | + limits: { maxElements: 1 }, |
| 28 | + summary: { elementCount: 1, capturedElements: 1, truncated: false }, |
| 29 | + snapshot: snapshot(url, title), |
| 30 | +}) |
| 31 | + |
| 32 | +async function writePng(path: string): Promise<void> { |
| 33 | + const png = new PNG({ width: 1, height: 1 }) |
| 34 | + png.data.set([255, 255, 255, 255]) |
| 35 | + await writeFile(path, PNG.sync.write(png)) |
| 36 | +} |
| 37 | + |
| 38 | +async function visualCompare(artifactRoot: string, args: string[]) { |
| 39 | + return runVisualCompareCommand({ |
| 40 | + artifactRoot, |
| 41 | + server: { |
| 42 | + serverUrl: "http://127.0.0.1:1", |
| 43 | + playground: { run: async () => ({ text: "" }) }, |
| 44 | + async [Symbol.asyncDispose]() {}, |
| 45 | + }, |
| 46 | + spec: { command: "wordpress.visual-compare", args }, |
| 47 | + }) |
| 48 | +} |
| 49 | + |
| 50 | +await withTempDir("wp-codebox-visual-dom-snapshots-", async (artifactRoot) => { |
| 51 | + const sourceScreenshot = join(artifactRoot, "input-source.png") |
| 52 | + const candidateScreenshot = join(artifactRoot, "input-candidate.png") |
| 53 | + const sourceSidecar = join(artifactRoot, "source-sidecar.json") |
| 54 | + const candidateSidecar = join(artifactRoot, "candidate-sidecar.json") |
| 55 | + await Promise.all([writePng(sourceScreenshot), writePng(candidateScreenshot)]) |
| 56 | + await writeFile(sourceSidecar, JSON.stringify(domSnapshotArtifact("https://source.example.test/", "Source"))) |
| 57 | + await writeFile(candidateSidecar, JSON.stringify(domSnapshotArtifact("https://candidate.example.test/", "Candidate"))) |
| 58 | + |
| 59 | + const supplied = await visualCompare(artifactRoot, [ |
| 60 | + `source-screenshot=${sourceScreenshot}`, |
| 61 | + `candidate-screenshot=${candidateScreenshot}`, |
| 62 | + `source-dom-snapshot=${sourceSidecar}`, |
| 63 | + `candidate-dom-snapshot=${candidateSidecar}`, |
| 64 | + ]) |
| 65 | + const suppliedSummary = JSON.parse(supplied.output) |
| 66 | + assert.equal(suppliedSummary.schema, "wp-codebox/visual-compare/v1") |
| 67 | + assert.equal(suppliedSummary.files.sourceDomSnapshot, "files/browser/visual-compare/source-dom-snapshot.json") |
| 68 | + assert.equal(suppliedSummary.files.candidateDomSnapshot, "files/browser/visual-compare/candidate-dom-snapshot.json") |
| 69 | + const persistedSource = JSON.parse(await readFile(join(artifactRoot, suppliedSummary.files.sourceDomSnapshot), "utf8")) |
| 70 | + const persistedCandidate = JSON.parse(await readFile(join(artifactRoot, suppliedSummary.files.candidateDomSnapshot), "utf8")) |
| 71 | + assert.equal(persistedSource.schema, "wp-codebox/browser-dom-snapshot/v1") |
| 72 | + assert.equal(persistedSource.command, "wordpress.visual-compare") |
| 73 | + assert.equal(persistedSource.screenshot, "files/browser/visual-compare/source.png") |
| 74 | + assert.deepEqual(persistedSource.snapshot, snapshot("https://source.example.test/", "Source")) |
| 75 | + assert.equal(persistedCandidate.schema, "wp-codebox/browser-dom-snapshot/v1") |
| 76 | + assert.equal(persistedCandidate.screenshot, "files/browser/visual-compare/candidate.png") |
| 77 | + assert.equal(persistedCandidate.finalUrl, "https://candidate.example.test/") |
| 78 | + |
| 79 | + await withTempDir("wp-codebox-visual-dom-snapshots-screenshot-only-", async (screenshotOnlyRoot) => { |
| 80 | + const screenshotOnly = await visualCompare(screenshotOnlyRoot, [`source-screenshot=${sourceScreenshot}`, `candidate-screenshot=${candidateScreenshot}`]) |
| 81 | + const screenshotOnlySummary = JSON.parse(screenshotOnly.output) |
| 82 | + assert.equal("sourceDomSnapshot" in screenshotOnlySummary.files, false) |
| 83 | + assert.equal("candidateDomSnapshot" in screenshotOnlySummary.files, false) |
| 84 | + }) |
| 85 | +}) |
| 86 | + |
| 87 | +const page = createServer((_request, response) => { |
| 88 | + response.writeHead(200, { "content-type": "text/html" }) |
| 89 | + response.end("<!doctype html><title>URL snapshot</title><main>URL snapshot</main>") |
| 90 | +}) |
| 91 | +page.listen(0, "127.0.0.1") |
| 92 | +await once(page, "listening") |
| 93 | +try { |
| 94 | + const address = page.address() |
| 95 | + if (!address || typeof address === "string") { |
| 96 | + throw new Error("test server did not expose a TCP address") |
| 97 | + } |
| 98 | + await withTempDir("wp-codebox-visual-dom-url-capture-", async (artifactRoot) => { |
| 99 | + const url = `http://127.0.0.1:${address.port}/` |
| 100 | + const result = await visualCompare(artifactRoot, [`source-url=${url}`, `candidate-url=${url}`]) |
| 101 | + const summary = JSON.parse(result.output) |
| 102 | + const source = JSON.parse(await readFile(join(artifactRoot, summary.files.sourceDomSnapshot), "utf8")) |
| 103 | + const candidate = JSON.parse(await readFile(join(artifactRoot, summary.files.candidateDomSnapshot), "utf8")) |
| 104 | + assert.equal(source.schema, "wp-codebox/browser-dom-snapshot/v1") |
| 105 | + assert.equal(source.finalUrl, url) |
| 106 | + assert.equal(source.snapshot.title, "URL snapshot") |
| 107 | + assert.equal(candidate.snapshot.url, url) |
| 108 | + }) |
| 109 | +} finally { |
| 110 | + page.close() |
| 111 | +} |
| 112 | + |
| 113 | +console.log("browser visual compare DOM snapshots passed") |
0 commit comments