@recursica/adapter-tester 3.0.0 → 4.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.
- package/README.md +36 -21
- package/dist/adapter-tester.schema.json.d.ts +18 -9
- package/dist/cli.cjs +31 -18
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +231 -181
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +23 -12
- package/dist/config.d.ts.map +1 -1
- package/dist/fileConfig.d.ts +14 -5
- package/dist/fileConfig.d.ts.map +1 -1
- package/dist/golden/diffPng.d.ts +12 -7
- package/dist/golden/diffPng.d.ts.map +1 -1
- package/dist/golden/manifestStore.d.ts +19 -1
- package/dist/golden/manifestStore.d.ts.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/testing/runVisualRegression.d.ts +46 -21
- package/dist/testing/runVisualRegression.d.ts.map +1 -1
- package/dist/testing.cjs +3 -3
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +251 -165
- package/dist/testing.js.map +1 -1
- package/package.json +2 -6
- package/src/adapter-tester.schema.json +18 -9
package/dist/cli.js
CHANGED
|
@@ -1,323 +1,373 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn as
|
|
3
|
-
import { readFileSync as S, existsSync as
|
|
4
|
-
import { dirname as
|
|
5
|
-
import { fileURLToPath as
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import { createProxyMiddleware as
|
|
9
|
-
import { m as
|
|
10
|
-
import { a as
|
|
11
|
-
const
|
|
12
|
-
function
|
|
13
|
-
return new Promise((
|
|
14
|
-
const e = new
|
|
2
|
+
import { spawn as F, spawnSync as B } from "node:child_process";
|
|
3
|
+
import { readFileSync as S, existsSync as M, mkdirSync as W, writeFileSync as A } from "node:fs";
|
|
4
|
+
import { dirname as I, join as a, resolve as E } from "node:path";
|
|
5
|
+
import { fileURLToPath as V, pathToFileURL as q } from "node:url";
|
|
6
|
+
import H from "node:net";
|
|
7
|
+
import K from "express";
|
|
8
|
+
import { createProxyMiddleware as Y } from "http-proxy-middleware";
|
|
9
|
+
import { m as X } from "./mantineSourceOfTruth-BUkqMNEo.js";
|
|
10
|
+
import { a as z, i as Q } from "./index-DqQzFSAH.js";
|
|
11
|
+
const j = I(V(import.meta.url));
|
|
12
|
+
function O(t) {
|
|
13
|
+
return new Promise((r) => {
|
|
14
|
+
const e = new H.Socket();
|
|
15
15
|
e.setTimeout(200), e.once("connect", () => {
|
|
16
|
-
e.destroy(),
|
|
16
|
+
e.destroy(), r(!0);
|
|
17
17
|
}), e.once("timeout", () => {
|
|
18
|
-
e.destroy(),
|
|
18
|
+
e.destroy(), r(!1);
|
|
19
19
|
}), e.once("error", () => {
|
|
20
|
-
e.destroy(),
|
|
20
|
+
e.destroy(), r(!1);
|
|
21
21
|
}), e.connect(t, "127.0.0.1");
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
-
async function
|
|
24
|
+
async function $(t, r = 6e4) {
|
|
25
25
|
const e = Date.now();
|
|
26
|
-
for (; Date.now() - e <
|
|
27
|
-
if (await
|
|
28
|
-
await new Promise((
|
|
26
|
+
for (; Date.now() - e < r; ) {
|
|
27
|
+
if (await O(t)) return !0;
|
|
28
|
+
await new Promise((o) => setTimeout(o, 500));
|
|
29
29
|
}
|
|
30
30
|
return !1;
|
|
31
31
|
}
|
|
32
|
-
function
|
|
32
|
+
function N(t) {
|
|
33
33
|
return Number(new URL(t).port);
|
|
34
34
|
}
|
|
35
|
-
function
|
|
36
|
-
var
|
|
35
|
+
function L(t, r) {
|
|
36
|
+
var o, s;
|
|
37
37
|
console.log(
|
|
38
|
-
`[Dev Launcher] Port ${
|
|
38
|
+
`[Dev Launcher] Port ${r.port} is inactive. Launching Storybook for ${t}...`
|
|
39
39
|
);
|
|
40
|
-
const e =
|
|
41
|
-
cwd:
|
|
40
|
+
const e = F(r.command, {
|
|
41
|
+
cwd: r.cwd,
|
|
42
42
|
stdio: "pipe",
|
|
43
43
|
shell: !0
|
|
44
44
|
});
|
|
45
|
-
return (
|
|
46
|
-
for (const
|
|
45
|
+
return (o = e.stdout) == null || o.on("data", (n) => {
|
|
46
|
+
for (const p of n.toString().split(`
|
|
47
47
|
`))
|
|
48
|
-
|
|
49
|
-
}), (s = e.stderr) == null || s.on("data", (
|
|
50
|
-
for (const
|
|
48
|
+
p.trim() && console.log(`[${t} SB] ${p.trim()}`);
|
|
49
|
+
}), (s = e.stderr) == null || s.on("data", (n) => {
|
|
50
|
+
for (const p of n.toString().split(`
|
|
51
51
|
`))
|
|
52
|
-
|
|
53
|
-
}), e.on("error", (
|
|
54
|
-
console.error(`[${t} SB ERROR] Failed to start process:`,
|
|
52
|
+
p.trim() && console.error(`[${t} SB ERROR] ${p.trim()}`);
|
|
53
|
+
}), e.on("error", (n) => {
|
|
54
|
+
console.error(`[${t} SB ERROR] Failed to start process:`, n);
|
|
55
55
|
}), e;
|
|
56
56
|
}
|
|
57
|
-
function
|
|
58
|
-
const
|
|
59
|
-
console.log(`[Dev Launcher] Auto-launching browser: ${t}`),
|
|
57
|
+
function Z(t) {
|
|
58
|
+
const r = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
59
|
+
console.log(`[Dev Launcher] Auto-launching browser: ${t}`), F(r, [t], { shell: process.platform === "win32" }).on(
|
|
60
60
|
"error",
|
|
61
61
|
(e) => {
|
|
62
62
|
console.error("[Dev Launcher] Failed to auto-launch browser:", e);
|
|
63
63
|
}
|
|
64
64
|
);
|
|
65
65
|
}
|
|
66
|
-
function
|
|
67
|
-
const [
|
|
68
|
-
if (!(
|
|
66
|
+
function ee(t, r, e = {}) {
|
|
67
|
+
const [o, s] = t.targets, [n, p] = r;
|
|
68
|
+
if (!(o != null && o.sourceOfTruth) || !s || s.sourceOfTruth || !n || !p)
|
|
69
69
|
throw new Error(
|
|
70
70
|
"adapter-tester dev mode requires exactly two targets: [sourceOfTruth, target] — check adapter-tester.config.json."
|
|
71
71
|
);
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
if (
|
|
75
|
-
|
|
72
|
+
const y = e.port ?? 6010, f = N(o.url), m = N(s.url), i = K(), b = a(j, "../public"), w = a(j, "../report-header.txt");
|
|
73
|
+
i.get("/", (d, l, c) => {
|
|
74
|
+
if (d.query.path) {
|
|
75
|
+
c();
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
|
-
const
|
|
78
|
+
const x = S(a(b, "index.html"), "utf8").replace(
|
|
79
79
|
"<head>",
|
|
80
80
|
`<head>
|
|
81
81
|
<script>window.__ADAPTER_TESTER__ = ${JSON.stringify({
|
|
82
82
|
ownName: s.name,
|
|
83
|
-
sourceOfTruthName:
|
|
84
|
-
sourceOfTruthPort:
|
|
83
|
+
sourceOfTruthName: o.name,
|
|
84
|
+
sourceOfTruthPort: f
|
|
85
85
|
})};<\/script>`
|
|
86
86
|
);
|
|
87
|
-
|
|
88
|
-
}),
|
|
87
|
+
l.send(x);
|
|
88
|
+
}), i.get("/report-header.txt", (d, l) => {
|
|
89
89
|
try {
|
|
90
|
-
|
|
90
|
+
l.type("text/plain").send(S(w, "utf8"));
|
|
91
91
|
} catch {
|
|
92
|
-
|
|
92
|
+
l.status(500).send("Error loading report-header.txt");
|
|
93
93
|
}
|
|
94
|
-
}),
|
|
94
|
+
}), i.use(
|
|
95
95
|
"/",
|
|
96
|
-
|
|
97
|
-
target: `http://localhost:${
|
|
96
|
+
Y({
|
|
97
|
+
target: `http://localhost:${m}`,
|
|
98
98
|
changeOrigin: !0,
|
|
99
99
|
ws: !0
|
|
100
100
|
})
|
|
101
101
|
);
|
|
102
|
-
const
|
|
102
|
+
const g = [];
|
|
103
103
|
let v = !1;
|
|
104
|
-
const
|
|
104
|
+
const k = () => {
|
|
105
105
|
if (!v) {
|
|
106
106
|
v = !0, console.log(
|
|
107
107
|
`
|
|
108
108
|
[Dev Launcher] Shutting down Dev Mode server and spawned Storybooks...`
|
|
109
109
|
);
|
|
110
|
-
for (const
|
|
110
|
+
for (const d of g) d.kill("SIGINT");
|
|
111
111
|
process.exit(0);
|
|
112
112
|
}
|
|
113
113
|
};
|
|
114
|
-
process.on("SIGINT",
|
|
114
|
+
process.on("SIGINT", k), process.on("SIGTERM", k), i.listen(y, async () => {
|
|
115
115
|
console.log(`
|
|
116
116
|
====================================================
|
|
117
117
|
🚀 Adapter Dev Mode proxy running at:
|
|
118
|
-
http://localhost:${
|
|
118
|
+
http://localhost:${y}
|
|
119
119
|
====================================================
|
|
120
120
|
`);
|
|
121
|
-
const [
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
const [d, l] = await Promise.all([
|
|
122
|
+
O(f),
|
|
123
|
+
O(m)
|
|
124
124
|
]);
|
|
125
|
-
|
|
126
|
-
`[Dev Launcher] ${
|
|
127
|
-
) :
|
|
128
|
-
`[Dev Launcher] ${s.name} is already running on port ${
|
|
129
|
-
) :
|
|
130
|
-
const
|
|
131
|
-
|
|
125
|
+
d ? console.log(
|
|
126
|
+
`[Dev Launcher] ${o.name} is already running on port ${f}.`
|
|
127
|
+
) : g.push(L(o.name, n)), l ? console.log(
|
|
128
|
+
`[Dev Launcher] ${s.name} is already running on port ${m}.`
|
|
129
|
+
) : g.push(L(s.name, p));
|
|
130
|
+
const c = [];
|
|
131
|
+
d || c.push($(f)), l || c.push($(m)), c.length > 0 ? (console.log("[Dev Launcher] Waiting for Storybooks to be responsive..."), (await Promise.all(c)).every(Boolean) ? console.log("[Dev Launcher] All Storybooks are active and responsive!") : console.warn(
|
|
132
132
|
"[Dev Launcher] Warning: some Storybooks timed out during startup, but proceeding..."
|
|
133
|
-
)) : console.log("[Dev Launcher] Both Storybooks already active."),
|
|
133
|
+
)) : console.log("[Dev Launcher] Both Storybooks already active."), Z(`http://localhost:${y}`);
|
|
134
134
|
});
|
|
135
135
|
}
|
|
136
|
-
const
|
|
137
|
-
$schema:
|
|
138
|
-
$id:
|
|
139
|
-
title:
|
|
140
|
-
description:
|
|
141
|
-
type:
|
|
142
|
-
additionalProperties:
|
|
143
|
-
properties:
|
|
144
|
-
},
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
function
|
|
148
|
-
if (
|
|
149
|
-
const e = (
|
|
150
|
-
var
|
|
151
|
-
const s = (
|
|
152
|
-
return ` - ${
|
|
136
|
+
const te = "http://json-schema.org/draft-07/schema#", re = "https://github.com/borderux/recursica/tree/main/packages/adapter-tester/src/adapter-tester.schema.json", oe = "adapter-tester.config.json", se = "Configuration for @recursica/adapter-tester. All fields are optional — an adapter repo with a normal `storybook` npm script needs no config file at all.", ne = "object", ie = !1, ae = { $schema: { type: "string", description: 'Optional pointer to this schema for editor autocomplete, e.g. "https://raw.githubusercontent.com/borderux/recursica/main/packages/adapter-tester/src/adapter-tester.schema.json".' }, name: { type: "string", 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").` }, storybook: { type: "object", additionalProperties: !1, description: "This project's own Storybook target.", properties: { port: { type: "integer", minimum: 1, maximum: 65535, description: "Port the target's Storybook is served on. Auto-detected from this project's own `scripts.storybook` (a `-p <port>`/`--port <port>` flag) when omitted, falling back to Storybook's own default of 6006." }, command: { type: "string", description: "Command that boots the target's Storybook. Defaults to `npm run storybook`." }, cwd: { type: "string", description: 'Directory the command runs in, relative to the config file. Defaults to ".".' } } }, sourceOfTruth: { description: 'The comparison target. Defaults to `{ "type": "mantine-harness" }`.', oneOf: [{ type: "object", additionalProperties: !1, description: "Default mode: boots a throwaway harness that installs the published @recursica/mantine-adapter from npm — no monorepo checkout required.", properties: { type: { const: "mantine-harness" }, port: { type: "integer", minimum: 1, maximum: 65535, description: "Port the throwaway harness's Storybook is served on. Defaults to 6011." }, mantineAdapterVersion: { type: "string", description: "npm version/tag of @recursica/mantine-adapter to install in the harness. Defaults to `latest`." }, storybookTemplateVersion: { type: "string", description: "npm version/tag of @recursica/storybook-template to install in the harness. Defaults to `latest`." } } }, { type: "object", additionalProperties: !1, description: "Non-standard mode: points at an already-addressable Storybook — e.g. a sibling workspace package's own Storybook inside this monorepo.", required: ["type", "port"], properties: { type: { const: "url" }, name: { type: "string", description: 'Label for the source-of-truth target. Defaults to "Mantine".' }, port: { type: "integer", minimum: 1, maximum: 65535, description: "Port the source-of-truth Storybook is served on." }, command: { type: "string", description: "Command that boots the source-of-truth Storybook. Defaults to `npm run storybook`." }, cwd: { type: "string", description: 'Directory the command runs in, relative to the config file. Defaults to ".".' } } }] }, isSourceOfTruthAdapter: { type: "boolean", 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." }, diffThresholdPixels: { type: "number", minimum: 0, 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." }, stories: { type: "object", additionalProperties: { type: "object", additionalProperties: !1, properties: { threshold: { type: "number", minimum: 0, description: "Diff threshold override for matching stories, in mismatched pixels. Overrides `diffThresholdPixels`." }, exclude: { type: "boolean", description: "Skip matching stories entirely — no own-drift check, no divergence check, no golden captured. For stories that don't have a cross-adapter counterpart; use `excludeTitlePrefixes` to drop a whole title instead." } } }, description: "Per-story overrides, keyed by story id prefix (a story matches if its id equals the key or starts with it). When more than one key matches a story, the longest (most specific) key wins." }, excludeTitlePrefixes: { type: "array", items: { type: "string" }, description: "Storybook title prefixes to exclude from comparison, in addition to the built-in denylist (`Theme/*`, `Tokens/*`, `Introduction`)." } }, ce = {
|
|
137
|
+
$schema: te,
|
|
138
|
+
$id: re,
|
|
139
|
+
title: oe,
|
|
140
|
+
description: se,
|
|
141
|
+
type: ne,
|
|
142
|
+
additionalProperties: ie,
|
|
143
|
+
properties: ae
|
|
144
|
+
}, pe = Q, U = new z.Ajv({ allErrors: !0, strict: !0 });
|
|
145
|
+
pe(U);
|
|
146
|
+
const R = U.compile(ce);
|
|
147
|
+
function de(t, r) {
|
|
148
|
+
if (R(t)) return;
|
|
149
|
+
const e = (R.errors ?? []).map((o) => {
|
|
150
|
+
var n;
|
|
151
|
+
const s = (n = o.params) != null && n.additionalProperty ? ` '${o.params.additionalProperty}'` : "";
|
|
152
|
+
return ` - ${o.instancePath || "root"} ${o.message}${s}`;
|
|
153
153
|
}).join(`
|
|
154
154
|
`);
|
|
155
|
-
throw new Error(`Invalid ${
|
|
155
|
+
throw new Error(`Invalid ${r}:
|
|
156
156
|
${e}`);
|
|
157
157
|
}
|
|
158
|
-
const
|
|
159
|
-
function
|
|
160
|
-
const
|
|
161
|
-
if (!
|
|
158
|
+
const le = "@recursica/mantine-adapter", ue = "adapter-tester.config.json", C = "npm run storybook", he = 6006, fe = 6011, _ = "Mantine", me = 3500;
|
|
159
|
+
function G(t) {
|
|
160
|
+
const r = a(t, "package.json");
|
|
161
|
+
if (!M(r))
|
|
162
162
|
throw new Error(
|
|
163
163
|
`No package.json found in ${t} — run adapter-tester from the root of the project being tested.`
|
|
164
164
|
);
|
|
165
|
-
return JSON.parse(S(
|
|
165
|
+
return JSON.parse(S(r, "utf8"));
|
|
166
166
|
}
|
|
167
|
-
function
|
|
167
|
+
function ge(t) {
|
|
168
168
|
var s;
|
|
169
|
-
const e = (s =
|
|
170
|
-
return
|
|
169
|
+
const e = (s = G(t).scripts) == null ? void 0 : s.storybook, o = e == null ? void 0 : e.match(/(?:-p|--port)[ =](\d+)/);
|
|
170
|
+
return o ? Number(o[1]) : he;
|
|
171
171
|
}
|
|
172
|
-
function
|
|
173
|
-
const e =
|
|
172
|
+
function ye(t) {
|
|
173
|
+
const e = G(t).name;
|
|
174
174
|
return e ? e.split("/").pop() : "Adapter";
|
|
175
175
|
}
|
|
176
|
-
function
|
|
177
|
-
const
|
|
178
|
-
if (!
|
|
176
|
+
function we(t) {
|
|
177
|
+
const r = a(t, ue);
|
|
178
|
+
if (!M(r))
|
|
179
179
|
return {};
|
|
180
|
-
const e = JSON.parse(S(
|
|
181
|
-
return
|
|
180
|
+
const e = JSON.parse(S(r, "utf8"));
|
|
181
|
+
return de(e, r), e;
|
|
182
182
|
}
|
|
183
|
-
function
|
|
184
|
-
var
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
183
|
+
function ve(t, r = {}) {
|
|
184
|
+
var k, d, l;
|
|
185
|
+
const e = we(t), o = e.isSourceOfTruthAdapter ?? !1;
|
|
186
|
+
if (r.mantineAdapterVersion && o)
|
|
187
|
+
throw new Error(
|
|
188
|
+
"--source-of-truth-version has no effect on the source-of-truth adapter's own config (isSourceOfTruthAdapter: true) — there's nothing to pin a version for."
|
|
189
|
+
);
|
|
190
|
+
const s = e.name ?? ye(t), n = ((k = e.storybook) == null ? void 0 : k.port) ?? ge(t), p = ((d = e.storybook) == null ? void 0 : d.command) ?? C, y = E(t, ((l = e.storybook) == null ? void 0 : l.cwd) ?? "."), f = {
|
|
191
|
+
command: p,
|
|
192
|
+
port: n,
|
|
193
|
+
cwd: y,
|
|
189
194
|
reuseExistingServer: !process.env.CI,
|
|
190
195
|
timeout: 120 * 1e3
|
|
191
|
-
},
|
|
192
|
-
diffThresholdPixels:
|
|
193
|
-
|
|
194
|
-
excludeTitlePrefixes:
|
|
195
|
-
excludeStoryIds: o.excludeStoryIds,
|
|
196
|
+
}, m = {
|
|
197
|
+
diffThresholdPixels: e.diffThresholdPixels ?? me,
|
|
198
|
+
stories: e.stories,
|
|
199
|
+
excludeTitlePrefixes: e.excludeTitlePrefixes,
|
|
196
200
|
// Keyed off `ownCwd`, not `cwd` — the project actually being tested, not
|
|
197
|
-
// wherever the config file happens to live. Matters
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
goldenDir:
|
|
203
|
-
isSourceOfTruthAdapter:
|
|
201
|
+
// wherever the config file happens to live. Matters whenever
|
|
202
|
+
// `storybook.cwd` points at a sibling package: its goldens must resolve
|
|
203
|
+
// to that package's own `test/golden/` (the same directory used when
|
|
204
|
+
// that package runs adapter-tester directly), not a directory under
|
|
205
|
+
// wherever this config file lives.
|
|
206
|
+
goldenDir: a(y, "test", "golden"),
|
|
207
|
+
isSourceOfTruthAdapter: o,
|
|
204
208
|
// Overwritten by the CLI from --update-golden/--approve-divergence.
|
|
205
|
-
goldenMode: "check"
|
|
209
|
+
goldenMode: "check",
|
|
210
|
+
// Overwritten by the CLI from --divergence-only.
|
|
211
|
+
checkMode: "own"
|
|
206
212
|
};
|
|
207
|
-
if (
|
|
213
|
+
if (o)
|
|
208
214
|
return {
|
|
209
215
|
engineConfig: {
|
|
210
|
-
...
|
|
211
|
-
targets: [{ name:
|
|
216
|
+
...m,
|
|
217
|
+
targets: [{ name: s, url: `http://localhost:${n}` }]
|
|
212
218
|
},
|
|
213
|
-
webServers: [
|
|
219
|
+
webServers: [f]
|
|
214
220
|
};
|
|
215
|
-
const
|
|
221
|
+
const i = e.sourceOfTruth ?? {
|
|
216
222
|
type: "mantine-harness"
|
|
217
223
|
};
|
|
218
|
-
let
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
let b, w, g, v;
|
|
225
|
+
if (i.type === "url") {
|
|
226
|
+
if (r.mantineAdapterVersion)
|
|
227
|
+
throw new Error(
|
|
228
|
+
`--source-of-truth-version has no effect with sourceOfTruth.type: "url" — that mode reads a sibling package's local checkout directly, not a published version.`
|
|
229
|
+
);
|
|
230
|
+
b = i.name ?? _, w = i.port;
|
|
231
|
+
const c = E(t, i.cwd ?? ".");
|
|
232
|
+
g = {
|
|
233
|
+
command: i.command ?? C,
|
|
234
|
+
port: i.port,
|
|
235
|
+
cwd: c,
|
|
226
236
|
reuseExistingServer: !process.env.CI,
|
|
227
237
|
timeout: 120 * 1e3
|
|
228
|
-
},
|
|
238
|
+
}, v = {
|
|
229
239
|
type: "local",
|
|
230
|
-
dir:
|
|
240
|
+
dir: a(c, "test", "golden")
|
|
231
241
|
};
|
|
232
|
-
} else
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
242
|
+
} else {
|
|
243
|
+
b = _, w = i.port ?? fe;
|
|
244
|
+
const c = r.mantineAdapterVersion ?? i.mantineAdapterVersion;
|
|
245
|
+
g = X({
|
|
246
|
+
dir: a(t, ".adapter-tester/mantine-harness"),
|
|
247
|
+
port: w,
|
|
248
|
+
mantineAdapterVersion: c,
|
|
249
|
+
storybookTemplateVersion: i.storybookTemplateVersion
|
|
250
|
+
}), v = {
|
|
239
251
|
type: "npm",
|
|
240
|
-
packageName:
|
|
241
|
-
versionSpec:
|
|
242
|
-
cacheDir:
|
|
252
|
+
packageName: le,
|
|
253
|
+
versionSpec: c ?? "latest",
|
|
254
|
+
cacheDir: a(t, ".adapter-tester/mantine-golden-cache")
|
|
243
255
|
};
|
|
256
|
+
}
|
|
244
257
|
return {
|
|
245
258
|
engineConfig: {
|
|
246
|
-
...
|
|
259
|
+
...m,
|
|
247
260
|
targets: [
|
|
248
261
|
{
|
|
249
|
-
name:
|
|
250
|
-
url: `http://localhost:${
|
|
262
|
+
name: b,
|
|
263
|
+
url: `http://localhost:${w}`,
|
|
251
264
|
sourceOfTruth: !0
|
|
252
265
|
},
|
|
253
|
-
{ name:
|
|
266
|
+
{ name: s, url: `http://localhost:${n}` }
|
|
254
267
|
],
|
|
255
|
-
sourceOfTruthGolden:
|
|
268
|
+
sourceOfTruthGolden: v
|
|
256
269
|
},
|
|
257
270
|
// [sourceOfTruth, own] — Dev Mode needs both; the automated golden run
|
|
258
271
|
// (cli.ts) only ever boots the last entry, since its divergence check
|
|
259
272
|
// reads stored golden files, not a live source-of-truth page.
|
|
260
|
-
webServers: [
|
|
273
|
+
webServers: [g, f]
|
|
261
274
|
};
|
|
262
275
|
}
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
276
|
+
const be = I(V(import.meta.url)), ke = q(a(be, "testing.js")).href, h = process.argv.slice(2);
|
|
277
|
+
let J;
|
|
278
|
+
const T = h.indexOf("--source-of-truth-version");
|
|
279
|
+
if (T !== -1) {
|
|
280
|
+
const t = h[T + 1];
|
|
281
|
+
if (t === void 0 || t.startsWith("--"))
|
|
282
|
+
throw new Error(
|
|
283
|
+
"--source-of-truth-version requires a value, e.g. --source-of-truth-version 0.53.0"
|
|
284
|
+
);
|
|
285
|
+
J = t, h.splice(T, 2);
|
|
286
|
+
}
|
|
287
|
+
const P = process.cwd(), { engineConfig: u, webServers: D } = ve(P, {
|
|
288
|
+
mantineAdapterVersion: J
|
|
289
|
+
});
|
|
290
|
+
h.includes("--update-golden") ? u.goldenMode = "update-golden" : h.includes("--approve-divergence") && (u.goldenMode = "approve-divergence");
|
|
291
|
+
if (h.includes("--divergence-only")) {
|
|
292
|
+
if (u.isSourceOfTruthAdapter)
|
|
293
|
+
throw new Error(
|
|
294
|
+
"--divergence-only has nothing to diverge from — isSourceOfTruthAdapter is true in this project's adapter-tester.config.json."
|
|
295
|
+
);
|
|
296
|
+
u.checkMode = "divergence";
|
|
297
|
+
} else if (u.goldenMode === "approve-divergence")
|
|
298
|
+
throw new Error(
|
|
299
|
+
"--approve-divergence only makes sense with --divergence-only — there's nothing to approve without the divergence check running."
|
|
300
|
+
);
|
|
301
|
+
h.includes("--dry-run") && (console.log(JSON.stringify({ engineConfig: u, webServers: D }, null, 2)), process.exit(0));
|
|
302
|
+
const Se = /* @__PURE__ */ new Set([
|
|
267
303
|
"--dry-run",
|
|
268
304
|
"--serve",
|
|
269
305
|
"--update-golden",
|
|
270
|
-
"--approve-divergence"
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
306
|
+
"--approve-divergence",
|
|
307
|
+
"--divergence-only"
|
|
308
|
+
]), Te = h.filter((t) => !Se.has(t));
|
|
309
|
+
if (h.includes("--serve")) {
|
|
310
|
+
if (u.isSourceOfTruthAdapter)
|
|
274
311
|
throw new Error(
|
|
275
312
|
"Dev Mode (--serve) has nothing to sync this project's Storybook against — isSourceOfTruthAdapter is true in this project's adapter-tester.config.json."
|
|
276
313
|
);
|
|
277
|
-
|
|
314
|
+
ee(u, D);
|
|
278
315
|
} else
|
|
279
|
-
|
|
280
|
-
function
|
|
281
|
-
const e =
|
|
282
|
-
|
|
283
|
-
|
|
316
|
+
Oe(D.slice(-1), u);
|
|
317
|
+
function Oe(t, r) {
|
|
318
|
+
const e = a(P, ".adapter-tester/run");
|
|
319
|
+
W(e, { recursive: !0 }), A(
|
|
320
|
+
a(e, "playwright.config.js"),
|
|
284
321
|
`// Generated by \`adapter-tester\` — do not edit, regenerated on every run.
|
|
285
322
|
import { defineConfig, devices } from "@playwright/test";
|
|
286
323
|
|
|
287
324
|
export default defineConfig({
|
|
288
325
|
testDir: ${JSON.stringify(e)},
|
|
289
|
-
//
|
|
290
|
-
// story's
|
|
291
|
-
//
|
|
292
|
-
|
|
326
|
+
// Stories run across Playwright's default worker pool — each golden check
|
|
327
|
+
// only reads/writes its own story's manifest.json entry, under a lock, so
|
|
328
|
+
// concurrent workers never race each other (see \`updateManifestEntry\`).
|
|
329
|
+
// Override with \`--workers <n>\` (forwarded straight through to Playwright).
|
|
330
|
+
fullyParallel: true,
|
|
293
331
|
forbidOnly: !!process.env.CI,
|
|
294
332
|
retries: process.env.CI ? 2 : 0,
|
|
295
|
-
workers: 1,
|
|
296
333
|
reporter: [["html", { open: "never" }], ["list"]],
|
|
297
334
|
use: { trace: "on-first-retry" },
|
|
298
335
|
projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }],
|
|
299
336
|
webServer: ${JSON.stringify(t, null, 2)},
|
|
300
337
|
});
|
|
301
338
|
`
|
|
302
|
-
),
|
|
303
|
-
|
|
339
|
+
), A(
|
|
340
|
+
a(e, "visual-regression.spec.js"),
|
|
304
341
|
`// Generated by \`adapter-tester\` — do not edit, regenerated on every run.
|
|
305
|
-
import {
|
|
342
|
+
import { test } from "@playwright/test";
|
|
343
|
+
import { resolveVisualRegressionPlan } from ${JSON.stringify(ke)};
|
|
344
|
+
|
|
345
|
+
// \`test.describe\`/\`test\` are called here, not inside resolveVisualRegressionPlan,
|
|
346
|
+
// so Playwright's HTML report groups the suite under this file instead of a
|
|
347
|
+
// sourcemapped path into adapter-tester's own library code.
|
|
348
|
+
const { ownTargetName, suiteLabel, stories, checkStory } =
|
|
349
|
+
await resolveVisualRegressionPlan(${JSON.stringify(r, null, 2)});
|
|
306
350
|
|
|
307
|
-
|
|
351
|
+
test.describe(\`\${ownTargetName} — \${suiteLabel}\`, () => {
|
|
352
|
+
for (const story of stories) {
|
|
353
|
+
test(story.id, async ({ browser }, testInfo) => {
|
|
354
|
+
await checkStory(story, browser, testInfo);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
});
|
|
308
358
|
`
|
|
309
359
|
);
|
|
310
|
-
const
|
|
360
|
+
const o = B(
|
|
311
361
|
"npx",
|
|
312
362
|
[
|
|
313
363
|
"playwright",
|
|
314
364
|
"test",
|
|
315
365
|
"--config",
|
|
316
|
-
|
|
317
|
-
...
|
|
366
|
+
a(e, "playwright.config.js"),
|
|
367
|
+
...Te
|
|
318
368
|
],
|
|
319
|
-
{ stdio: "inherit", cwd:
|
|
369
|
+
{ stdio: "inherit", cwd: P, shell: process.platform === "win32" }
|
|
320
370
|
);
|
|
321
|
-
process.exit(
|
|
371
|
+
process.exit(o.status ?? 1);
|
|
322
372
|
}
|
|
323
373
|
//# sourceMappingURL=cli.js.map
|