@recursica/adapter-tester 1.1.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.
package/README.md ADDED
@@ -0,0 +1,164 @@
1
+ # @recursica/adapter-tester
2
+
3
+ A specialized automated visual regression testing engine and interactive developer environment for Recursica adapters.
4
+
5
+ ## Overview
6
+
7
+ The `@recursica/adapter-tester` ensures that a Recursica adapter's components visually match Recursica's design tokens and Mantine as the source-of-truth adapter.
8
+
9
+ This package provides three primary utilities:
10
+
11
+ 1. **Automated Visual Tests:** Headless, pixel-by-pixel regression checks for all stories.
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 library** (`import ... from "@recursica/adapter-tester"`) so any adapter repo — including ones that never checked out this monorepo — can run the same comparison against its own local Storybook. See [Using this package in another adapter repo](#using-this-package-in-another-adapter-repo).
14
+
15
+ Inside this monorepo, it runs its bundled config comparing `@recursica/mantine-adapter` (source of truth) against `@recursica/mui-adapter`, both launched as sibling workspace packages — see `adapter-tester.config.ts`.
16
+
17
+ ---
18
+
19
+ ## Prerequisites
20
+
21
+ Before running the tester or dev mode, ensure that both adapter Storybooks are running locally on their assigned odd ports to prevent clashing:
22
+
23
+ - **Mantine Adapter (Source of Truth)**: `http://localhost:6011`
24
+ - **MUI Adapter (Target)**: `http://localhost:6012`
25
+
26
+ _(Launch these from their respective package directories using `npm run storybook`)_
27
+
28
+ ---
29
+
30
+ ## Interactive Dev Mode
31
+
32
+ The **Dev Mode** is a powerful visual auditing tool that syncs the Mantine and MUI Storybooks in real-time. When you interact with controls (like toggling 'disabled' or changing 'size') in the Mantine pane, the Dev Mode instantly updates the MUI pane to match that exact state.
33
+
34
+ ### How to Start
35
+
36
+ 1. Ensure the Storybooks are running on `6011` and `6012`.
37
+ 2. Run the Dev Mode server:
38
+ ```bash
39
+ cd packages/adapter-tester
40
+ npm run dev
41
+ ```
42
+ 3. Open your browser to [http://localhost:6010](http://localhost:6010).
43
+
44
+ ### Taking Component Notes
45
+
46
+ The bottom portion of the right panel features a **Component Notes** area.
47
+
48
+ - As you navigate through different component states, this text area automatically switches context.
49
+ - Your notes are instantly and persistently saved to your browser's `localStorage`, strictly bound to the exact Storybook URL configuration (e.g., `ui-kit-slider--default | disabled:true`).
50
+
51
+ ### Generating an AI Report
52
+
53
+ When you are done auditing discrepancies, click the **Full Report** button in the notes header.
54
+
55
+ This will crawl your local storage and compile every note you've taken into a clean, markdown-formatted report. The report maps your notes to human-readable component headers and exact Storybook configuration URLs.
56
+
57
+ #### Modifying the AI Prompt Header
58
+
59
+ The compiled report is specifically designed to be copy-pasted directly into an AI coding assistant. It begins with a system prompt block that tells the AI exactly what the report represents and how to apply the fixes.
60
+
61
+ You can modify this instructional header at any time by editing:
62
+ `packages/adapter-tester/report-header.txt`
63
+
64
+ The Dev Mode server fetches this file dynamically, so any changes you make to `report-header.txt` will instantly appear the next time you click **Full Report**—no restart required!
65
+
66
+ ---
67
+
68
+ ## Automated Visual Tests
69
+
70
+ To execute a full headless visual regression scan across all components:
71
+
72
+ ```bash
73
+ cd packages/adapter-tester
74
+ npm run test:visual
75
+ ```
76
+
77
+ ### What happens during execution?
78
+
79
+ 1. **Automatic Storybook Bootup**: The testing engine checks ports `6011` and `6012`. If they are inactive, it automatically starts the Mantine and MUI Storybooks in the background and cleans them up upon exit. If they are already active, it instantly reuses them.
80
+ 2. **Story Discovery**: The test suite fetches the source of truth's Storybook index (`http://localhost:6011/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/*`).
81
+ 3. **Headless Snapshots**: It launches headless Chrome in the background, navigates to the isolated iframe views for both adapters, and takes side-by-side snapshots.
82
+ 4. **Pixel Diffing**: It diffs the images using `pixelmatch` against our global mismatch threshold.
83
+ 5. **Native Report Generation**: All raw snapshots, highlighted pixel diff overlays, and extracted computed CSS DOM trees are embedded directly as test attachments.
84
+
85
+ ### Output & Reports
86
+
87
+ All test outcomes and visual outputs are compiled into the standard, git-ignored Playwright reports folder:
88
+
89
+ - **Interactive HTML Dashboard**: View the full side-by-side browser layout, visual difference overlays, and style audits by opening:
90
+ `packages/adapter-tester/playwright-report/index.html`
91
+ - **Review in Browser**: Open the interactive dashboard directly from your terminal by running:
92
+ ```bash
93
+ npx playwright show-report
94
+ ```
95
+ - **Raw Screenshot Assets**: Individual screenshot buffers and visual diff outputs are retained in the standard `packages/adapter-tester/test-results/` folder for reference.
96
+
97
+ ---
98
+
99
+ ## Using this package in another adapter repo
100
+
101
+ `@recursica/adapter-tester` is a real, installable devDependency — a standalone adapter repo (not checked out inside this monorepo) can use it to diff its own Storybook against Mantine, Recursica's source-of-truth adapter, without cloning this monorepo at all.
102
+
103
+ ```bash
104
+ npm install --save-dev @recursica/adapter-tester
105
+ ```
106
+
107
+ Add a config listing your adapter's already-running local Storybook alongside a Mantine source-of-truth target:
108
+
109
+ ```ts
110
+ // adapter-tester.config.ts
111
+ import { defineAdapterTesterConfig } from "@recursica/adapter-tester";
112
+
113
+ export default defineAdapterTesterConfig({
114
+ targets: [
115
+ { name: "Mantine", url: "http://localhost:6011", sourceOfTruth: true },
116
+ { name: "MyAdapter", url: "http://localhost:6006" },
117
+ ],
118
+ diffThresholdPixels: 3500,
119
+ });
120
+ ```
121
+
122
+ Wire the Mantine side up as a `playwright.config.ts` `webServer` entry with `mantineSourceOfTruthWebServer()` — it scaffolds a small throwaway project that installs the real, published `@recursica/mantine-adapter` package and boots a real Storybook from it, so you never need this monorepo checked out:
123
+
124
+ ```ts
125
+ // playwright.config.ts
126
+ import { defineConfig } from "@playwright/test";
127
+ import { mantineSourceOfTruthWebServer } from "@recursica/adapter-tester";
128
+ import config from "./adapter-tester.config";
129
+
130
+ export default defineConfig({
131
+ testDir: "./tests",
132
+ webServer: [
133
+ mantineSourceOfTruthWebServer({
134
+ dir: "./.adapter-tester/mantine-harness", // add to .gitignore
135
+ port: 6011,
136
+ }),
137
+ {
138
+ command: "npm run storybook", // your own adapter's Storybook
139
+ port: 6006,
140
+ reuseExistingServer: !process.env.CI,
141
+ },
142
+ ],
143
+ });
144
+ ```
145
+
146
+ Then define the test suite itself in a spec file — it diffs every non-source-of-truth target against the source of truth, story by story:
147
+
148
+ ```ts
149
+ // tests/visual-regression.spec.ts
150
+ import { runVisualRegression } from "@recursica/adapter-tester/testing";
151
+ import config from "../adapter-tester.config";
152
+
153
+ await runVisualRegression(config);
154
+ ```
155
+
156
+ ```bash
157
+ npx playwright test
158
+ ```
159
+
160
+ Notes:
161
+
162
+ - `mantineSourceOfTruthWebServer()` regenerates its harness directory on every run — commit `.adapter-tester/` to your `.gitignore` rather than checking it in.
163
+ - Pass `mantineAdapterVersion`/`storybookTemplateVersion` to pin the harness to a specific published release instead of tracking `latest`.
164
+ - `defineAdapterTesterConfig` requires exactly one target marked `sourceOfTruth: true`; it throws otherwise.
@@ -0,0 +1,2 @@
1
+ "use strict";function u(t){const r=t.targets.filter(e=>e.sourceOfTruth).length;if(r!==1)throw new Error(`adapter-tester config must mark exactly one target as sourceOfTruth (found ${r})`);return t}function o(t){const r=t.targets.find(e=>e.sourceOfTruth);if(!r)throw new Error("adapter-tester config has no target marked sourceOfTruth");return r}exports.defineAdapterTesterConfig=u;exports.getSourceOfTruth=o;
2
+ //# sourceMappingURL=config-B0Eop8Az.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-B0Eop8Az.cjs","sources":["../src/config.ts"],"sourcesContent":["export interface AdapterTarget {\n /** Human-readable name used in test titles, report labels, and artifact filenames. */\n name: string;\n /** Base URL the target's Storybook instance is served from. */\n url: string;\n /**\n * Marks this target as the visual/token source of truth every other target\n * is compared against. Exactly one target in a config must set this true.\n */\n sourceOfTruth?: boolean;\n}\n\nexport interface AdapterTesterConfig {\n targets: AdapterTarget[];\n /** Global pixel-diff threshold applied to every story comparison. */\n diffThresholdPixels: number;\n /**\n * Story ids (matched by prefix) that get `relaxedThresholdPixels` instead\n * of `diffThresholdPixels`, for components with acceptable cross-library\n * structural variation (e.g. native control widgets).\n */\n relaxedThresholdStoryIds?: string[];\n /** Pixel-diff threshold applied to `relaxedThresholdStoryIds`. */\n relaxedThresholdPixels?: number;\n /**\n * Storybook entry title categories excluded from the comparison — an\n * entry is excluded if its title equals one of these or starts with\n * `\"<value>/\"`. Defaults to [\"Theme\", \"Tokens\", \"Introduction\"]:\n * `@recursica/storybook-template`'s own default token/theme demo stories\n * (which every adapter's Storybook inherits automatically) plus each\n * adapter's own onboarding \"Introduction\" stories — neither has a\n * cross-adapter counterpart to diff against.\n */\n excludeTitlePrefixes?: string[];\n}\n\nexport function defineAdapterTesterConfig(\n config: AdapterTesterConfig,\n): AdapterTesterConfig {\n const sourceOfTruthCount = config.targets.filter(\n (target) => target.sourceOfTruth,\n ).length;\n if (sourceOfTruthCount !== 1) {\n throw new Error(\n `adapter-tester config must mark exactly one target as sourceOfTruth (found ${sourceOfTruthCount})`,\n );\n }\n return config;\n}\n\nexport function getSourceOfTruth(config: AdapterTesterConfig): AdapterTarget {\n const sourceOfTruth = config.targets.find((target) => target.sourceOfTruth);\n if (!sourceOfTruth) {\n throw new Error(\n \"adapter-tester config has no target marked sourceOfTruth\",\n );\n }\n return sourceOfTruth;\n}\n"],"names":["defineAdapterTesterConfig","config","sourceOfTruthCount","target","getSourceOfTruth","sourceOfTruth"],"mappings":"aAoCO,SAASA,EACdC,EACqB,CACrB,MAAMC,EAAqBD,EAAO,QAAQ,OACvCE,GAAWA,EAAO,aAAA,EACnB,OACF,GAAID,IAAuB,EACzB,MAAM,IAAI,MACR,8EAA8EA,CAAkB,GAAA,EAGpG,OAAOD,CACT,CAEO,SAASG,EAAiBH,EAA4C,CAC3E,MAAMI,EAAgBJ,EAAO,QAAQ,KAAME,GAAWA,EAAO,aAAa,EAC1E,GAAI,CAACE,EACH,MAAM,IAAI,MACR,0DAAA,EAGJ,OAAOA,CACT"}
@@ -0,0 +1,23 @@
1
+ function o(t) {
2
+ const r = t.targets.filter(
3
+ (e) => e.sourceOfTruth
4
+ ).length;
5
+ if (r !== 1)
6
+ throw new Error(
7
+ `adapter-tester config must mark exactly one target as sourceOfTruth (found ${r})`
8
+ );
9
+ return t;
10
+ }
11
+ function u(t) {
12
+ const r = t.targets.find((e) => e.sourceOfTruth);
13
+ if (!r)
14
+ throw new Error(
15
+ "adapter-tester config has no target marked sourceOfTruth"
16
+ );
17
+ return r;
18
+ }
19
+ export {
20
+ o as d,
21
+ u as g
22
+ };
23
+ //# sourceMappingURL=config-CKgyDyNF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-CKgyDyNF.js","sources":["../src/config.ts"],"sourcesContent":["export interface AdapterTarget {\n /** Human-readable name used in test titles, report labels, and artifact filenames. */\n name: string;\n /** Base URL the target's Storybook instance is served from. */\n url: string;\n /**\n * Marks this target as the visual/token source of truth every other target\n * is compared against. Exactly one target in a config must set this true.\n */\n sourceOfTruth?: boolean;\n}\n\nexport interface AdapterTesterConfig {\n targets: AdapterTarget[];\n /** Global pixel-diff threshold applied to every story comparison. */\n diffThresholdPixels: number;\n /**\n * Story ids (matched by prefix) that get `relaxedThresholdPixels` instead\n * of `diffThresholdPixels`, for components with acceptable cross-library\n * structural variation (e.g. native control widgets).\n */\n relaxedThresholdStoryIds?: string[];\n /** Pixel-diff threshold applied to `relaxedThresholdStoryIds`. */\n relaxedThresholdPixels?: number;\n /**\n * Storybook entry title categories excluded from the comparison — an\n * entry is excluded if its title equals one of these or starts with\n * `\"<value>/\"`. Defaults to [\"Theme\", \"Tokens\", \"Introduction\"]:\n * `@recursica/storybook-template`'s own default token/theme demo stories\n * (which every adapter's Storybook inherits automatically) plus each\n * adapter's own onboarding \"Introduction\" stories — neither has a\n * cross-adapter counterpart to diff against.\n */\n excludeTitlePrefixes?: string[];\n}\n\nexport function defineAdapterTesterConfig(\n config: AdapterTesterConfig,\n): AdapterTesterConfig {\n const sourceOfTruthCount = config.targets.filter(\n (target) => target.sourceOfTruth,\n ).length;\n if (sourceOfTruthCount !== 1) {\n throw new Error(\n `adapter-tester config must mark exactly one target as sourceOfTruth (found ${sourceOfTruthCount})`,\n );\n }\n return config;\n}\n\nexport function getSourceOfTruth(config: AdapterTesterConfig): AdapterTarget {\n const sourceOfTruth = config.targets.find((target) => target.sourceOfTruth);\n if (!sourceOfTruth) {\n throw new Error(\n \"adapter-tester config has no target marked sourceOfTruth\",\n );\n }\n return sourceOfTruth;\n}\n"],"names":["defineAdapterTesterConfig","config","sourceOfTruthCount","target","getSourceOfTruth","sourceOfTruth"],"mappings":"AAoCO,SAASA,EACdC,GACqB;AACrB,QAAMC,IAAqBD,EAAO,QAAQ;AAAA,IACxC,CAACE,MAAWA,EAAO;AAAA,EAAA,EACnB;AACF,MAAID,MAAuB;AACzB,UAAM,IAAI;AAAA,MACR,8EAA8EA,CAAkB;AAAA,IAAA;AAGpG,SAAOD;AACT;AAEO,SAASG,EAAiBH,GAA4C;AAC3E,QAAMI,IAAgBJ,EAAO,QAAQ,KAAK,CAACE,MAAWA,EAAO,aAAa;AAC1E,MAAI,CAACE;AACH,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAGJ,SAAOA;AACT;"}
@@ -0,0 +1,37 @@
1
+ export interface AdapterTarget {
2
+ /** Human-readable name used in test titles, report labels, and artifact filenames. */
3
+ name: string;
4
+ /** Base URL the target's Storybook instance is served from. */
5
+ url: string;
6
+ /**
7
+ * Marks this target as the visual/token source of truth every other target
8
+ * is compared against. Exactly one target in a config must set this true.
9
+ */
10
+ sourceOfTruth?: boolean;
11
+ }
12
+ export interface AdapterTesterConfig {
13
+ targets: AdapterTarget[];
14
+ /** Global pixel-diff threshold applied to every story comparison. */
15
+ diffThresholdPixels: number;
16
+ /**
17
+ * Story ids (matched by prefix) that get `relaxedThresholdPixels` instead
18
+ * of `diffThresholdPixels`, for components with acceptable cross-library
19
+ * structural variation (e.g. native control widgets).
20
+ */
21
+ relaxedThresholdStoryIds?: string[];
22
+ /** Pixel-diff threshold applied to `relaxedThresholdStoryIds`. */
23
+ relaxedThresholdPixels?: number;
24
+ /**
25
+ * Storybook entry title categories excluded from the comparison — an
26
+ * entry is excluded if its title equals one of these or starts with
27
+ * `"<value>/"`. Defaults to ["Theme", "Tokens", "Introduction"]:
28
+ * `@recursica/storybook-template`'s own default token/theme demo stories
29
+ * (which every adapter's Storybook inherits automatically) plus each
30
+ * adapter's own onboarding "Introduction" stories — neither has a
31
+ * cross-adapter counterpart to diff against.
32
+ */
33
+ excludeTitlePrefixes?: string[];
34
+ }
35
+ export declare function defineAdapterTesterConfig(config: AdapterTesterConfig): AdapterTesterConfig;
36
+ export declare function getSourceOfTruth(config: AdapterTesterConfig): AdapterTarget;
37
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,qEAAqE;IACrE,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAC;IACpC,kEAAkE;IAClE,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;;;OAQG;IACH,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,mBAAmB,GAC1B,mBAAmB,CAUrB;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa,CAQ3E"}
@@ -0,0 +1,11 @@
1
+ export interface DomNodeData {
2
+ tagName: string;
3
+ classes: string[];
4
+ attributes: Record<string, string>;
5
+ text?: string;
6
+ children: DomNodeData[];
7
+ styles: Record<string, string>;
8
+ }
9
+ export declare function extractDomNode(el: Element, stylesToCapture: string[]): DomNodeData;
10
+ export declare const COMMON_STYLES_TO_CAPTURE: string[];
11
+ //# sourceMappingURL=extractDomNode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractDomNode.d.ts","sourceRoot":"","sources":["../src/extractDomNode.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,wBAAgB,cAAc,CAC5B,EAAE,EAAE,OAAO,EACX,eAAe,EAAE,MAAM,EAAE,GACxB,WAAW,CAuCb;AAED,eAAO,MAAM,wBAAwB,UAsBpC,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Generates a small, throwaway Storybook project that installs
3
+ * `@recursica/mantine-adapter` as a real npm dependency (not a workspace
4
+ * link) and boots a real Storybook from its published `src/**\/*.stories.tsx`
5
+ * files, using `@recursica/storybook-template`'s exported factories.
6
+ *
7
+ * This lets any repo — including ones that never checked out the Recursica
8
+ * monorepo — run adapter-tester's visual regression suite against Mantine
9
+ * (Recursica's source-of-truth adapter) as one side of the comparison, while
10
+ * the other side is that repo's own already-running local Storybook.
11
+ *
12
+ * See PROPOSAL-installed-package-harness.md for the verified prototype this
13
+ * is built from, and the three upstream gaps it works around.
14
+ */
15
+ export interface MantineSourceOfTruthHarnessOptions {
16
+ /**
17
+ * Directory the harness project is scaffolded into. Regenerated on every
18
+ * call — add it to your .gitignore rather than committing it.
19
+ */
20
+ dir: string;
21
+ /** Port the harness's Storybook dev server boots on. */
22
+ port: number;
23
+ /** npm version/range for @recursica/mantine-adapter. Defaults to "latest". */
24
+ mantineAdapterVersion?: string;
25
+ /** npm version/range for @recursica/storybook-template. Defaults to "latest". */
26
+ storybookTemplateVersion?: string;
27
+ }
28
+ export interface HarnessWebServerConfig {
29
+ command: string;
30
+ port: number;
31
+ cwd: string;
32
+ reuseExistingServer: boolean;
33
+ timeout: number;
34
+ }
35
+ /** Writes the harness project's files to `options.dir` without booting it. */
36
+ export declare function scaffoldMantineSourceOfTruthHarness(options: MantineSourceOfTruthHarnessOptions): string;
37
+ /**
38
+ * Scaffolds the harness and returns a Playwright `webServer` entry for it.
39
+ * Spread the result directly into `playwright.config.ts`'s `webServer` array.
40
+ */
41
+ export declare function mantineSourceOfTruthWebServer(options: MantineSourceOfTruthHarnessOptions): HarnessWebServerConfig;
42
+ //# sourceMappingURL=mantineSourceOfTruth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mantineSourceOfTruth.d.ts","sourceRoot":"","sources":["../../src/harness/mantineSourceOfTruth.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,kCAAkC;IACjD;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,iFAAiF;IACjF,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,mBAAmB,EAAE,OAAO,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB;AAgHD,8EAA8E;AAC9E,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,kCAAkC,GAC1C,MAAM,CAsBR;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,kCAAkC,GAC1C,sBAAsB,CASxB"}
package/dist/index.cjs ADDED
@@ -0,0 +1,54 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("./config-B0Eop8Az.cjs"),o=require("node:fs"),t=require("node:path"),i="^8.0.0",s="^10.3.3",n="^19.0.0",m={"@storybook/addon-docs":s,"@storybook/addon-a11y":s,"storybook-dark-mode":"^5.0.0"},p={"react-markdown":"^10.1.0"};function l(e){return{name:"adapter-tester-mantine-source-of-truth-harness",private:!0,type:"module",scripts:{storybook:`storybook dev -p ${e.port}`},dependencies:{"@recursica/mantine-adapter":e.mantineAdapterVersion,"@recursica/storybook-template":e.storybookTemplateVersion,"@recursica/official-release":"latest","@recursica/adapter-common":"latest","@mantine/core":i,"@mantine/dates":i,react:n,"react-dom":n,storybook:s,"@storybook/react-vite":s,...m,...p}}}const y=`import { createMainConfig } from "@recursica/storybook-template/main";
2
+
3
+ const config = createMainConfig({
4
+ stories: [
5
+ "../node_modules/@recursica/mantine-adapter/src/**/*.stories.@(js|jsx|mjs|ts|tsx)",
6
+ ],
7
+ enableCORS: true,
8
+ });
9
+
10
+ // react-docgen-typescript can't resolve a TS project for a config file living
11
+ // in .storybook/ when the component source it's docgen'ing lives three
12
+ // directories down inside node_modules — it throws "Cannot read properties
13
+ // of undefined (reading 'fileExists')", which surfaces as a plain 404 on
14
+ // preview.tsx. Docgen only powers Storybook's Controls/Docs tables, which
15
+ // this harness never renders, so disabling it is a safe workaround (see
16
+ // PROPOSAL-installed-package-harness.md, gap 3).
17
+ config.typescript = { ...config.typescript, reactDocgen: false };
18
+
19
+ export default config;
20
+ `,g=`import type { Preview } from "@storybook/react-vite";
21
+ import { createPreviewConfig } from "@recursica/storybook-template/preview";
22
+ import { MantineProvider } from "@mantine/core";
23
+ import "@mantine/core/styles.css";
24
+ import "@mantine/dates/styles.css";
25
+ import "@recursica/adapter-common/style.css";
26
+ import "@recursica/official-release/recursica_variables_scoped.css";
27
+ import recursicaTokens from "@recursica/official-release/recursica_tokens.json";
28
+ import recursicaBrand from "@recursica/official-release/recursica_brand.json";
29
+ import recursicaUIKit from "@recursica/official-release/recursica_ui-kit.json";
30
+
31
+ const basePreview = createPreviewConfig({
32
+ defaultTheme: "light",
33
+ recursicaTokensJsonPath: recursicaTokens,
34
+ recursicaBrandJsonPath: recursicaBrand,
35
+ recursicaUIKitJsonPath: recursicaUIKit,
36
+ });
37
+
38
+ const preview: Preview = {
39
+ ...basePreview,
40
+ decorators: [
41
+ (Story) => (
42
+ <MantineProvider>
43
+ <Story />
44
+ </MantineProvider>
45
+ ),
46
+ ...(basePreview.decorators || []),
47
+ ],
48
+ };
49
+
50
+ export default preview;
51
+ `;function c(e){const{dir:r,port:u,mantineAdapterVersion:d="latest",storybookTemplateVersion:f="latest"}=e;return o.mkdirSync(t.join(r,".storybook"),{recursive:!0}),o.writeFileSync(t.join(r,"package.json"),JSON.stringify(l({mantineAdapterVersion:d,storybookTemplateVersion:f,port:u}),null,2)+`
52
+ `),o.writeFileSync(t.join(r,".storybook/main.ts"),y),o.writeFileSync(t.join(r,".storybook/preview.tsx"),g),o.writeFileSync(t.join(r,".gitignore"),`node_modules
53
+ `),r}function b(e){const r=c(e);return{command:"npm install --no-audit --no-fund && npm run storybook",port:e.port,cwd:r,reuseExistingServer:!process.env.CI,timeout:180*1e3}}exports.defineAdapterTesterConfig=a.defineAdapterTesterConfig;exports.getSourceOfTruth=a.getSourceOfTruth;exports.mantineSourceOfTruthWebServer=b;exports.scaffoldMantineSourceOfTruthHarness=c;
54
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/harness/mantineSourceOfTruth.ts"],"sourcesContent":["import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/**\n * Generates a small, throwaway Storybook project that installs\n * `@recursica/mantine-adapter` as a real npm dependency (not a workspace\n * link) and boots a real Storybook from its published `src/**\\/*.stories.tsx`\n * files, using `@recursica/storybook-template`'s exported factories.\n *\n * This lets any repo — including ones that never checked out the Recursica\n * monorepo — run adapter-tester's visual regression suite against Mantine\n * (Recursica's source-of-truth adapter) as one side of the comparison, while\n * the other side is that repo's own already-running local Storybook.\n *\n * See PROPOSAL-installed-package-harness.md for the verified prototype this\n * is built from, and the three upstream gaps it works around.\n */\n\nexport interface MantineSourceOfTruthHarnessOptions {\n /**\n * Directory the harness project is scaffolded into. Regenerated on every\n * call — add it to your .gitignore rather than committing it.\n */\n dir: string;\n /** Port the harness's Storybook dev server boots on. */\n port: number;\n /** npm version/range for @recursica/mantine-adapter. Defaults to \"latest\". */\n mantineAdapterVersion?: string;\n /** npm version/range for @recursica/storybook-template. Defaults to \"latest\". */\n storybookTemplateVersion?: string;\n}\n\nexport interface HarnessWebServerConfig {\n command: string;\n port: number;\n cwd: string;\n reuseExistingServer: boolean;\n timeout: number;\n}\n\n// Peer/dev ranges pinned to what @recursica/mantine-adapter and\n// @recursica/storybook-template themselves require, so the harness can't\n// drift onto an incompatible Mantine or Storybook major version.\nconst MANTINE_CORE_RANGE = \"^8.0.0\";\nconst STORYBOOK_RANGE = \"^10.3.3\";\nconst REACT_RANGE = \"^19.0.0\";\n\n// storybook-template's createMainConfig() defaults its addons list to these\n// three but doesn't declare them as peerDependencies (proposal gap 2) — a\n// harness that skips installing any of them gets a silent \"could not\n// resolve addon\" warning at boot, then a hard runtime crash later when Vite\n// pre-bundles preview.tsx's dependency graph. Installed explicitly here.\nconst DEFAULT_ADDON_DEPENDENCIES = {\n \"@storybook/addon-docs\": STORYBOOK_RANGE,\n \"@storybook/addon-a11y\": STORYBOOK_RANGE,\n \"storybook-dark-mode\": \"^5.0.0\",\n};\n\n// mantine-adapter's Introduction.stories.tsx (Version.tsx/OverStyling.tsx)\n// needs react-markdown, but it's a devDependency there — Storybook-only,\n// never bundled into dist — so an external `npm install` of the published\n// package won't pull it in. The harness boots a real Storybook against\n// src/, so it must provide this itself. Installed explicitly here.\nconst WORKAROUND_DEPENDENCIES = {\n \"react-markdown\": \"^10.1.0\",\n};\n\nfunction harnessPackageJson(options: {\n mantineAdapterVersion: string;\n storybookTemplateVersion: string;\n port: number;\n}) {\n return {\n name: \"adapter-tester-mantine-source-of-truth-harness\",\n private: true,\n type: \"module\",\n scripts: {\n storybook: `storybook dev -p ${options.port}`,\n },\n dependencies: {\n \"@recursica/mantine-adapter\": options.mantineAdapterVersion,\n \"@recursica/storybook-template\": options.storybookTemplateVersion,\n \"@recursica/official-release\": \"latest\",\n \"@recursica/adapter-common\": \"latest\",\n \"@mantine/core\": MANTINE_CORE_RANGE,\n \"@mantine/dates\": MANTINE_CORE_RANGE,\n react: REACT_RANGE,\n \"react-dom\": REACT_RANGE,\n storybook: STORYBOOK_RANGE,\n \"@storybook/react-vite\": STORYBOOK_RANGE,\n ...DEFAULT_ADDON_DEPENDENCIES,\n ...WORKAROUND_DEPENDENCIES,\n },\n };\n}\n\nconst MAIN_TS = `import { createMainConfig } from \"@recursica/storybook-template/main\";\n\nconst config = createMainConfig({\n stories: [\n \"../node_modules/@recursica/mantine-adapter/src/**/*.stories.@(js|jsx|mjs|ts|tsx)\",\n ],\n enableCORS: true,\n});\n\n// react-docgen-typescript can't resolve a TS project for a config file living\n// in .storybook/ when the component source it's docgen'ing lives three\n// directories down inside node_modules — it throws \"Cannot read properties\n// of undefined (reading 'fileExists')\", which surfaces as a plain 404 on\n// preview.tsx. Docgen only powers Storybook's Controls/Docs tables, which\n// this harness never renders, so disabling it is a safe workaround (see\n// PROPOSAL-installed-package-harness.md, gap 3).\nconfig.typescript = { ...config.typescript, reactDocgen: false };\n\nexport default config;\n`;\n\nconst PREVIEW_TSX = `import type { Preview } from \"@storybook/react-vite\";\nimport { createPreviewConfig } from \"@recursica/storybook-template/preview\";\nimport { MantineProvider } from \"@mantine/core\";\nimport \"@mantine/core/styles.css\";\nimport \"@mantine/dates/styles.css\";\nimport \"@recursica/adapter-common/style.css\";\nimport \"@recursica/official-release/recursica_variables_scoped.css\";\nimport recursicaTokens from \"@recursica/official-release/recursica_tokens.json\";\nimport recursicaBrand from \"@recursica/official-release/recursica_brand.json\";\nimport recursicaUIKit from \"@recursica/official-release/recursica_ui-kit.json\";\n\nconst basePreview = createPreviewConfig({\n defaultTheme: \"light\",\n recursicaTokensJsonPath: recursicaTokens,\n recursicaBrandJsonPath: recursicaBrand,\n recursicaUIKitJsonPath: recursicaUIKit,\n});\n\nconst preview: Preview = {\n ...basePreview,\n decorators: [\n (Story) => (\n <MantineProvider>\n <Story />\n </MantineProvider>\n ),\n ...(basePreview.decorators || []),\n ],\n};\n\nexport default preview;\n`;\n\n/** Writes the harness project's files to `options.dir` without booting it. */\nexport function scaffoldMantineSourceOfTruthHarness(\n options: MantineSourceOfTruthHarnessOptions,\n): string {\n const {\n dir,\n port,\n mantineAdapterVersion = \"latest\",\n storybookTemplateVersion = \"latest\",\n } = options;\n\n mkdirSync(join(dir, \".storybook\"), { recursive: true });\n writeFileSync(\n join(dir, \"package.json\"),\n JSON.stringify(\n harnessPackageJson({ mantineAdapterVersion, storybookTemplateVersion, port }),\n null,\n 2,\n ) + \"\\n\",\n );\n writeFileSync(join(dir, \".storybook/main.ts\"), MAIN_TS);\n writeFileSync(join(dir, \".storybook/preview.tsx\"), PREVIEW_TSX);\n writeFileSync(join(dir, \".gitignore\"), \"node_modules\\n\");\n\n return dir;\n}\n\n/**\n * Scaffolds the harness and returns a Playwright `webServer` entry for it.\n * Spread the result directly into `playwright.config.ts`'s `webServer` array.\n */\nexport function mantineSourceOfTruthWebServer(\n options: MantineSourceOfTruthHarnessOptions,\n): HarnessWebServerConfig {\n const dir = scaffoldMantineSourceOfTruthHarness(options);\n return {\n command: \"npm install --no-audit --no-fund && npm run storybook\",\n port: options.port,\n cwd: dir,\n reuseExistingServer: !process.env.CI,\n timeout: 180 * 1000,\n };\n}\n"],"names":["MANTINE_CORE_RANGE","STORYBOOK_RANGE","REACT_RANGE","DEFAULT_ADDON_DEPENDENCIES","WORKAROUND_DEPENDENCIES","harnessPackageJson","options","MAIN_TS","PREVIEW_TSX","scaffoldMantineSourceOfTruthHarness","dir","port","mantineAdapterVersion","storybookTemplateVersion","mkdirSync","join","writeFileSync","mantineSourceOfTruthWebServer"],"mappings":"qKA2CMA,EAAqB,SACrBC,EAAkB,UAClBC,EAAc,UAOdC,EAA6B,CACjC,wBAAyBF,EACzB,wBAAyBA,EACzB,sBAAuB,QACzB,EAOMG,EAA0B,CAC9B,iBAAkB,SACpB,EAEA,SAASC,EAAmBC,EAIzB,CACD,MAAO,CACL,KAAM,iDACN,QAAS,GACT,KAAM,SACN,QAAS,CACP,UAAW,oBAAoBA,EAAQ,IAAI,EAAA,EAE7C,aAAc,CACZ,6BAA8BA,EAAQ,sBACtC,gCAAiCA,EAAQ,yBACzC,8BAA+B,SAC/B,4BAA6B,SAC7B,gBAAiBN,EACjB,iBAAkBA,EAClB,MAAOE,EACP,YAAaA,EACb,UAAWD,EACX,wBAAyBA,EACzB,GAAGE,EACH,GAAGC,CAAA,CACL,CAEJ,CAEA,MAAMG,EAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBVC,EAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkCb,SAASC,EACdH,EACQ,CACR,KAAM,CACJ,IAAAI,EACA,KAAAC,EACA,sBAAAC,EAAwB,SACxB,yBAAAC,EAA2B,QAAA,EACzBP,EAEJQ,OAAAA,EAAAA,UAAUC,EAAAA,KAAKL,EAAK,YAAY,EAAG,CAAE,UAAW,GAAM,EACtDM,EAAAA,cACED,EAAAA,KAAKL,EAAK,cAAc,EACxB,KAAK,UACHL,EAAmB,CAAE,sBAAAO,EAAuB,yBAAAC,EAA0B,KAAAF,EAAM,EAC5E,KACA,CAAA,EACE;AAAA,CAAA,EAENK,EAAAA,cAAcD,EAAAA,KAAKL,EAAK,oBAAoB,EAAGH,CAAO,EACtDS,EAAAA,cAAcD,EAAAA,KAAKL,EAAK,wBAAwB,EAAGF,CAAW,EAC9DQ,EAAAA,cAAcD,EAAAA,KAAKL,EAAK,YAAY,EAAG;AAAA,CAAgB,EAEhDA,CACT,CAMO,SAASO,EACdX,EACwB,CACxB,MAAMI,EAAMD,EAAoCH,CAAO,EACvD,MAAO,CACL,QAAS,wDACT,KAAMA,EAAQ,KACd,IAAKI,EACL,oBAAqB,CAAC,QAAQ,IAAI,GAClC,QAAS,IAAM,GAAA,CAEnB"}
@@ -0,0 +1,3 @@
1
+ export { defineAdapterTesterConfig, getSourceOfTruth, type AdapterTarget, type AdapterTesterConfig, } from './config.js';
2
+ export { scaffoldMantineSourceOfTruthHarness, mantineSourceOfTruthWebServer, type MantineSourceOfTruthHarnessOptions, type HarnessWebServerConfig, } from './harness/mantineSourceOfTruth.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,KAAK,aAAa,EAClB,KAAK,mBAAmB,GACzB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,mCAAmC,EACnC,6BAA6B,EAC7B,KAAK,kCAAkC,EACvC,KAAK,sBAAsB,GAC5B,MAAM,mCAAmC,CAAC"}