@recursica/adapter-tester 2.2.0 → 3.0.0

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.
Files changed (40) hide show
  1. package/README.md +47 -16
  2. package/dist/adapter-tester.schema.json.d.ts +14 -10
  3. package/dist/cli.cjs +20 -24
  4. package/dist/cli.cjs.map +1 -1
  5. package/dist/cli.js +235 -4842
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config.d.ts +46 -6
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/fileConfig.d.ts +9 -2
  10. package/dist/fileConfig.d.ts.map +1 -1
  11. package/dist/golden/diffPng.d.ts +8 -0
  12. package/dist/golden/diffPng.d.ts.map +1 -0
  13. package/dist/golden/manifest.schema.json.d.ts +27 -0
  14. package/dist/golden/manifestStore.d.ts +15 -0
  15. package/dist/golden/manifestStore.d.ts.map +1 -0
  16. package/dist/golden/resolveSourceOfTruthGolden.d.ts +29 -0
  17. package/dist/golden/resolveSourceOfTruthGolden.d.ts.map +1 -0
  18. package/dist/golden/validateManifest.d.ts +7 -0
  19. package/dist/golden/validateManifest.d.ts.map +1 -0
  20. package/dist/index-C6uYPRmx.cjs +9 -0
  21. package/dist/index-C6uYPRmx.cjs.map +1 -0
  22. package/dist/index-DqQzFSAH.js +4667 -0
  23. package/dist/index-DqQzFSAH.js.map +1 -0
  24. package/dist/index.cjs +1 -1
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.js +21 -6
  27. package/dist/index.js.map +1 -1
  28. package/dist/testing/runVisualRegression.d.ts +22 -5
  29. package/dist/testing/runVisualRegression.d.ts.map +1 -1
  30. package/dist/testing.cjs +4 -1
  31. package/dist/testing.cjs.map +1 -1
  32. package/dist/testing.js +221 -151
  33. package/dist/testing.js.map +1 -1
  34. package/package.json +3 -2
  35. package/src/adapter-tester.schema.json +14 -10
  36. package/src/golden/manifest.schema.json +24 -0
  37. package/dist/config-B0Eop8Az.cjs +0 -2
  38. package/dist/config-B0Eop8Az.cjs.map +0 -1
  39. package/dist/config-CDxTeSAY.js +0 -21
  40. package/dist/config-CDxTeSAY.js.map +0 -1
package/README.md CHANGED
@@ -8,13 +8,13 @@ The `@recursica/adapter-tester` ensures that a Recursica adapter's components vi
8
8
 
9
9
  This package provides three primary utilities:
10
10
 
