@forgebook/testing 0.1.2 → 0.1.3

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
@@ -11,13 +11,144 @@ tests:
11
11
  - **on the server** (Vitest), each story rendered to a string, so no component reads `window` or `document` while it
12
12
  renders, and its controls checked against its args.
13
13
 
14
- **Requirements:** `forgebook` · Vite 8 · `@playwright/test` 1.64+ · Vitest 5 ·
14
+ **Requirements:** `forgebook` · Vite 8 · `@playwright/test` 1.64+ · Vitest 5 · Docker for the visual tests ·
15
15
  [MIT licence](https://github.com/ensiria/forgebook/blob/develop/packages/testing/LICENSE)
16
16
 
17
17
  ```sh
18
18
  npm install --save-dev @forgebook/testing @playwright/test vitest
19
19
  ```
20
20
 
21
+ ## Setting up the tests
22
+
23
+ The tests run on the package's own **story source** (the `StorySource` its catalogue lists), in a folder of their
24
+ own. The example below is a package whose stories are `src/*.stories.tsx` and whose source is `stories/index.ts`:
25
+
26
+ ```ts
27
+ // stories/index.ts
28
+ /// <reference types="@forgebook/story-format/client" />
29
+ import type { StorySource } from "@forgebook/story-format/source";
30
+
31
+ import files from "../src/*.stories.tsx?forgebook-files";
32
+
33
+ export const source: StorySource = {
34
+ id: "acme-ui",
35
+ name: "Acme UI",
36
+ files: [files],
37
+ sheets: { required: [], theme: [], restyled: [] }
38
+ };
39
+ ```
40
+
41
+ ### In the browser
42
+
43
+ Six files in `test/stories/`. Where the stories are, with the same patterns as the source:
44
+
45
+ ```ts
46
+ // test/stories/storyFiles.ts
47
+ import { join } from "node:path";
48
+
49
+ import type { StoryFilesOptions } from "@forgebook/testing/story-files";
50
+
51
+ export const STORY_FILES: StoryFilesOptions = {
52
+ sourceId: "acme-ui",
53
+ name: "Acme UI",
54
+ root: join(import.meta.dirname, "..", "..", "stories"),
55
+ patterns: ["../src/*.stories.tsx"]
56
+ };
57
+ ```
58
+
59
+ The folder and the ports, then the Playwright configuration and the spec:
60
+
61
+ ```ts
62
+ // test/stories/testServer.ts
63
+ import type { StoryTestServer } from "@forgebook/testing/playwright";
64
+
65
+ export const STORY_SERVER: StoryTestServer = { dir: import.meta.dirname, port: 41831, browserPort: 41832 };
66
+
67
+ // test/stories/playwright.config.ts
68
+ import { storyTestConfig } from "@forgebook/testing/playwright";
69
+
70
+ import { STORY_SERVER } from "./testServer";
71
+
72
+ export default storyTestConfig(STORY_SERVER);
73
+
74
+ // test/stories/stories.spec.ts
75
+ import { storyBrowserTests } from "@forgebook/testing/browser";
76
+
77
+ import { STORY_FILES } from "./storyFiles";
78
+
79
+ storyBrowserTests(STORY_FILES);
80
+ ```
81
+
82
+ The frame page the tests open, and the Vite configuration that serves it:
83
+
84
+ ```html
85
+ <!-- test/stories/frame.html -->
86
+ <!doctype html>
87
+ <html lang="en">
88
+ <head><meta charset="utf-8" /><title>Stories</title></head>
89
+ <body><div id="root"></div><script type="module" src="./frame.tsx"></script></body>
90
+ </html>
91
+ ```
92
+
93
+ ```tsx
94
+ // test/stories/frame.tsx
95
+ import "@forgebook/testing/fonts.css";
96
+ import { mountFrame } from "@forgebook/react/frame";
97
+
98
+ import { source } from "../../stories/index";
99
+
100
+ const root = document.getElementById("root");
101
+ if (root !== null) void mountFrame(root, { sources: [source] });
102
+
103
+ // test/stories/vite.config.ts
104
+ import { react } from "@forgebook/react/vite";
105
+ import { storyTestViteConfig } from "@forgebook/testing/vite";
106
+
107
+ export default storyTestViteConfig(import.meta.dirname, react());
108
+ ```
109
+
110
+ Then `npx playwright install chromium` once, and `playwright test -c test/stories/playwright.config.ts` starts the
111
+ frame page and runs one test per story and variant (light, dark, both with more contrast, a narrow width, and the
112
+ structure view), starting Vite through the package manager that runs them (`vite` in `testServer.ts` names
113
+ another command). A story that breaks a rule fails all its variants; `storyBrowserTests` also takes `check`, a rule of
114
+ the package's own run on every frame.
115
+
116
+ ### On the server
117
+
118
+ One test file and a Vitest configuration with the catalogue's plugin, which reads the source's
119
+ `?forgebook-files` imports:
120
+
121
+ ```tsx
122
+ // test/stories/stories.server.test.tsx
123
+ import { reactRenderer } from "@forgebook/react/renderer";
124
+ import { storyServerTests } from "@forgebook/testing/server";
125
+
126
+ import { source } from "../../stories/index";
127
+
128
+ storyServerTests(source, { renderer: reactRenderer });
129
+
130
+ // vitest.config.ts
131
+ import { react } from "@forgebook/react/vite";
132
+ import { forgebookPlugin } from "forgebook/vite";
133
+ import { defineConfig } from "vitest/config";
134
+
135
+ export default defineConfig({
136
+ plugins: [forgebookPlugin({ framework: react() })],
137
+ test: { include: ["test/**/*.test.tsx"] }
138
+ });
139
+ ```
140
+
141
+ `storyServerTests` also takes `wrap`: a second pass with every story inside a decorator (a provider with a partial
142
+ translation catalogue), its HTML checked.
143
+
144
+ ### Limits today
145
+
146
+ - The visual tests (`visual.spec.ts`, `visualTestConfig`, `startVisualBrowser`) compare the frames with reference
147
+ images in the Playwright container, so they need Docker; this guide does not cover them yet, their modules' TSDoc
148
+ does.
149
+
150
+ ## Imports and commands
151
+
21
152
  | Import or command | What it is |
22
153
  |---|---|
23
154
  | `@forgebook/testing/browser` | The browser and visual tests of every story |
@@ -26,5 +157,4 @@ npm install --save-dev @forgebook/testing @playwright/test vitest
26
157
  | `forgebook-heights` | Measures every story's height at a few widths, for the catalogue's loading boxes |
27
158
  | `forgebook-shot` | Opens one story of a running catalogue and reports it with a picture |
28
159
 
29
- Each module's TSDoc describes its setup; a step-by-step guide is still to be written. More:
30
- [Forgebook's README](https://github.com/ensiria/forgebook/blob/develop/packages/forgebook/README.md).
160
+ More: [Forgebook's README](https://github.com/ensiria/forgebook/blob/develop/packages/forgebook/README.md).
@@ -9,7 +9,19 @@ export type StoryTestServer = {
9
9
  readonly browserPort: number;
10
10
  /** How many tests run at once; every core when left out or not a positive number. */
11
11
  readonly workers?: number;
12
+ /**
13
+ * The command that runs Vite (`node ./node_modules/vite/bin/vite.js`); by default the package manager that runs
14
+ * the tests runs it (`frameServerCommand`).
15
+ */
16
+ readonly vite?: string;
12
17
  };
18
+ /**
19
+ * The command that runs Vite, through the package manager that runs the tests: the one its user agent names
20
+ * (`npm_config_user_agent`, which every package manager sets for the scripts it runs), npx when none does.
21
+ *
22
+ * @param userAgent - the package manager's user agent (`pnpm/10.19.0 npm/? node/v24.15.0 darwin arm64`), if any.
23
+ */
24
+ export declare const frameServerCommand: (userAgent: string | undefined) => string;
13
25
  /**
14
26
  * The browser tests' configuration: `stories.spec.ts` in the host's Chromium.
15
27
  *
@@ -1 +1 @@
1
- {"version":3,"file":"playwrightConfig.d.ts","sourceRoot":"","sources":["../../src/playwrightConfig.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,iEAAiE;AACjE,MAAM,MAAM,eAAe,GAAG;IAC5B,mGAAmG;IACnG,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,qFAAqF;IACrF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAaF;;;;GAIG;AACH,eAAO,MAAM,eAAe,WAAY,eAAe,KAAG,oBAQxD,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,WAAY,eAAe,KAAG,oBAkB1D,CAAC"}
1
+ {"version":3,"file":"playwrightConfig.d.ts","sourceRoot":"","sources":["../../src/playwrightConfig.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,iEAAiE;AACjE,MAAM,MAAM,eAAe,GAAG;IAC5B,mGAAmG;IACnG,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,qFAAqF;IACrF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAUF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,cAAe,MAAM,GAAG,SAAS,KAAG,MAGlE,CAAC;AAaF;;;;GAIG;AACH,eAAO,MAAM,eAAe,WAAY,eAAe,KAAG,oBAQxD,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,WAAY,eAAe,KAAG,oBAkB1D,CAAC"}
@@ -12,11 +12,28 @@
12
12
  * @module
13
13
  */
14
14
  import { availableParallelism } from "node:os";
15
+ /** The command each package manager runs a dependency's binary with, by the name its user agent starts with. */
16
+ const RUNNERS = {
17
+ pnpm: "pnpm exec vite",
18
+ yarn: "yarn vite",
19
+ bun: "bunx vite",
20
+ npm: "npx vite"
21
+ };
22
+ /**
23
+ * The command that runs Vite, through the package manager that runs the tests: the one its user agent names
24
+ * (`npm_config_user_agent`, which every package manager sets for the scripts it runs), npx when none does.
25
+ *
26
+ * @param userAgent - the package manager's user agent (`pnpm/10.19.0 npm/? node/v24.15.0 darwin arm64`), if any.
27
+ */
28
+ export const frameServerCommand = (userAgent) => {
29
+ const manager = userAgent?.split("/")[0] ?? "npm";
30
+ return RUNNERS[manager] ?? "npx vite";
31
+ };
15
32
  /** The workers to use. */
16
33
  const workersOf = ({ workers }) => workers !== undefined && workers > 0 ? workers : availableParallelism();
17
34
  /** The test instance of the frame page, started by Playwright and stopped after the run. */
18
- const webServer = ({ dir, port }) => ({
19
- command: `pnpm exec vite --config ${dir}/vite.config.ts --host 127.0.0.1 --port ${port} --strictPort`,
35
+ const webServer = ({ dir, port, vite }) => ({
36
+ command: `${vite ?? frameServerCommand(process.env["npm_config_user_agent"])} --config ${dir}/vite.config.ts --host 127.0.0.1 --port ${port} --strictPort`,
20
37
  url: `http://127.0.0.1:${port}/frame.html`,
21
38
  reuseExistingServer: false
22
39
  });
@@ -1 +1 @@
1
- {"version":3,"file":"playwrightConfig.js","sourceRoot":"","sources":["../../src/playwrightConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAgB/C,0BAA0B;AAC1B,MAAM,SAAS,GAAG,CAAC,EAAE,OAAO,EAAmB,EAAU,EAAE,CACzD,OAAO,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,EAAE,CAAC;AAE1E,4FAA4F;AAC5F,MAAM,SAAS,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,EAAmB,EAAkD,EAAE,CAAC,CAAC;IACrG,OAAO,EAAE,2BAA2B,GAAG,2CAA2C,IAAI,eAAe;IACrG,GAAG,EAAE,oBAAoB,IAAI,aAAa;IAC1C,mBAAmB,EAAE,KAAK;CAC3B,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAuB,EAAwB,EAAE,CAAC,CAAC;IACjF,OAAO,EAAE,MAAM,CAAC,GAAG;IACnB,SAAS,EAAE,iBAAiB;IAC5B,aAAa,EAAE,IAAI;IACnB,OAAO,EAAE,SAAS,CAAC,MAAM,CAAC;IAC1B,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC;IAC5B,GAAG,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,oBAAoB,MAAM,CAAC,IAAI,GAAG,EAAE;CAC9E,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAuB,EAAwB,EAAE;IAChF,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC;IACtD,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,GAAG;QACnB,SAAS,EAAE,gBAAgB;QAC3B,oBAAoB,EAAE,sCAAsC;QAC5D,aAAa,EAAE,IAAI;QACnB,OAAO,EAAE,SAAS,CAAC,MAAM,CAAC;QAC1B,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;QACpB,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC;QAC5B,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,GAAG,mBAAmB,EAAE,CAAC;QAC3E,GAAG,EAAE;YACH,WAAW,EAAE,UAAU;YACvB,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;YACtC,OAAO,EAAE,UAAU,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,sBAAsB,IAAI,MAAM,CAAC,IAAI,GAAG;YACzF,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,EAAE,UAAU,EAAE,kBAAkB,MAAM,CAAC,WAAW,GAAG,EAAE,EAAE,CAAC;SACtG;KACF,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"playwrightConfig.js","sourceRoot":"","sources":["../../src/playwrightConfig.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgebook/testing",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Tests from a package's stories: each story and variant in the browser with axe, spills and overlays, screenshots, and each story rendered on the server.",
5
5
  "license": "MIT",
6
6
  "repository": {