@cevek/screentest 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,45 +7,59 @@ screenshots. Everything ships in one npm package — no copy-paste.
7
7
  ## Install
8
8
 
9
9
  ```bash
10
- pnpm add -D @cevek/screentest playwright vitest
11
- pnpm exec playwright install firefox
10
+ # pick your package manager — all three are supported:
11
+ npm i -D @cevek/screentest playwright vitest && npx playwright install firefox
12
+ # pnpm add -D @cevek/screentest playwright vitest && pnpm exec playwright install firefox
13
+ # yarn add -D @cevek/screentest playwright vitest && yarn playwright install firefox
12
14
  ```
13
15
 
14
16
  `playwright` and `vitest` are peer-deps — keep them in your devDependencies.
15
17
 
18
+ `screentest init` detects your package manager (from your lockfile or
19
+ `packageManager` field) and writes a matching `Dockerfile.tests`.
20
+
16
21
  ## Quick start
17
22
 
18
23
  ```bash
19
24
  # 1. Scaffold the test harness into your project.
20
- pnpm exec screentest init
25
+ npx screentest init
21
26
 
22
27
  # 2. Build the Docker image once.
23
28
  docker build -f Dockerfile.tests -t screentest-tests .
24
29
 
25
30
  # 3. Run.
26
- APP_URL=http://localhost:5050 pnpm exec screentest test
31
+ APP_URL=http://localhost:5050 npx screentest test
27
32
  ```
28
33
 
29
34
  `screentest init` creates:
30
35
 
31
36
  - `Dockerfile.tests` — playwright-noble + your project deps + vitest CMD
32
- - `vitest.config.ts` pre-wired with the package's globalSetup
37
+ (auto-picked for pnpm / npm / yarn based on your lockfile)
38
+ - `vitest.screentest.config.ts` — separate vitest config wired with the
39
+ package's globalSetup. Kept under its own name so your normal
40
+ `vitest.config.ts` (unit tests) is untouched.
33
41
  - `tests/example.test.ts` — minimal Playwright test using the runner helpers
34
42
 
35
43
  Existing files are not overwritten — re-run `init` safely.
36
44
 
45
+ `screentest test` always runs vitest with
46
+ `--config vitest.screentest.config.ts`, so unit tests via plain `vitest run`
47
+ and screenshot tests via `screentest test` stay completely separate.
48
+
37
49
  ## Commands
38
50
 
39
51
  ```bash
40
52
  screentest <doc-json-path> [--port 5174] [--no-open]
41
53
  [--worker-url URL] [--token TOKEN]
42
54
  ```
55
+
43
56
  Open the review UI on a pre-built `doc.json`. Used internally by `screentest
44
57
  test` and `screentest review`.
45
58
 
46
59
  ```bash
47
60
  screentest test [--host]
48
61
  ```
62
+
49
63
  Default workflow: run vitest **inside Docker** (network=host), and on failure
50
64
  auto-launch the review UI. `--host` runs vitest on the host instead (debug
51
65
  only — bytes diverge from your CI baseline). Set `CI=1` to skip the auto-UI
@@ -54,11 +68,13 @@ launch.
54
68
  ```bash
55
69
  screentest review
56
70
  ```
71
+
57
72
  Skip vitest, just regenerate `doc.json` from cached actuals and open the UI.
58
73
 
59
74
  ```bash
60
75
  screentest init
61
76
  ```
77
+
62
78
  Scaffold Dockerfile + vitest config + example test (see above).
63
79
 
64
80
  ## Writing tests
@@ -121,7 +137,7 @@ Or pass `--worker-url` / `--token` to `screentest` directly.
121
137
 
122
138
  ```yaml
123
139
  - run: docker build -f Dockerfile.tests -t screentest-tests .
124
- - run: CI=1 pnpm exec screentest test
140
+ - run: CI=1 npx screentest test
125
141
  ```
126
142
 
127
143
  In `CI=1` the orchestrator exits with vitest's code and never tries to open
