@testivai/witness 2.4.0 → 2.5.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/dist/baselines/store.d.ts +17 -0
- package/dist/baselines/store.d.ts.map +1 -1
- package/dist/baselines/store.js +30 -1
- package/dist/baselines/store.js.map +1 -1
- package/dist/bin/testivai.js +2 -0
- package/dist/bin/testivai.js.map +1 -1
- package/dist/capture/environment.d.ts +217 -0
- package/dist/capture/environment.d.ts.map +1 -0
- package/dist/capture/environment.js +399 -0
- package/dist/capture/environment.js.map +1 -0
- package/dist/commands/env.d.ts +37 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +113 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/config/local-config.d.ts +14 -0
- package/dist/config/local-config.d.ts.map +1 -1
- package/dist/config/local-config.js +1 -0
- package/dist/config/local-config.js.map +1 -1
- package/dist/config/validate-config.d.ts.map +1 -1
- package/dist/config/validate-config.js +12 -0
- package/dist/config/validate-config.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/dist/report/anchor.d.ts +18 -0
- package/dist/report/anchor.d.ts.map +1 -0
- package/dist/report/anchor.js +51 -0
- package/dist/report/anchor.js.map +1 -0
- package/dist/report/compare.d.ts +7 -0
- package/dist/report/compare.d.ts.map +1 -1
- package/dist/report/compare.js +22 -2
- package/dist/report/compare.js.map +1 -1
- package/dist/report/environment.d.ts +101 -0
- package/dist/report/environment.d.ts.map +1 -0
- package/dist/report/environment.js +331 -0
- package/dist/report/environment.js.map +1 -0
- package/dist/report/generator.d.ts +7 -2
- package/dist/report/generator.d.ts.map +1 -1
- package/dist/report/generator.js +33 -6
- package/dist/report/generator.js.map +1 -1
- package/dist/report/index.d.ts +5 -2
- package/dist/report/index.d.ts.map +1 -1
- package/dist/report/index.js +11 -1
- package/dist/report/index.js.map +1 -1
- package/dist/report/results.d.ts +33 -0
- package/dist/report/results.d.ts.map +1 -1
- package/dist/report/template.d.ts +28 -1
- package/dist/report/template.d.ts.map +1 -1
- package/dist/report/template.js +127 -15
- package/dist/report/template.js.map +1 -1
- package/dist/report/validate.d.ts +50 -0
- package/dist/report/validate.d.ts.map +1 -0
- package/dist/report/validate.js +343 -0
- package/dist/report/validate.js.map +1 -0
- package/dist/standalone/run.d.ts +12 -0
- package/dist/standalone/run.d.ts.map +1 -1
- package/dist/standalone/run.js +37 -1
- package/dist/standalone/run.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Capture provenance — where a capture came from.
|
|
4
|
+
*
|
|
5
|
+
* When the same URL renders differently, the question a reviewer needs
|
|
6
|
+
* answered first is whether the PAGE changed or the MACHINE did: a new
|
|
7
|
+
* browser build, another OS or distro (another set of fonts), another device
|
|
8
|
+
* scale, another way of taking the screenshot. Every adapter records the
|
|
9
|
+
* same `environment` block in the capture's metadata.json; approving a
|
|
10
|
+
* capture freezes it into the baseline's metadata.json, and the compare
|
|
11
|
+
* reads the two side by side (report/environment.ts).
|
|
12
|
+
*
|
|
13
|
+
* The block is plain JSON, so non-JS adapters write the same shape
|
|
14
|
+
* (docs/extension-api.md). Every part an adapter cannot observe is left out,
|
|
15
|
+
* never guessed: an absent field reads as "not recorded", not as a change.
|
|
16
|
+
*/
|
|
17
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
+
}
|
|
23
|
+
Object.defineProperty(o, k2, desc);
|
|
24
|
+
}) : (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
o[k2] = m[k];
|
|
27
|
+
}));
|
|
28
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
+
}) : function(o, v) {
|
|
31
|
+
o["default"] = v;
|
|
32
|
+
});
|
|
33
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
+
var ownKeys = function(o) {
|
|
35
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
+
var ar = [];
|
|
37
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
+
return ar;
|
|
39
|
+
};
|
|
40
|
+
return ownKeys(o);
|
|
41
|
+
};
|
|
42
|
+
return function (mod) {
|
|
43
|
+
if (mod && mod.__esModule) return mod;
|
|
44
|
+
var result = {};
|
|
45
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
+
__setModuleDefault(result, mod);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
exports.RENDER_PROBE_EXPRESSION = exports.ENVIRONMENT_POLICIES = exports.CAPTURE_METHODS = void 0;
|
|
52
|
+
exports.distroFromOsRelease = distroFromOsRelease;
|
|
53
|
+
exports.detectCi = detectCi;
|
|
54
|
+
exports.collectHostEnvironment = collectHostEnvironment;
|
|
55
|
+
exports.browserFromUserAgent = browserFromUserAgent;
|
|
56
|
+
exports.browserFromCapabilities = browserFromCapabilities;
|
|
57
|
+
exports.installedPackageVersion = installedPackageVersion;
|
|
58
|
+
exports.parseRenderProbe = parseRenderProbe;
|
|
59
|
+
exports.buildCaptureEnvironment = buildCaptureEnvironment;
|
|
60
|
+
exports.webDriverEnvironment = webDriverEnvironment;
|
|
61
|
+
exports.readEnvironment = readEnvironment;
|
|
62
|
+
exports.applyEnvironmentPolicyToName = applyEnvironmentPolicyToName;
|
|
63
|
+
exports.parseEnvironmentVariant = parseEnvironmentVariant;
|
|
64
|
+
const fs = __importStar(require("fs"));
|
|
65
|
+
const os = __importStar(require("os"));
|
|
66
|
+
/**
|
|
67
|
+
* How the screenshot was taken, as `<extent>:<mechanism>`. The extent is what
|
|
68
|
+
* the image covers (the full page or the viewport); the mechanism is what
|
|
69
|
+
* took it. A change of method changes the image, and often its size.
|
|
70
|
+
*/
|
|
71
|
+
exports.CAPTURE_METHODS = {
|
|
72
|
+
/** CDP Page.captureScreenshot with captureBeyondViewport. */
|
|
73
|
+
CDP_FULL_PAGE: 'full-page:cdp',
|
|
74
|
+
/** Playwright page.screenshot({ fullPage: true }). */
|
|
75
|
+
PLAYWRIGHT_FULL_PAGE: 'full-page:playwright',
|
|
76
|
+
/** Viewport screenshots scrolled and stitched together. */
|
|
77
|
+
STITCHED_FULL_PAGE: 'full-page:stitched',
|
|
78
|
+
/** Firefox's own full-page screenshot (Selenium save_full_page_screenshot). */
|
|
79
|
+
FIREFOX_FULL_PAGE: 'full-page:firefox',
|
|
80
|
+
/** WebDriver takeScreenshot: the viewport only. */
|
|
81
|
+
WEBDRIVER_VIEWPORT: 'viewport:webdriver',
|
|
82
|
+
};
|
|
83
|
+
exports.ENVIRONMENT_POLICIES = ['warn', 'separate', 'strict'];
|
|
84
|
+
// ── Host ────────────────────────────────────────────────────────────────────
|
|
85
|
+
/**
|
|
86
|
+
* The distro from /etc/os-release, as `<ID> <VERSION_ID>` ("ubuntu 24.04",
|
|
87
|
+
* "debian 12"; "arch" for a rolling release). That pair decides the fonts and
|
|
88
|
+
* the FreeType build a Linux machine draws text with; the kernel does not.
|
|
89
|
+
*/
|
|
90
|
+
function distroFromOsRelease(text) {
|
|
91
|
+
const fields = {};
|
|
92
|
+
for (const line of text.split(/\r?\n/)) {
|
|
93
|
+
const m = /^([A-Z_]+)=(.*)$/.exec(line.trim());
|
|
94
|
+
if (m)
|
|
95
|
+
fields[m[1]] = m[2].replace(/^["']|["']$/g, '');
|
|
96
|
+
}
|
|
97
|
+
if (!fields.ID)
|
|
98
|
+
return undefined;
|
|
99
|
+
return fields.VERSION_ID ? `${fields.ID} ${fields.VERSION_ID}` : fields.ID;
|
|
100
|
+
}
|
|
101
|
+
/** The CI provider, and the runner image where the provider names one. */
|
|
102
|
+
function detectCi(env) {
|
|
103
|
+
if (env.GITHUB_ACTIONS === 'true') {
|
|
104
|
+
const runnerImage = env.ImageOS && env.ImageVersion ? `${env.ImageOS}/${env.ImageVersion}` : undefined;
|
|
105
|
+
return runnerImage ? { provider: 'github-actions', runnerImage } : { provider: 'github-actions' };
|
|
106
|
+
}
|
|
107
|
+
if (env.GITLAB_CI)
|
|
108
|
+
return { provider: 'gitlab' };
|
|
109
|
+
if (env.CIRCLECI)
|
|
110
|
+
return { provider: 'circleci' };
|
|
111
|
+
if (env.BUILDKITE)
|
|
112
|
+
return { provider: 'buildkite' };
|
|
113
|
+
if (env.JENKINS_URL)
|
|
114
|
+
return { provider: 'jenkins' };
|
|
115
|
+
if (env.TF_BUILD)
|
|
116
|
+
return { provider: 'azure-pipelines' };
|
|
117
|
+
if (env.CI && env.CI !== 'false' && env.CI !== '0')
|
|
118
|
+
return { provider: 'ci' };
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
const safely = (fn, fallback) => {
|
|
122
|
+
try {
|
|
123
|
+
return fn();
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return fallback;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
/** The machine-level part of the block. Never throws. */
|
|
130
|
+
function collectHostEnvironment(sources = {}) {
|
|
131
|
+
const env = sources.env ?? process.env;
|
|
132
|
+
const platform = sources.platform ?? process.platform;
|
|
133
|
+
const readFile = sources.readFile ?? ((p) => safely(() => fs.readFileSync(p, 'utf-8'), null));
|
|
134
|
+
const exists = sources.exists ?? ((p) => fs.existsSync(p));
|
|
135
|
+
const host = {
|
|
136
|
+
os: {
|
|
137
|
+
platform,
|
|
138
|
+
release: sources.release ?? safely(() => os.release(), ''),
|
|
139
|
+
arch: sources.arch ?? process.arch,
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
if (platform === 'linux') {
|
|
143
|
+
const text = safely(() => readFile('/etc/os-release'), null);
|
|
144
|
+
const distro = text ? distroFromOsRelease(text) : undefined;
|
|
145
|
+
if (distro)
|
|
146
|
+
host.os.distro = distro;
|
|
147
|
+
}
|
|
148
|
+
const ci = detectCi(env);
|
|
149
|
+
if (ci)
|
|
150
|
+
host.ci = ci;
|
|
151
|
+
if (safely(() => exists('/.dockerenv') || exists('/run/.containerenv'), false))
|
|
152
|
+
host.container = true;
|
|
153
|
+
const label = (env.TESTIVAI_ENV_LABEL ?? '').trim();
|
|
154
|
+
if (label)
|
|
155
|
+
host.label = label;
|
|
156
|
+
return host;
|
|
157
|
+
}
|
|
158
|
+
// ── Browser ─────────────────────────────────────────────────────────────────
|
|
159
|
+
/**
|
|
160
|
+
* The engine and its version from a user agent: the fallback for an adapter
|
|
161
|
+
* whose driver does not say (a Playwright persistent context has no
|
|
162
|
+
* browser() to ask). Edge is reported as chromium with channel msedge, the
|
|
163
|
+
* way Playwright names it.
|
|
164
|
+
*/
|
|
165
|
+
function browserFromUserAgent(ua) {
|
|
166
|
+
if (!ua)
|
|
167
|
+
return null;
|
|
168
|
+
const edge = /\bEdg\/([\d.]+)/.exec(ua);
|
|
169
|
+
const chrome = /\b(HeadlessChrome|Chrome|Chromium)\/([\d.]+)/.exec(ua);
|
|
170
|
+
if (chrome) {
|
|
171
|
+
const browser = { name: 'chromium', version: chrome[2] };
|
|
172
|
+
if (chrome[1] === 'HeadlessChrome')
|
|
173
|
+
browser.headless = true;
|
|
174
|
+
if (edge)
|
|
175
|
+
browser.channel = 'msedge';
|
|
176
|
+
return browser;
|
|
177
|
+
}
|
|
178
|
+
const firefox = /\bFirefox\/([\d.]+)/.exec(ua);
|
|
179
|
+
if (firefox)
|
|
180
|
+
return { name: 'firefox', version: firefox[1] };
|
|
181
|
+
// Two linear matches, not /Version\/([\d.]+).*Safari\//: that pattern
|
|
182
|
+
// backtracks quadratically on a long run of dots.
|
|
183
|
+
const safari = /\bVersion\/([\d.]+)/.exec(ua);
|
|
184
|
+
if (safari && /\bSafari\//.test(ua.slice(safari.index + safari[0].length)))
|
|
185
|
+
return { name: 'webkit', version: safari[1] };
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
/** WebDriver browserName values, normalized to the engine family and its branded channel. */
|
|
189
|
+
const WEBDRIVER_BROWSERS = {
|
|
190
|
+
chrome: { name: 'chromium', channel: 'chrome', driver: 'chromedriver' },
|
|
191
|
+
'chrome-headless-shell': { name: 'chromium', channel: 'chrome-headless-shell', driver: 'chromedriver' },
|
|
192
|
+
chromium: { name: 'chromium', driver: 'chromedriver' },
|
|
193
|
+
microsoftedge: { name: 'chromium', channel: 'msedge', driver: 'msedgedriver' },
|
|
194
|
+
msedge: { name: 'chromium', channel: 'msedge', driver: 'msedgedriver' },
|
|
195
|
+
firefox: { name: 'firefox', driver: 'geckodriver' },
|
|
196
|
+
safari: { name: 'webkit', channel: 'safari' },
|
|
197
|
+
};
|
|
198
|
+
/** The first token of a driver version string ("141.0.7390.37 (hash-refs/…)" → "141.0.7390.37"). */
|
|
199
|
+
const firstToken = (v) => (typeof v === 'string' && v.trim() ? v.trim().split(/\s+/)[0] : undefined);
|
|
200
|
+
/**
|
|
201
|
+
* The browser (and the WebDriver binary) from W3C capabilities, as
|
|
202
|
+
* WebdriverIO's `browser.capabilities` or selenium-webdriver's
|
|
203
|
+
* `getCapabilities()` returns them (a plain object, or an object with
|
|
204
|
+
* `get()`). Headless is read from the browser's own launch arguments, and
|
|
205
|
+
* only said when they are there to say it. Null without a browser name.
|
|
206
|
+
*/
|
|
207
|
+
function browserFromCapabilities(caps) {
|
|
208
|
+
if (!caps || typeof caps !== 'object')
|
|
209
|
+
return null;
|
|
210
|
+
const c = caps;
|
|
211
|
+
const get = (key) => (typeof c.get === 'function' ? c.get(key) : c[key]);
|
|
212
|
+
const rawName = get('browserName');
|
|
213
|
+
if (typeof rawName !== 'string' || rawName.length === 0)
|
|
214
|
+
return null;
|
|
215
|
+
const known = WEBDRIVER_BROWSERS[rawName.toLowerCase()];
|
|
216
|
+
const version = get('browserVersion') ?? get('version');
|
|
217
|
+
const browser = {
|
|
218
|
+
name: known?.name ?? rawName.toLowerCase(),
|
|
219
|
+
version: typeof version === 'string' && version.length > 0 ? version : null,
|
|
220
|
+
};
|
|
221
|
+
if (known?.channel)
|
|
222
|
+
browser.channel = known.channel;
|
|
223
|
+
const options = (get('goog:chromeOptions') ?? get('ms:edgeOptions') ?? get('moz:firefoxOptions'));
|
|
224
|
+
if (options && Array.isArray(options.args)) {
|
|
225
|
+
browser.headless = options.args.some((a) => typeof a === 'string' && /^-{1,2}headless(=|$)/.test(a));
|
|
226
|
+
}
|
|
227
|
+
let driverVersion;
|
|
228
|
+
if (known?.driver === 'chromedriver')
|
|
229
|
+
driverVersion = firstToken(get('chrome')?.chromedriverVersion);
|
|
230
|
+
if (known?.driver === 'msedgedriver')
|
|
231
|
+
driverVersion = firstToken(get('msedge')?.msedgedriverVersion);
|
|
232
|
+
if (known?.driver === 'geckodriver')
|
|
233
|
+
driverVersion = firstToken(get('moz:geckodriverVersion'));
|
|
234
|
+
return driverVersion && known?.driver ? { browser, driver: { name: known.driver, version: driverVersion } } : { browser };
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* The version of a package as the project at `root` resolves it (the user's
|
|
238
|
+
* own webdriverio or selenium-webdriver, not one bundled here), or null.
|
|
239
|
+
*/
|
|
240
|
+
function installedPackageVersion(name, root = process.cwd()) {
|
|
241
|
+
try {
|
|
242
|
+
const pkgPath = require.resolve(`${name}/package.json`, { paths: [root] });
|
|
243
|
+
const version = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')).version;
|
|
244
|
+
return typeof version === 'string' ? version : null;
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// ── Render probe ────────────────────────────────────────────────────────────
|
|
251
|
+
/**
|
|
252
|
+
* One self-contained in-page expression (no imports, no closures) that
|
|
253
|
+
* measures what the page is actually rendered with. Measured, not taken from
|
|
254
|
+
* configuration: a config can say 1280×720 at scale 1 while the browser
|
|
255
|
+
* renders something else. Each field is read on its own, so one missing API
|
|
256
|
+
* never loses the rest.
|
|
257
|
+
*/
|
|
258
|
+
exports.RENDER_PROBE_EXPRESSION = `(function () {
|
|
259
|
+
var out = {};
|
|
260
|
+
try { out.viewport = { width: window.innerWidth, height: window.innerHeight }; } catch (e) {}
|
|
261
|
+
try { out.deviceScaleFactor = window.devicePixelRatio; } catch (e) {}
|
|
262
|
+
try { out.colorScheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } catch (e) {}
|
|
263
|
+
try { out.reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'reduce' : 'no-preference'; } catch (e) {}
|
|
264
|
+
try { out.locale = navigator.language; } catch (e) {}
|
|
265
|
+
try { out.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; } catch (e) {}
|
|
266
|
+
try { out.userAgent = navigator.userAgent; } catch (e) {}
|
|
267
|
+
return out;
|
|
268
|
+
})()`;
|
|
269
|
+
const isNumber = (v) => typeof v === 'number' && Number.isFinite(v);
|
|
270
|
+
const isString = (v) => typeof v === 'string' && v.length > 0;
|
|
271
|
+
/** Validate the probe's result. Mistyped fields are dropped, never trusted. */
|
|
272
|
+
function parseRenderProbe(raw) {
|
|
273
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
274
|
+
return null;
|
|
275
|
+
const r = raw;
|
|
276
|
+
const render = {};
|
|
277
|
+
const vp = r.viewport;
|
|
278
|
+
if (vp && isNumber(vp.width) && isNumber(vp.height))
|
|
279
|
+
render.viewport = { width: vp.width, height: vp.height };
|
|
280
|
+
if (isNumber(r.deviceScaleFactor))
|
|
281
|
+
render.deviceScaleFactor = r.deviceScaleFactor;
|
|
282
|
+
if (r.colorScheme === 'light' || r.colorScheme === 'dark')
|
|
283
|
+
render.colorScheme = r.colorScheme;
|
|
284
|
+
if (r.reducedMotion === 'reduce' || r.reducedMotion === 'no-preference')
|
|
285
|
+
render.reducedMotion = r.reducedMotion;
|
|
286
|
+
if (isString(r.locale))
|
|
287
|
+
render.locale = r.locale;
|
|
288
|
+
if (isString(r.timezone))
|
|
289
|
+
render.timezone = r.timezone;
|
|
290
|
+
return isString(r.userAgent) ? { render, userAgent: r.userAgent } : { render };
|
|
291
|
+
}
|
|
292
|
+
// ── Assembly ────────────────────────────────────────────────────────────────
|
|
293
|
+
/** Drop undefined members, so the block round-trips through JSON unchanged. */
|
|
294
|
+
function compact(value) {
|
|
295
|
+
const out = {};
|
|
296
|
+
for (const [k, v] of Object.entries(value))
|
|
297
|
+
if (v !== undefined)
|
|
298
|
+
out[k] = v;
|
|
299
|
+
return out;
|
|
300
|
+
}
|
|
301
|
+
/** Assemble the block from what one adapter observed. */
|
|
302
|
+
function buildCaptureEnvironment(parts) {
|
|
303
|
+
const { host } = parts;
|
|
304
|
+
// Built in the documented order (docs/extension-api.md), so metadata.json reads the same everywhere.
|
|
305
|
+
const env = {
|
|
306
|
+
os: compact({ ...host.os }),
|
|
307
|
+
browser: compact({ ...parts.browser }),
|
|
308
|
+
};
|
|
309
|
+
if (parts.framework)
|
|
310
|
+
env.framework = compact({ ...parts.framework });
|
|
311
|
+
env.adapter = { ...parts.adapter };
|
|
312
|
+
if (parts.driver)
|
|
313
|
+
env.driver = { ...parts.driver };
|
|
314
|
+
if (parts.render) {
|
|
315
|
+
const render = compact({ ...parts.render });
|
|
316
|
+
if (Object.keys(render).length > 0)
|
|
317
|
+
env.render = render;
|
|
318
|
+
}
|
|
319
|
+
if (host.ci)
|
|
320
|
+
env.ci = compact({ ...host.ci });
|
|
321
|
+
if (host.container)
|
|
322
|
+
env.container = true;
|
|
323
|
+
if (host.label)
|
|
324
|
+
env.label = host.label;
|
|
325
|
+
if (parts.project)
|
|
326
|
+
env.project = parts.project;
|
|
327
|
+
return env;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* The block for a WebDriver adapter: the browser and driver from the
|
|
331
|
+
* capabilities (the user agent when they say nothing), the render settings
|
|
332
|
+
* from the probe. Null when neither names the browser: never guessed.
|
|
333
|
+
*/
|
|
334
|
+
function webDriverEnvironment(input) {
|
|
335
|
+
const probed = parseRenderProbe(input.probe);
|
|
336
|
+
const fromCaps = browserFromCapabilities(input.capabilities);
|
|
337
|
+
const browser = fromCaps?.browser ?? (probed?.userAgent ? browserFromUserAgent(probed.userAgent) : null);
|
|
338
|
+
if (!browser)
|
|
339
|
+
return null;
|
|
340
|
+
return buildCaptureEnvironment({
|
|
341
|
+
host: input.host ?? collectHostEnvironment(),
|
|
342
|
+
browser,
|
|
343
|
+
framework: { ...input.framework },
|
|
344
|
+
adapter: input.adapter,
|
|
345
|
+
driver: fromCaps?.driver,
|
|
346
|
+
render: probed?.render,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* The block as recorded in a metadata.json, or null when there is none:
|
|
351
|
+
* captures from before provenance, or from an adapter that does not record
|
|
352
|
+
* it. Only the three parts every recorder writes are required.
|
|
353
|
+
*/
|
|
354
|
+
function readEnvironment(raw) {
|
|
355
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
356
|
+
return null;
|
|
357
|
+
const e = raw;
|
|
358
|
+
const obj = (v) => Boolean(v) && typeof v === 'object' && !Array.isArray(v);
|
|
359
|
+
if (!obj(e.os) || typeof e.os.platform !== 'string')
|
|
360
|
+
return null;
|
|
361
|
+
if (!obj(e.browser) || typeof e.browser.name !== 'string')
|
|
362
|
+
return null;
|
|
363
|
+
if (!obj(e.adapter) || typeof e.adapter.captureMethod !== 'string')
|
|
364
|
+
return null;
|
|
365
|
+
return e;
|
|
366
|
+
}
|
|
367
|
+
// ── separate policy ─────────────────────────────────────────────────────────
|
|
368
|
+
const safeSegment = (s) => s.replace(/[^a-z0-9_-]+/gi, '_').toLowerCase();
|
|
369
|
+
/** Node's process.platform values: a suffix ending in one of them is an environment variant. */
|
|
370
|
+
const PLATFORMS = ['aix', 'android', 'darwin', 'freebsd', 'haiku', 'linux', 'openbsd', 'sunos', 'win32', 'cygwin', 'netbsd'];
|
|
371
|
+
/**
|
|
372
|
+
* environmentPolicy "separate" keys each snapshot by environment:
|
|
373
|
+
* `<name>__<browser>__<platform>`, the same in-the-name mechanism as the
|
|
374
|
+
* Playwright multi-project variant, so the on-disk layout, results.json, the
|
|
375
|
+
* report, approvals and the approve action all work unchanged. A browser the
|
|
376
|
+
* name already ends with (a project named after it) is not repeated. Never
|
|
377
|
+
* keyed by version: a browser upgrade should be seen, not hidden behind a
|
|
378
|
+
* fresh baseline. Other policies return the name unchanged.
|
|
379
|
+
*/
|
|
380
|
+
function applyEnvironmentPolicyToName(name, env, policy) {
|
|
381
|
+
if (policy !== 'separate')
|
|
382
|
+
return name;
|
|
383
|
+
const browser = safeSegment(env.browser.name);
|
|
384
|
+
const platform = safeSegment(env.os.platform);
|
|
385
|
+
const segments = name.split('__');
|
|
386
|
+
const withBrowser = segments.length > 1 && segments[segments.length - 1] === browser ? name : `${name}__${browser}`;
|
|
387
|
+
return `${withBrowser}__${platform}`;
|
|
388
|
+
}
|
|
389
|
+
/** The variant a separate-policy name carries, or null for a name without one. */
|
|
390
|
+
function parseEnvironmentVariant(name) {
|
|
391
|
+
const segments = name.split('__');
|
|
392
|
+
if (segments.length < 3)
|
|
393
|
+
return null;
|
|
394
|
+
const platform = segments[segments.length - 1];
|
|
395
|
+
if (!PLATFORMS.includes(platform))
|
|
396
|
+
return null;
|
|
397
|
+
return { base: segments.slice(0, -2).join('__'), browser: segments[segments.length - 2], platform };
|
|
398
|
+
}
|
|
399
|
+
//# sourceMappingURL=environment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment.js","sourceRoot":"","sources":["../../src/capture/environment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { type EnvironmentPolicy, type HostEnvironment } from '../capture/environment';
|
|
3
|
+
/**
|
|
4
|
+
* `testivai env` — this machine's part of the capture environment: the OS
|
|
5
|
+
* and distro, CI and its runner image, a container, TESTIVAI_ENV_LABEL,
|
|
6
|
+
* and the environmentPolicy that decides what a change of environment does.
|
|
7
|
+
* The rest of the block (browser, framework, adapter, render settings) is
|
|
8
|
+
* recorded by each capture, since it depends on the browser a test opens.
|
|
9
|
+
*
|
|
10
|
+
* `--fonts` adds a fingerprint of the installed font list (fc-list): two
|
|
11
|
+
* machines whose fingerprints differ draw text differently, the most common
|
|
12
|
+
* reason the same page renders differently "at the same URL". Not recorded
|
|
13
|
+
* per capture: listing fonts costs a process spawn.
|
|
14
|
+
*/
|
|
15
|
+
/** Count and a short hash of a font list (fc-list output), independent of its order and duplicates. */
|
|
16
|
+
export declare function fontFingerprint(fcListOutput: string): {
|
|
17
|
+
count: number;
|
|
18
|
+
sha256: string;
|
|
19
|
+
};
|
|
20
|
+
/** The installed fonts' fingerprint, or 'unavailable' when fc-list cannot run here. */
|
|
21
|
+
export declare function readFontFingerprint(): {
|
|
22
|
+
count: number;
|
|
23
|
+
sha256: string;
|
|
24
|
+
} | 'unavailable';
|
|
25
|
+
export interface EnvironmentReport {
|
|
26
|
+
host: HostEnvironment;
|
|
27
|
+
policy: EnvironmentPolicy;
|
|
28
|
+
/** null: not asked for; 'unavailable': no fc-list. */
|
|
29
|
+
fonts: {
|
|
30
|
+
count: number;
|
|
31
|
+
sha256: string;
|
|
32
|
+
} | 'unavailable' | null;
|
|
33
|
+
}
|
|
34
|
+
/** The human-readable `testivai env` output. */
|
|
35
|
+
export declare function formatEnvironment(report: EnvironmentReport): string;
|
|
36
|
+
export declare const envCommand: Command;
|
|
37
|
+
//# sourceMappingURL=env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/commands/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAA0B,KAAK,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9G;;;;;;;;;;;GAWG;AAEH,uGAAuG;AACvG,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAGvF;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,CAOvF;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,sDAAsD;IACtD,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,aAAa,GAAG,IAAI,CAAC;CACjE;AAED,gDAAgD;AAChD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAoBnE;AAED,eAAO,MAAM,UAAU,SAiBnB,CAAC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.envCommand = void 0;
|
|
37
|
+
exports.fontFingerprint = fontFingerprint;
|
|
38
|
+
exports.readFontFingerprint = readFontFingerprint;
|
|
39
|
+
exports.formatEnvironment = formatEnvironment;
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const child_process_1 = require("child_process");
|
|
42
|
+
const crypto = __importStar(require("crypto"));
|
|
43
|
+
const environment_1 = require("../capture/environment");
|
|
44
|
+
const local_config_1 = require("../config/local-config");
|
|
45
|
+
/**
|
|
46
|
+
* `testivai env` — this machine's part of the capture environment: the OS
|
|
47
|
+
* and distro, CI and its runner image, a container, TESTIVAI_ENV_LABEL,
|
|
48
|
+
* and the environmentPolicy that decides what a change of environment does.
|
|
49
|
+
* The rest of the block (browser, framework, adapter, render settings) is
|
|
50
|
+
* recorded by each capture, since it depends on the browser a test opens.
|
|
51
|
+
*
|
|
52
|
+
* `--fonts` adds a fingerprint of the installed font list (fc-list): two
|
|
53
|
+
* machines whose fingerprints differ draw text differently, the most common
|
|
54
|
+
* reason the same page renders differently "at the same URL". Not recorded
|
|
55
|
+
* per capture: listing fonts costs a process spawn.
|
|
56
|
+
*/
|
|
57
|
+
/** Count and a short hash of a font list (fc-list output), independent of its order and duplicates. */
|
|
58
|
+
function fontFingerprint(fcListOutput) {
|
|
59
|
+
const fonts = [...new Set(fcListOutput.split(/\r?\n/).map((l) => l.trim()).filter(Boolean))].sort();
|
|
60
|
+
return { count: fonts.length, sha256: crypto.createHash('sha256').update(fonts.join('\n')).digest('hex').slice(0, 12) };
|
|
61
|
+
}
|
|
62
|
+
/** The installed fonts' fingerprint, or 'unavailable' when fc-list cannot run here. */
|
|
63
|
+
function readFontFingerprint() {
|
|
64
|
+
try {
|
|
65
|
+
const out = (0, child_process_1.execFileSync)('fc-list', [':', 'family', 'style'], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 10000 });
|
|
66
|
+
return fontFingerprint(out);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return 'unavailable';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** The human-readable `testivai env` output. */
|
|
73
|
+
function formatEnvironment(report) {
|
|
74
|
+
const { host, policy, fonts } = report;
|
|
75
|
+
const os = `${host.os.platform} ${host.os.release} ${host.os.arch}${host.os.distro ? ` (${host.os.distro})` : ''}`;
|
|
76
|
+
const ci = host.ci ? `${host.ci.provider}${host.ci.runnerImage ? ` (runner image ${host.ci.runnerImage})` : ''}` : 'none';
|
|
77
|
+
const fontLine = fonts === null
|
|
78
|
+
? 'not checked (pass --fonts)'
|
|
79
|
+
: fonts === 'unavailable'
|
|
80
|
+
? 'fc-list is not available on this machine'
|
|
81
|
+
: `${fonts.count} installed, fingerprint ${fonts.sha256}`;
|
|
82
|
+
return [
|
|
83
|
+
'TestivAI capture environment (this machine)',
|
|
84
|
+
` os: ${os}`,
|
|
85
|
+
` ci: ${ci}`,
|
|
86
|
+
` container: ${host.container ? 'yes' : 'no'}`,
|
|
87
|
+
` label: ${host.label ?? 'none (set TESTIVAI_ENV_LABEL to name this environment)'}`,
|
|
88
|
+
` fonts: ${fontLine}`,
|
|
89
|
+
` policy: environmentPolicy "${policy}"`,
|
|
90
|
+
'Browser, framework, adapter and render settings are recorded by each capture (metadata.json `environment`).',
|
|
91
|
+
].join('\n');
|
|
92
|
+
}
|
|
93
|
+
exports.envCommand = new commander_1.Command('env')
|
|
94
|
+
.description("Print this machine's capture environment (OS, distro, CI, container, label) and the environmentPolicy")
|
|
95
|
+
.option('--fonts', 'Also fingerprint the installed fonts (fc-list): differing fingerprints mean text renders differently')
|
|
96
|
+
.option('--json', 'Print machine-readable JSON')
|
|
97
|
+
.action((options) => {
|
|
98
|
+
const report = {
|
|
99
|
+
host: (0, environment_1.collectHostEnvironment)(),
|
|
100
|
+
policy: (0, local_config_1.loadLocalConfig)(process.cwd()).environmentPolicy ?? 'warn',
|
|
101
|
+
fonts: options.fonts ? readFontFingerprint() : null,
|
|
102
|
+
};
|
|
103
|
+
if (options.json) {
|
|
104
|
+
const payload = { environment: report.host, environmentPolicy: report.policy };
|
|
105
|
+
if (report.fonts !== null)
|
|
106
|
+
payload.fonts = report.fonts;
|
|
107
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
process.stdout.write(`${formatEnvironment(report)}\n`);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/commands/env.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBA,0CAGC;AAGD,kDAOC;AAUD,8CAoBC;AA/DD,yCAAoC;AACpC,iDAA6C;AAC7C,+CAAiC;AACjC,wDAA8G;AAC9G,yDAAyD;AAEzD;;;;;;;;;;;GAWG;AAEH,uGAAuG;AACvG,SAAgB,eAAe,CAAC,YAAoB;IAClD,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpG,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AAC1H,CAAC;AAED,uFAAuF;AACvF,SAAgB,mBAAmB;IACjC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAA,4BAAY,EAAC,SAAS,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,KAAM,EAAE,CAAC,CAAC;QAC3I,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,aAAa,CAAC;IACvB,CAAC;AACH,CAAC;AASD,gDAAgD;AAChD,SAAgB,iBAAiB,CAAC,MAAyB;IACzD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IACvC,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,QAAQ,IAAI,IAAI,CAAC,EAAE,CAAC,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACnH,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,kBAAkB,IAAI,CAAC,EAAE,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1H,MAAM,QAAQ,GACZ,KAAK,KAAK,IAAI;QACZ,CAAC,CAAC,4BAA4B;QAC9B,CAAC,CAAC,KAAK,KAAK,aAAa;YACvB,CAAC,CAAC,0CAA0C;YAC5C,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,2BAA2B,KAAK,CAAC,MAAM,EAAE,CAAC;IAChE,OAAO;QACL,6CAA6C;QAC7C,gBAAgB,EAAE,EAAE;QACpB,gBAAgB,EAAE,EAAE;QACpB,gBAAgB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;QAC/C,gBAAgB,IAAI,CAAC,KAAK,IAAI,wDAAwD,EAAE;QACxF,gBAAgB,QAAQ,EAAE;QAC1B,mCAAmC,MAAM,GAAG;QAC5C,6GAA6G;KAC9G,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAEY,QAAA,UAAU,GAAG,IAAI,mBAAO,CAAC,KAAK,CAAC;KACzC,WAAW,CAAC,uGAAuG,CAAC;KACpH,MAAM,CAAC,SAAS,EAAE,sGAAsG,CAAC;KACzH,MAAM,CAAC,QAAQ,EAAE,6BAA6B,CAAC;KAC/C,MAAM,CAAC,CAAC,OAA4C,EAAE,EAAE;IACvD,MAAM,MAAM,GAAsB;QAChC,IAAI,EAAE,IAAA,oCAAsB,GAAE;QAC9B,MAAM,EAAE,IAAA,8BAAe,EAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,iBAAiB,IAAI,MAAM;QAClE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC,IAAI;KACpD,CAAC;IACF,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,OAAO,GAA4B,EAAE,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;QACxG,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI;YAAE,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QACxD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAChE,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACzD,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Reads/writes `.testivai/config.json`. Everything runs locally, so this
|
|
5
5
|
* file is optional — absent, every field falls back to its default.
|
|
6
6
|
*/
|
|
7
|
+
import type { EnvironmentPolicy } from '../capture/environment';
|
|
7
8
|
export interface LocalConfig {
|
|
8
9
|
/** Pixel diff threshold (0-1). Default: 0.1 */
|
|
9
10
|
threshold: number;
|
|
@@ -135,6 +136,19 @@ export interface LocalConfig {
|
|
|
135
136
|
* server's --approval-prompt / --no-approval-prompt flag overrides this.
|
|
136
137
|
*/
|
|
137
138
|
mcpApprovalPrompt?: boolean;
|
|
139
|
+
/**
|
|
140
|
+
* What a change of capture environment (browser, OS, device scale,
|
|
141
|
+
* capture method…: the recorded `environment`) since the baseline does:
|
|
142
|
+
* - "warn" (default): never changes a status; the report says when a
|
|
143
|
+
* diff is rendering drift rather than calling it render noise.
|
|
144
|
+
* - "separate": adapters key each snapshot by environment,
|
|
145
|
+
* `<name>__<browser>__<platform>`, so each browser and OS gates
|
|
146
|
+
* against its own baselines (never keyed by version). Only baselines
|
|
147
|
+
* of an environment this run captured can be missing.
|
|
148
|
+
* - "strict": a snapshot whose rendering environment changed is
|
|
149
|
+
* changed, whatever its pixels say (exit 1 with --fail-on-diff).
|
|
150
|
+
*/
|
|
151
|
+
environmentPolicy?: EnvironmentPolicy;
|
|
138
152
|
}
|
|
139
153
|
/**
|
|
140
154
|
* Load local config from `.testivai/config.json`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-config.d.ts","sourceRoot":"","sources":["../../src/config/local-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"local-config.d.ts","sourceRoot":"","sources":["../../src/config/local-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,QAAQ,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,UAAU,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B;;;;;;;;;OASG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IACvD;;;;OAIG;IACH,WAAW,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;;;;;;OAcG;IACH,aAAa,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC;AAsBD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAqChE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC/B,WAAW,CAab;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,WAAW,CAE9C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-config.js","sourceRoot":"","sources":["../../src/config/local-config.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"local-config.js","sourceRoot":"","sources":["../../src/config/local-config.ts"],"names":[],"mappings":"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"}
|