11
- 1. **Automated Visual Tests:** Headless, pixel-by-pixel regression checks for all stories, run as a single CLI command (`adapter-tester`) reading a small JSON config — no Playwright config or spec files to author.
11
+ 1. **Automated Visual Tests:** Headless, golden-image regression checks for all stories, run as a single CLI command (`adapter-tester`) reading a small JSON config — no Playwright config or spec files to author. See [Golden Images](#golden-images) below for the model.
12
12
  2. **Interactive Dev Mode:** A side-by-side synchronized browser environment with built-in note taking for auditing components and feeding fixes directly to an AI agent.
13
- 3. **An installable devDependency**, currently wired into `@recursica/mui-adapter` (not `@recursica/mantine-adapter` Mantine is itself the source of truth, so it has nothing to diff against). Any other adapter repo — including one that never checked out this monorepo — can install it the same way. See [Using this package in another adapter repo](#using-this-package-in-another-adapter-repo).
13
+ 3. **An installable devDependency**, wired into `@recursica/mantine-adapter` the source-of-truth adapter, run in `isSourceOfTruthAdapter` mode (own-drift check only, since it has nothing above it to diverge from). Any other adapter repo — including one that never checked out this monorepo — can install it the same way. See [Using this package in another adapter repo](#using-this-package-in-another-adapter-repo).
14
14
 
15
- **Default mode** (used by `@recursica/mui-adapter`, and any external adapter repo installing this package): compares this project's own Storybook — the one already defined by its `storybook` npm script — against a throwaway harness that installs the _published_ `@recursica/mantine-adapter` from npm. No monorepo checkout, no manual config, in most cases no config file at all.
15
+ **Default mode** (used by any adapter repo installing this package): checks this project's own Storybook — the one already defined by its `storybook` npm script — against its own committed golden images, and flags divergence from the source-of-truth adapter's (`@recursica/mantine-adapter`) own golden images. No monorepo checkout, no manual config, in most cases no config file at all.
16
16
 
17
- **Non-standard mode**: this monorepo also runs adapter-tester against itself Mantine and MUI both as local sibling workspace packages — to catch regressions in _uncommitted_ code before it's published. See `adapter-tester.config.json` in this package for that config; it's the same CLI, just pointed at `sourceOfTruth.type: "url"` instead of the default Mantine harness.
17
+ **Non-standard mode**: this monorepo also runs adapter-tester against itself, pointed at an adapter's and `@recursica/mantine-adapter`'s local sibling workspace packages — so the divergence check sees local, uncommitted `mantine-adapter` golden changes before they're published, not whatever was last published to npm. See `adapter-tester.config.json` in this package for that config; it's the same CLI, just pointed at `sourceOfTruth.type: "url"` instead of the default Mantine harness.
18
18
 
19
19
  ---
20
20
 
@@ -94,11 +94,12 @@ npm run adapter-tester:automated -- --grep "Toast"
94
94
 
95
95
  ### What happens during execution?
96
96
 
97
- 1. **Automatic Storybook Bootup**: `adapter-tester` boots both sides of the comparison — this project's own Storybook, and the source-of-truth Storybook (a throwaway Mantine harness by default) reusing them if already running.
98
- 2. **Story Discovery**: The test suite fetches the source of truth's Storybook index (`/index.json`) to dynamically discover and parameterize tests for every component story, excluding `@recursica/storybook-template`'s own default token/theme demo stories (`Theme/*`, `Tokens/*`) and each adapter's own onboarding stories (`Introduction/*`).
99
- 3. **Headless Snapshots**: It launches headless Chrome in the background, navigates to the isolated iframe views for both targets, and takes side-by-side snapshots.
100
- 4. **Pixel Diffing**: It diffs the images using `pixelmatch` against the configured mismatch threshold.
101
- 5. **Native Report Generation**: All raw snapshots, highlighted pixel diff overlays, and extracted computed CSS DOM trees are embedded directly as test attachments.
97
+ 1. **Automatic Storybook Bootup**: `adapter-tester` boots only this project's own Storybook the divergence check reads the source-of-truth adapter's stored golden files, never a live Storybook, so nothing else needs to boot.
98
+ 2. **Story Discovery**: The test suite fetches this project's own Storybook index (`/index.json`) to dynamically discover and parameterize tests for every component story, excluding `@recursica/storybook-template`'s own default token/theme demo stories (`Theme/*`, `Tokens/*`) and each adapter's own onboarding stories (`Introduction/*`).
99
+ 3. **Headless Snapshot**: It launches headless Chrome, navigates to the isolated iframe view, and takes a screenshot.
100
+ 4. **Own-Drift Check**: Diffs the screenshot against this project's own `test/golden/<story-id>.png` using `pixelmatch`, against the configured mismatch threshold. No golden yet for a story is not a failure — one is captured from this run.
101
+ 5. **Divergence Check**: Diffs this project's own golden against the source-of-truth adapter's golden (skipped for the source-of-truth adapter's own config). Different and not yet reviewed via `--approve-divergence`? Flagged as a report annotation — never a failure on its own.
102
+ 6. **Native Report Generation**: Screenshots, diff overlays, and divergence flags are embedded directly as test attachments/annotations.
102
103
 
103
104
  ### Output & Reports
104
105
 
@@ -113,6 +114,35 @@ All test outcomes and visual outputs are compiled into the standard, git-ignored
113
114
 
114
115
  ---
115
116
 
117
+ ## Golden Images
118
+
119
+ Each adapter's `test/golden/` directory — committed to git, alongside a `manifest.json` tracking when each was captured — is its own baseline:
120
+
121
+ - **Own-drift check (hard fail):** this run's live render vs this project's own golden. Catches unintended CSS regressions.
122
+ - **Divergence check (soft flag, never fails a run):** this project's own golden vs the source-of-truth adapter's golden — different underlying UI libraries can legitimately render a component differently, so a divergence isn't automatically wrong, just unreviewed.
123
+
124
+ These are explicit, developer-run workflows — not run in CI, and not scoped down automatically; use `--grep` to target specific stories.
125
+
126
+ ```bash
127
+ # Capture/refresh this project's own goldens for stories matching "Toast",
128
+ # from the current live render. Use after an intentional styling change.
129
+ npm run adapter-tester:automated -- --grep "Toast" --update-golden
130
+
131
+ # Review a flagged divergence from the source of truth, and accept it as
132
+ # intentional. Also (re)captures the own golden from the current live render.
133
+ npm run adapter-tester:automated -- --grep "Toast" --approve-divergence
134
+ ```
135
+
136
+ `--approve-divergence` records the source-of-truth adapter's own `manifest.json` `createdAt` for that story at review time. If the source of truth's golden is recaptured later, the divergence flags again for re-review — approval isn't a permanent exemption.
137
+
138
+ A story with no golden yet (new story, or one never captured) is not a failure in either mode — one is captured from the current run automatically.
139
+
140
+ Renamed/removed stories just leave an orphaned `test/golden/<story-id>.png`/manifest entry behind; nothing prunes these automatically.
141
+
142
+ `manifest.json` is validated against [`src/golden/manifest.schema.json`](./src/golden/manifest.schema.json) on every read/write — don't hand-edit it.
143
+
144
+ ---
145
+
116
146
  ## Configuring `adapter-tester.config.json`
117
147
 
118
148
  All fields are optional — an adapter repo with a normal `storybook` npm script (e.g. `storybook dev -p 6012`) needs no config file at all.
@@ -126,18 +156,19 @@ Every `adapter-tester.config.json` is validated against [`src/adapter-tester.sch
126
156
  "storybook": { "port": 6006, "command": "npm run storybook" },
127
157
  "sourceOfTruth": { "type": "mantine-harness" },
128
158
  "diffThresholdPixels": 3500,
129
- "relaxedThresholdStoryIds": ["ui-kit-slider"],
130
- "relaxedThresholdPixels": 15000,
131
- "excludeTitlePrefixes": ["Theme", "Tokens", "Introduction"]
159
+ "storyThresholds": { "ui-kit-slider": 15000 },
160
+ "excludeTitlePrefixes": ["Theme", "Tokens", "Introduction"],
161
+ "excludeStoryIds": ["ui-kit-slider--range-mode-with-icons-and-inputs"]
132
162
  }
133
163
  ```
134
164
 
135
165
  - `name` — label for this project's own target. Defaults to the unscoped `package.json` name.
136
166
  - `storybook.port` — defaults to whatever `-p`/`--port` is set on this project's own `storybook` script; falls back to Storybook's default of `6006`.
137
167
  - `storybook.command`/`storybook.cwd` — default to `npm run storybook` in the current directory.
138
- - `sourceOfTruth` — defaults to `{ "type": "mantine-harness" }`: a throwaway harness that installs the _published_ `@recursica/mantine-adapter` from npm, so you never need this monorepo checked out. This is the standard mode every external adapter repo (and `@recursica/mui-adapter`) uses.
139
- - `sourceOfTruth.type: "url"` — the **non-standard** mode: points at an already-addressable Storybook via `command`/`port`/`cwd` instead of the harness. Used inside this monorepo to compare local, uncommitted `mantine-adapter`/`mui-adapter` workspace packages against each other — see `adapter-tester.config.json` in this package.
140
- - `diffThresholdPixels`/`relaxedThresholdStoryIds`/`relaxedThresholdPixels`/`excludeTitlePrefixes` — same meaning as before; see `src/adapter-tester.schema.json` for full docs.
168
+ - `sourceOfTruth` — defaults to `{ "type": "mantine-harness" }`: a throwaway harness that installs the _published_ `@recursica/mantine-adapter` from npm, so you never need this monorepo checked out. This is the standard mode every external adapter repo uses.
169
+ - `sourceOfTruth.type: "url"` — the **non-standard** mode: points at an already-addressable Storybook via `command`/`port`/`cwd` instead of the harness, and reads that sibling package's `test/golden/` directly from disk for the divergence check (including uncommitted local changes) instead of resolving a published npm version. Used inside this monorepo — see `adapter-tester.config.json` in this package.
170
+ - `isSourceOfTruthAdapter` — set `true` only in the source-of-truth adapter's own config (`@recursica/mantine-adapter`). Skips `sourceOfTruth`/the divergence check entirely; runs the own-drift check standalone. Also disables Dev Mode (`--serve`) there's nothing to sync against.
171
+ - `diffThresholdPixels`/`storyThresholds`/`excludeTitlePrefixes`/`excludeStoryIds` — same meaning as before; see `src/adapter-tester.schema.json` for full docs.
141
172
 
142
173
  ---
143
174
 
@@ -163,4 +194,4 @@ Any extra args after `adapter-tester:automated` are passed straight through to `
163
194
  npm run adapter-tester:automated -- --grep "Toast"
164
195
  ```
165
196
 
166
- Add `adapter-tester.config.json` only to override defaults (see [Configuring `adapter-tester.config.json`](#configuring-adapter-testerconfigjson) above), e.g. to raise the diff threshold or relax specific stories. Add `.adapter-tester/` to your `.gitignore` — it's regenerated on every run.
197
+ Add `adapter-tester.config.json` only to override defaults (see [Configuring `adapter-tester.config.json`](#configuring-adapter-testerconfigjson) above), e.g. to raise the diff threshold or relax specific stories. Add `.adapter-tester/` to your `.gitignore` — it's regenerated on every run. Commit `test/golden/` — that's your project's actual baseline, not a build artifact.
@@ -12,7 +12,7 @@ declare const _default: {
12
12
  },
13
13
  "name": {
14
14
  "type": "string",
15
- "description": "Label for this project's own target. Defaults to the unscoped name in this project's package.json (e.g. \"@recursica/mui-adapter\" -> \"mui-adapter\")."
15
+ "description": "Label for this project's own target. Defaults to the unscoped name in this project's package.json (e.g. \"@recursica/some-adapter\" -> \"some-adapter\")."
16
16
  },
17
17
  "storybook": {
18
18
  "type": "object",
@@ -89,25 +89,29 @@ declare const _default: {
89
89
  }
90
90
  ]
91
91
  },
92
+ "isSourceOfTruthAdapter": {
93
+ "type": "boolean",
94
+ "description": "True only for the source-of-truth adapter's own config (mantine-adapter). Skips `sourceOfTruth` entirely — there's nothing above it to diverge from — and runs the own-drift golden check standalone, against just this project's own Storybook. Defaults to false."
95
+ },
92
96
  "diffThresholdPixels": {
93
97
  "type": "number",
94
98
  "minimum": 0,
95
99
  "description": "Global visual diff threshold, in mismatched pixels, before a story is considered a failure. Defaults to 3500. AI agents must not modify this field — see AGENT.md."
96
100
  },
97
- "relaxedThresholdStoryIds": {
98
- "type": "array",
99
- "items": { "type": "string" },
100
- "description": "Story id prefixes that use `relaxedThresholdPixels` instead of the global `diffThresholdPixels`."
101
- },
102
- "relaxedThresholdPixels": {
103
- "type": "number",
104
- "minimum": 0,
105
- "description": "Diff threshold applied to stories matching `relaxedThresholdStoryIds`."
101
+ "storyThresholds": {
102
+ "type": "object",
103
+ "additionalProperties": { "type": "number", "minimum": 0 },
104
+ "description": "Per-story diff threshold overrides, keyed by story id prefix (a story matches if its id equals the key or starts with it). Overrides `diffThresholdPixels` for matching stories. When more than one key matches a story, the longest (most specific) key wins."
106
105
  },
107
106
  "excludeTitlePrefixes": {
108
107
  "type": "array",
109
108
  "items": { "type": "string" },
110
109
  "description": "Storybook title prefixes to exclude from comparison, in addition to the built-in denylist (`Theme/*`, `Tokens/*`, `Introduction`)."
110
+ },
111
+ "excludeStoryIds": {
112
+ "type": "array",
113
+ "items": { "type": "string" },
114
+ "description": "Story id prefixes to skip entirely — no own-drift check, no divergence check, no golden captured. For excluding individual stories that don't have a cross-adapter counterpart; use `excludeTitlePrefixes` to drop a whole title instead."
111
115
  }
112
116
  }
113
117
  }