package/dist/index.js CHANGED
@@ -15265,7 +15265,12 @@ async function runVitestInDocker(snapshotFile, cacheDir) {
15265
15265
  async function runOrchestrator(opts = {}) {
15266
15266
  const paths = resolveRunnerPaths();
15267
15267
  if (opts.reviewOnly) {
15268
- const total = await generateDoc(paths.snapshotFile, paths.actualDir, paths.docFile, paths.cacheDir);
15268
+ const total = await generateDoc(
15269
+ paths.snapshotFile,
15270
+ paths.actualDir,
15271
+ paths.docFile,
15272
+ paths.cacheDir
15273
+ );
15269
15274
  if (total === 0) {
15270
15275
  process.stdout.write("Nothing to review \u2014 all snapshots match.\n");
15271
15276
  return 0;
@@ -15281,7 +15286,7 @@ async function runOrchestrator(opts = {}) {
15281
15286
  await fs4.rm(paths.actualDir, { recursive: true, force: true });
15282
15287
  let testCode;
15283
15288
  if (opts.hostMode) {
15284
- testCode = await run("pnpm", ["exec", "vitest", "run"]);
15289
+ testCode = await run("npx", ["vitest", "--config", "vitest.screentest.config.ts", "run"]);
15285
15290
  } else {
15286
15291
  if (opts.requireDockerImage !== false && !ensureDockerImage()) {
15287
15292
  return 1;
@@ -15292,7 +15297,12 @@ async function runOrchestrator(opts = {}) {
15292
15297
  process.stderr.write(
15293
15298
  "\n\u2192 Tests failed \u2014 launching screentest for review.\n Close the tool (Ctrl+C) when done; the original failure will still propagate.\n\n"
15294
15299
  );
15295
- const total = await generateDoc(paths.snapshotFile, paths.actualDir, paths.docFile, paths.cacheDir);
15300
+ const total = await generateDoc(
15301
+ paths.snapshotFile,
15302
+ paths.actualDir,
15303
+ paths.docFile,
15304
+ paths.cacheDir
15305
+ );
15296
15306
  if (total > 0) {
15297
15307
  const reviewCode = await run("node", [process.argv[1], paths.docFile]);
15298
15308
  if (reviewCode !== 0 && reviewCode !== 130) {
@@ -15310,17 +15320,36 @@ async function runOrchestrator(opts = {}) {
15310
15320
  }
15311
15321
 
15312
15322
  // src/init.ts
15313
- import { copyFile, mkdir } from "fs/promises";
15323
+ import { copyFile, mkdir, readFile } from "fs/promises";
15314
15324
  import { existsSync as existsSync2 } from "fs";
15315
15325
  import { dirname as dirname4, join as join4, resolve as resolve6 } from "path";
15316
15326
  import { fileURLToPath as fileURLToPath2 } from "url";
15327
+ async function detectPackageManager(cwd) {
15328
+ if (existsSync2(join4(cwd, "pnpm-lock.yaml"))) return "pnpm";
15329
+ if (existsSync2(join4(cwd, "yarn.lock"))) return "yarn";
15330
+ if (existsSync2(join4(cwd, "package-lock.json"))) return "npm";
15331
+ try {
15332
+ const pkg = JSON.parse(
15333
+ await readFile(join4(cwd, "package.json"), "utf8")
15334
+ );
15335
+ const pm = pkg.packageManager ?? "";
15336
+ if (pm.startsWith("pnpm")) return "pnpm";
15337
+ if (pm.startsWith("yarn")) return "yarn";
15338
+ } catch {
15339
+ }
15340
+ return "npm";
15341
+ }
15317
15342
  async function runInit() {
15318
15343
  const here = dirname4(fileURLToPath2(import.meta.url));
15319
- const templatesDir = [resolve6(here, "../templates"), resolve6(here, "../../templates")].find((p) => existsSync2(p)) ?? resolve6(here, "../templates");
15344
+ const templatesDir = [resolve6(here, "./templates"), resolve6(here, "../templates")].find((p) => existsSync2(p)) ?? resolve6(here, "./templates");
15320
15345
  const cwd = process.cwd();
15346
+ const pm = await detectPackageManager(cwd);
15347
+ process.stdout.write(`Detected package manager: ${pm}
15348
+
15349
+ `);
15321
15350
  const targets = [
15322
- { from: "Dockerfile.tests", to: "Dockerfile.tests" },
15323
- { from: "vitest.config.ts", to: "vitest.config.ts" },
15351
+ { from: `Dockerfile.${pm}.tests`, to: "Dockerfile.tests" },
15352
+ { from: "vitest.screentest.config.ts", to: "vitest.screentest.config.ts" },
15324
15353
  { from: "example.test.ts", to: "tests/example.test.ts" }
15325
15354
  ];
15326
15355
  for (const { from, to } of targets) {
@@ -15345,7 +15374,7 @@ async function runInit() {
15345
15374
  `
15346
15375
  Next steps:
15347
15376
  1. docker build -f Dockerfile.tests -t screentest-tests .
15348
- 2. APP_URL=http://localhost:<your-app-port> pnpm exec screentest test
15377
+ 2. APP_URL=http://localhost:<your-app-port> npx screentest test
15349
15378
  `
15350
15379
  );
15351
15380
  return 0;
package/dist/runner.d.ts CHANGED
@@ -15,10 +15,7 @@ export function compareSnapshot(page: Page, name: string): Promise<void>;
15
15
  * Freeze `Date` inside `ctx`. `when` may be an ISO string, unix-ms number,
16
16
  * or `Date`. Must be called BEFORE the first navigation in the context.
17
17
  */
18
- export function freezeDate(
19
- ctx: BrowserContext,
20
- when: string | number | Date,
21
- ): Promise<void>;
18
+ export function freezeDate(ctx: BrowserContext, when: string | number | Date): Promise<void>;
22
19
 
23
20
  /**
24
21
  * Wait for `document.fonts.ready` and every `<img>` to reach `complete`.
@@ -0,0 +1,17 @@
1
+ FROM mcr.microsoft.com/playwright:v1.60.0-noble
2
+
3
+ WORKDIR /work
4
+
5
+ # Install dependencies. --ignore-scripts skips your project's postinstall
6
+ # hooks (which usually scan src/ — not needed for the test image).
7
+ COPY package.json package-lock.json ./
8
+ RUN npm ci --ignore-scripts
9
+
10
+ # Only the bits needed to run vitest.
11
+ COPY vitest.screentest.config.ts ./
12
+ COPY tsconfig*.json ./
13
+ COPY tests ./tests
14
+
15
+ # Run vitest directly (skipping `npm exec`) — keeps the test image simple
16
+ # and matches how the host orchestrator invokes it.
17
+ CMD ["./node_modules/.bin/vitest", "--config", "vitest.screentest.config.ts", "run"]
@@ -0,0 +1,28 @@
1
+ FROM mcr.microsoft.com/playwright:v1.60.0-noble
2
+
3
+ WORKDIR /work
4
+
5
+ # corepack lets us use whatever pnpm version your project's `packageManager`
6
+ # field pins (pnpm 11 by default in modern projects). If your project uses
7
+ # npm or yarn instead, swap the install commands accordingly.
8
+ RUN corepack enable
9
+
10
+ # Install dependencies. Flags:
11
+ # --ignore-scripts skips your project's postinstall hooks (which
12
+ # usually scan src/ — not needed for the test image).
13
+ # --config.minimumReleaseAge=0 bypass pnpm 11's supply-chain policy for
14
+ # freshly-published packages (the test image is
15
+ # ephemeral, runs only what's already in lockfile).
16
+ COPY package.json pnpm-lock.yaml ./
17
+ COPY pnpm-workspace.yaml* ./
18
+ RUN pnpm install --frozen-lockfile --ignore-scripts --config.minimumReleaseAge=0
19
+
20
+ # Only the bits needed to run vitest.
21
+ COPY vitest.screentest.config.ts ./
22
+ COPY tsconfig*.json ./
23
+ COPY tests ./tests
24
+
25
+ # Run vitest directly (not via pnpm exec) — pnpm 11 re-runs its supply-chain
26
+ # policy on every exec, which would re-fail freshly-published packages
27
+ # even after install succeeded.
28
+ CMD ["./node_modules/.bin/vitest", "--config", "vitest.screentest.config.ts", "run"]
@@ -0,0 +1,21 @@
1
+ FROM mcr.microsoft.com/playwright:v1.60.0-noble
2
+
3
+ WORKDIR /work
4
+
5
+ # corepack lets yarn 2+/berry self-install from your project's
6
+ # `packageManager` field. For yarn 1 it's a no-op (yarn is bundled in newer
7
+ # Node images anyway).
8
+ RUN corepack enable
9
+
10
+ # Install dependencies. --ignore-scripts skips your project's postinstall
11
+ # hooks (which usually scan src/ — not needed for the test image).
12
+ COPY package.json yarn.lock ./
13
+ COPY .yarnrc.yml* ./
14
+ RUN yarn install --frozen-lockfile --ignore-scripts
15
+
16
+ # Only the bits needed to run vitest.
17
+ COPY vitest.screentest.config.ts ./
18
+ COPY tsconfig*.json ./
19
+ COPY tests ./tests
20
+
21
+ CMD ["./node_modules/.bin/vitest", "--config", "vitest.screentest.config.ts", "run"]
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.2.0",
6
+ "version": "0.2.2",
7
7
  "description": "Local desktop tool for visual screenshot-test review — CLI, runner helpers, and review UI shipped as a single package.",
8
8
  "license": "MIT",
9
9
  "type": "module",
@@ -1,19 +0,0 @@
1
- FROM mcr.microsoft.com/playwright:v1.60.0-noble
2
-
3
- WORKDIR /work
4
-
5
- # Install pnpm — if your project uses npm/yarn, swap the next two lines.
6
- RUN npm install -g pnpm@9
7
-
8
- # Install dependencies. --ignore-scripts skips your project's postinstall
9
- # hooks (which usually scan src/ — not needed for the test image).
10
- COPY package.json pnpm-lock.yaml ./
11
- COPY pnpm-workspace.yaml* ./
12
- RUN pnpm install --frozen-lockfile --ignore-scripts
13
-
14
- # Only the bits needed to run vitest.
15
- COPY vitest.config.ts ./
16
- COPY tsconfig*.json ./
17
- COPY tests ./tests
18
-
19
- CMD ["pnpm", "exec", "vitest", "run"]