diff --git a/packages/cli/src/commands/snapshot-contactSheet.test.ts b/packages/cli/src/commands/snapshot-contactSheet.test.ts index 296d19cfe2..693278df58 100644 --- a/packages/cli/src/commands/snapshot-contactSheet.test.ts +++ b/packages/cli/src/commands/snapshot-contactSheet.test.ts @@ -16,6 +16,11 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +const errorsBesideProgress = () => + vi + .mocked(console.error) + .mock.calls.filter(([line]) => !String(line).startsWith("[hyperframes] snapshot:")); + const capture = vi.hoisted(() => ({ evaluate: vi.fn(), screenshot: vi.fn(async () => Buffer.from("captured PNG")), @@ -123,7 +128,7 @@ describe("snapshot contact sheet failures", () => { }), ).resolves.toBeDefined(); - expect(console.error).not.toHaveBeenCalled(); + expect(errorsBesideProgress()).toEqual([]); for (const [index, time] of ["0.5", "1"].entries()) { const frame = join(project, "snapshots", `frame-0${index}-at-${time}s.png`); expect(readFileSync(frame)).toEqual(Buffer.from("captured PNG")); @@ -161,7 +166,7 @@ describe("snapshot contact sheet failures", () => { ); expect(console.log).toHaveBeenCalledWith(expect.stringContaining("contact-sheet.jpg")); expect(console.warn).not.toHaveBeenCalled(); - expect(console.error).not.toHaveBeenCalled(); + expect(errorsBesideProgress()).toEqual([]); }); it("builds the sheet from this run's frames, not another frame-*.png in the folder", async () => { @@ -262,7 +267,7 @@ describe("snapshot contact sheet failures", () => { } expect(readdirSync(out).filter((name) => name.startsWith("pair-"))).toEqual([]); expect(pairSheet).toHaveBeenCalledOnce(); - expect(console.error).toHaveBeenCalledOnce(); + expect(errorsBesideProgress()).toHaveLength(1); expect(console.error).toHaveBeenCalledWith( expect.stringContaining("Reference pair sheets skipped: Windows App Control"), ); @@ -325,7 +330,7 @@ describe("snapshot contact sheet failures", () => { join(project, "snapshots", `frame-0${index}-at-${time}s.png`), ), ); - expect(console.error).not.toHaveBeenCalled(); + expect(errorsBesideProgress()).toEqual([]); expect(console.warn).not.toHaveBeenCalledWith(expect.stringContaining("not recorded")); } finally { historyDeps.ownerWaitMs = previousWait; diff --git a/packages/cli/src/commands/snapshot.progress.browser.test.ts b/packages/cli/src/commands/snapshot.progress.browser.test.ts new file mode 100644 index 0000000000..c26468ee8c --- /dev/null +++ b/packages/cli/src/commands/snapshot.progress.browser.test.ts @@ -0,0 +1,48 @@ +import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterAll, expect, it, vi } from "vitest"; +import snapshotCommand from "./snapshot.js"; + +const root = mkdtempSync(join(tmpdir(), "hf-snapshot-progress-")); + +afterAll(() => rmSync(root, { recursive: true, force: true })); + +it("prints a progress line on stderr as each frame starts, and keeps stdout one --json envelope", async () => { + writeFileSync( + join(root, "index.html"), + ` +
+`, + ); + const log = vi.spyOn(console, "log").mockImplementation(() => undefined); + const error = vi.spyOn(console, "error").mockImplementation(() => undefined); + vi.spyOn(console, "warn").mockImplementation(() => undefined); + try { + await snapshotCommand.run?.({ + args: { + dir: root, + at: "1,2.5", + end: false, + json: true, + output: join(root, "out"), + describe: "false", + "browser-gpu": false, + }, + } as never); + const progress = error.mock.calls + .map(([line]) => String(line)) + .filter((line) => line.startsWith("[hyperframes] snapshot:")); + + expect(progress).toEqual([ + "[hyperframes] snapshot: frame 1/2 at 1s", + "[hyperframes] snapshot: frame 2/2 at 2.5s", + ]); + expect(log).toHaveBeenCalledTimes(1); + expect(JSON.parse(String(log.mock.calls[0]?.[0])).frames).toHaveLength(2); + } finally { + vi.restoreAllMocks(); + } +}, 120_000); diff --git a/packages/cli/src/commands/snapshot.ts b/packages/cli/src/commands/snapshot.ts index 80789c4bc0..e282223152 100644 --- a/packages/cli/src/commands/snapshot.ts +++ b/packages/cli/src/commands/snapshot.ts @@ -930,6 +930,7 @@ async function captureSnapshots( for (let i = 0; i < positions.length; i++) { const time = positions[i]!; + console.error(`[hyperframes] snapshot: frame ${i + 1}/${positions.length} at ${time}s`); const seekOptions = { exactTime: true, waitForImagesMs: SCREENSHOT_IMAGE_WAIT_MS }; await atStep(`seeking to ${time}s, frame ${i + 1} of ${positions.length}`, () =>