@argos-ci/vitest 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/README.md +119 -0
- package/dist/index.d.mts +175 -0
- package/dist/index.mjs +1076 -0
- package/dist/internal.d.mts +77 -0
- package/dist/internal.mjs +145 -0
- package/dist/plugin.d.mts +203 -0
- package/dist/plugin.mjs +361 -0
- package/dist/snapshot-file-jtgfnw7g.mjs +74 -0
- package/package.json +85 -0
package/dist/plugin.mjs
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { DO_NOT_USE_setMetadataConfig, argosScreenshot } from "@argos-ci/playwright";
|
|
4
|
+
import { resolveViewport } from "@argos-ci/browser";
|
|
5
|
+
import { createDirectory, getMetadataPath, getScreenshotName, readVersionFromPackage, writeMetadata } from "@argos-ci/util";
|
|
6
|
+
import { writeFile } from "node:fs/promises";
|
|
7
|
+
import { getSnapshotMimeType, upload } from "@argos-ci/core";
|
|
8
|
+
//#region src/iframe.ts
|
|
9
|
+
/**
|
|
10
|
+
* Selector of the iframe Vitest renders the test into on the orchestrator page.
|
|
11
|
+
*/
|
|
12
|
+
const VITEST_IFRAME_SELECTOR = "iframe[data-vitest=\"true\"]";
|
|
13
|
+
/**
|
|
14
|
+
* ID of the Vitest "tester" element that wraps the iframe with a `scale(...)`
|
|
15
|
+
* transform.
|
|
16
|
+
*/
|
|
17
|
+
const VITEST_TESTER_ID = "vitest-tester";
|
|
18
|
+
/**
|
|
19
|
+
* Remove the scale from the Vitest `#vitest-tester` element before taking a
|
|
20
|
+
* screenshot to avoid ending up with small screenshots.
|
|
21
|
+
* @returns A function to restore the scale after the screenshot.
|
|
22
|
+
*/
|
|
23
|
+
async function resetTesterScale(ctx) {
|
|
24
|
+
await ctx.page.evaluate((testerId) => {
|
|
25
|
+
const tester = document.getElementById(testerId);
|
|
26
|
+
if (!(tester instanceof HTMLElement)) return;
|
|
27
|
+
if (!tester.getAttribute("data-scale")) throw new Error("Vitest iframe data-scale attribute not found");
|
|
28
|
+
tester.dataset.bckTransform = tester.style.transform;
|
|
29
|
+
tester.style.transform = `scale(1)`;
|
|
30
|
+
}, VITEST_TESTER_ID);
|
|
31
|
+
return async () => {
|
|
32
|
+
await ctx.page.evaluate((testerId) => {
|
|
33
|
+
const tester = document.getElementById(testerId);
|
|
34
|
+
if (!(tester instanceof HTMLElement)) return;
|
|
35
|
+
tester.style.transform = tester.dataset.bckTransform ?? "";
|
|
36
|
+
}, VITEST_TESTER_ID);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resize the Vitest iframe.
|
|
41
|
+
*
|
|
42
|
+
* The story/test renders inside an `<iframe data-vitest="true">` on the host
|
|
43
|
+
* page and we screenshot the iframe's `<body>`. Anything overflowing the iframe
|
|
44
|
+
* box is not painted, so the iframe must be sized to hold the content.
|
|
45
|
+
*
|
|
46
|
+
* @param size - The viewport size, `"default"` to keep the natural size, or
|
|
47
|
+
* `"initial"` to restore the size backed up on the first resize.
|
|
48
|
+
* @param options.fullPage - When `true`, grow the height to fit the content
|
|
49
|
+
* while keeping the viewport width (Playwright-style full page).
|
|
50
|
+
*/
|
|
51
|
+
async function setIframeViewportSize(ctx, size, options = {}) {
|
|
52
|
+
await ctx.page.evaluate(({ size, fullPage, selector }) => {
|
|
53
|
+
const iframe = document.querySelector(selector);
|
|
54
|
+
if (!(iframe instanceof HTMLIFrameElement)) throw new Error("Vitest iframe not found");
|
|
55
|
+
if (!iframe.contentDocument) throw new Error("Vitest iframe contentDocument not found");
|
|
56
|
+
if (size === "initial") {
|
|
57
|
+
if (iframe.dataset.initialWidth && iframe.dataset.initialHeight) {
|
|
58
|
+
iframe.style.width = iframe.dataset.initialWidth;
|
|
59
|
+
iframe.style.height = iframe.dataset.initialHeight;
|
|
60
|
+
}
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (!iframe.dataset.initialWidth && !iframe.dataset.initialHeight) {
|
|
64
|
+
iframe.dataset.initialWidth = iframe.style.width;
|
|
65
|
+
iframe.dataset.initialHeight = iframe.style.height;
|
|
66
|
+
}
|
|
67
|
+
if (size !== "default") iframe.style.width = `${size.width}px`;
|
|
68
|
+
if (fullPage) {
|
|
69
|
+
if (!iframe.contentWindow) throw new Error(`Can't access iframe window`);
|
|
70
|
+
const viewportHeight = size === "default" ? iframe.contentWindow.innerHeight : size.height;
|
|
71
|
+
iframe.style.height = "auto";
|
|
72
|
+
iframe.style.height = viewportHeight < iframe.contentDocument.body.offsetHeight ? `${iframe.contentDocument.body.offsetHeight}px` : "100%";
|
|
73
|
+
} else if (size !== "default") {
|
|
74
|
+
iframe.style.height = "auto";
|
|
75
|
+
iframe.style.height = `${size.height}px`;
|
|
76
|
+
}
|
|
77
|
+
}, {
|
|
78
|
+
size,
|
|
79
|
+
fullPage: options.fullPage ?? false,
|
|
80
|
+
selector: VITEST_IFRAME_SELECTOR
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Grow the Vitest iframe to fit its content so nothing is clipped.
|
|
85
|
+
*
|
|
86
|
+
* This must run *after* `argosCSS` (which may inject a `zoom`) is applied,
|
|
87
|
+
* because `setIframeViewportSize` sizes the iframe *before* the content's final
|
|
88
|
+
* size is known. It only ever grows the iframe, never shrinks it.
|
|
89
|
+
*
|
|
90
|
+
* @param options.fitWidth - Also grow the iframe horizontally to paint content
|
|
91
|
+
* wider than the viewport. When `false`, only the height grows (to match
|
|
92
|
+
* Playwright's `fullPage` semantics: full height, viewport width).
|
|
93
|
+
*/
|
|
94
|
+
async function fitIframeToContent(ctx, options) {
|
|
95
|
+
await ctx.page.evaluate(({ fitWidth, selector }) => {
|
|
96
|
+
const iframe = document.querySelector(selector);
|
|
97
|
+
if (!(iframe instanceof HTMLIFrameElement) || !iframe.contentDocument) return;
|
|
98
|
+
const { body, documentElement } = iframe.contentDocument;
|
|
99
|
+
const contentHeight = Math.max(body.scrollHeight, body.offsetHeight, documentElement.scrollHeight);
|
|
100
|
+
if (contentHeight > iframe.clientHeight) iframe.style.height = `${contentHeight}px`;
|
|
101
|
+
if (fitWidth) {
|
|
102
|
+
const contentWidth = Math.max(body.scrollWidth, body.offsetWidth, documentElement.scrollWidth);
|
|
103
|
+
if (contentWidth > iframe.clientWidth) iframe.style.width = `${contentWidth}px`;
|
|
104
|
+
}
|
|
105
|
+
}, {
|
|
106
|
+
fitWidth: options.fitWidth,
|
|
107
|
+
selector: VITEST_IFRAME_SELECTOR
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
//#region src/screenshot.ts
|
|
112
|
+
/**
|
|
113
|
+
* Take a screenshot of the Vitest iframe body using the Playwright SDK.
|
|
114
|
+
*
|
|
115
|
+
* This is the shared primitive both the standalone command and Storybook build
|
|
116
|
+
* on. It:
|
|
117
|
+
* - strips the Vitest-specific `viewports`/`fullPage` options (they drive the
|
|
118
|
+
* iframe resize, not Playwright);
|
|
119
|
+
* - wraps `beforeScreenshot` so the content is grown to fit *after* `argosCSS`
|
|
120
|
+
* (and any user `beforeScreenshot`) has been applied — otherwise wide/tall
|
|
121
|
+
* content would be clipped;
|
|
122
|
+
* - captures the iframe's `<body>` via `@argos-ci/playwright`.
|
|
123
|
+
*
|
|
124
|
+
* @param config.fitWidth - Grow the iframe horizontally as well as vertically
|
|
125
|
+
* to fit the content (used when not capturing a fixed viewport width).
|
|
126
|
+
*/
|
|
127
|
+
async function screenshotFrame(ctx, name, options, config) {
|
|
128
|
+
const { viewports: _viewports, fullPage: _fullPage, ...rest } = options;
|
|
129
|
+
const userBeforeScreenshot = rest.beforeScreenshot;
|
|
130
|
+
const playwrightOptions = {
|
|
131
|
+
...rest,
|
|
132
|
+
beforeScreenshot: async (api) => {
|
|
133
|
+
await userBeforeScreenshot?.(api);
|
|
134
|
+
await fitIframeToContent(ctx, { fitWidth: config.fitWidth });
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
return argosScreenshot(await ctx.frame(), name, playwrightOptions);
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/version.ts
|
|
141
|
+
const require = createRequire(import.meta.url);
|
|
142
|
+
/**
|
|
143
|
+
* Get the version of the Argos Vitest SDK.
|
|
144
|
+
*/
|
|
145
|
+
async function getArgosVitestVersion() {
|
|
146
|
+
return readVersionFromPackage(require.resolve("@argos-ci/vitest/package.json"));
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get the version of Vitest itself (used as the automation library for
|
|
150
|
+
* `argosSnapshot`, which does not rely on a browser).
|
|
151
|
+
*/
|
|
152
|
+
async function getVitestVersion() {
|
|
153
|
+
return readVersionFromPackage(require.resolve("vitest/package.json"));
|
|
154
|
+
}
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/command.ts
|
|
157
|
+
/**
|
|
158
|
+
* Create the `argosScreenshot` browser command used to capture Argos
|
|
159
|
+
* screenshots from Vitest browser tests.
|
|
160
|
+
*
|
|
161
|
+
* Non-serializable options (`beforeScreenshot`, `afterScreenshot`, a
|
|
162
|
+
* `Locator`/`ElementHandle` `element`, …) come from `pluginOptions` (node side)
|
|
163
|
+
* and are merged with the serializable per-call options (per-call wins).
|
|
164
|
+
*/
|
|
165
|
+
const createArgosScreenshotCommand = (pluginOptions = {}) => {
|
|
166
|
+
return async (ctx, name, options) => {
|
|
167
|
+
if (!name) throw new Error("The `name` argument is required.");
|
|
168
|
+
const merged = {
|
|
169
|
+
...pluginOptions,
|
|
170
|
+
...options
|
|
171
|
+
};
|
|
172
|
+
const fullPage = merged.fullPage ?? false;
|
|
173
|
+
const fitWidth = !fullPage;
|
|
174
|
+
const restore = await resetTesterScale(ctx);
|
|
175
|
+
try {
|
|
176
|
+
const version = await getArgosVitestVersion();
|
|
177
|
+
const setMetadata = (viewport) => {
|
|
178
|
+
DO_NOT_USE_setMetadataConfig({
|
|
179
|
+
sdk: {
|
|
180
|
+
name: "@argos-ci/vitest",
|
|
181
|
+
version
|
|
182
|
+
},
|
|
183
|
+
playwrightLibraries: ["@vitest/browser-playwright"],
|
|
184
|
+
viewport
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
const attachments = [];
|
|
188
|
+
if (merged.viewports && merged.viewports.length > 0) for (const viewport of merged.viewports) {
|
|
189
|
+
const size = resolveViewport(viewport);
|
|
190
|
+
await setIframeViewportSize(ctx, size, { fullPage });
|
|
191
|
+
setMetadata(size);
|
|
192
|
+
const shot = await screenshotFrame(ctx, getScreenshotName(name, { viewportWidth: size.width }), merged, { fitWidth: false });
|
|
193
|
+
attachments.push(...shot);
|
|
194
|
+
await setIframeViewportSize(ctx, "initial", { fullPage });
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
await setIframeViewportSize(ctx, "default", { fullPage });
|
|
198
|
+
setMetadata();
|
|
199
|
+
const shot = await screenshotFrame(ctx, name, merged, { fitWidth });
|
|
200
|
+
attachments.push(...shot);
|
|
201
|
+
}
|
|
202
|
+
return attachments;
|
|
203
|
+
} finally {
|
|
204
|
+
await restore();
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Default extension of a serialized snapshot file.
|
|
210
|
+
*/
|
|
211
|
+
const DEFAULT_EXTENSION = ".txt";
|
|
212
|
+
/**
|
|
213
|
+
* Marker inserted before the extension so the reporter can reliably pick up
|
|
214
|
+
* snapshots (`**\/*.snapshot.*`) without matching screenshots or ARIA files.
|
|
215
|
+
*/
|
|
216
|
+
const SNAPSHOT_INFIX = ".snapshot";
|
|
217
|
+
/**
|
|
218
|
+
* Normalize a user-provided extension to a leading-dot form (`.txt`).
|
|
219
|
+
*/
|
|
220
|
+
function normalizeExtension(extension) {
|
|
221
|
+
return extension.startsWith(".") ? extension : `.${extension}`;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Write a serialized snapshot (and its metadata) to disk and return the
|
|
225
|
+
* corresponding Argos attachments.
|
|
226
|
+
*
|
|
227
|
+
* This is the shared Node-side primitive used by both the browser command (which
|
|
228
|
+
* receives the already-serialized string over RPC) and the Node code path.
|
|
229
|
+
*/
|
|
230
|
+
async function writeSnapshotFile(name, content, options = {}) {
|
|
231
|
+
if (!name) throw new Error("The `name` argument is required.");
|
|
232
|
+
const root = options.root ?? "./screenshots";
|
|
233
|
+
const extension = normalizeExtension(options.extension ?? DEFAULT_EXTENSION);
|
|
234
|
+
const snapshotPath = resolve(root, `${getScreenshotName(name)}${SNAPSHOT_INFIX}${extension}`);
|
|
235
|
+
const [vitestVersion, sdkVersion] = await Promise.all([getVitestVersion(), getArgosVitestVersion()]);
|
|
236
|
+
const tags = options.tag ? Array.isArray(options.tag) ? options.tag : [options.tag] : void 0;
|
|
237
|
+
const metadata = {
|
|
238
|
+
automationLibrary: {
|
|
239
|
+
name: "vitest",
|
|
240
|
+
version: vitestVersion
|
|
241
|
+
},
|
|
242
|
+
sdk: {
|
|
243
|
+
name: "@argos-ci/vitest",
|
|
244
|
+
version: sdkVersion
|
|
245
|
+
},
|
|
246
|
+
...tags ? { tags } : {}
|
|
247
|
+
};
|
|
248
|
+
await createDirectory(dirname(snapshotPath));
|
|
249
|
+
await Promise.all([writeFile(snapshotPath, content, "utf-8"), writeMetadata(snapshotPath, metadata)]);
|
|
250
|
+
return [{
|
|
251
|
+
name: `argos/snapshot___${name}`,
|
|
252
|
+
contentType: getSnapshotMimeType(snapshotPath),
|
|
253
|
+
path: snapshotPath
|
|
254
|
+
}, {
|
|
255
|
+
name: `argos/snapshot/metadata___${name}`,
|
|
256
|
+
contentType: "application/json",
|
|
257
|
+
path: getMetadataPath(snapshotPath)
|
|
258
|
+
}];
|
|
259
|
+
}
|
|
260
|
+
//#endregion
|
|
261
|
+
//#region src/snapshot-command.ts
|
|
262
|
+
/**
|
|
263
|
+
* Create the `argosSnapshot` browser command used to write serialized snapshots
|
|
264
|
+
* from Vitest browser tests. The serialized string is produced on the browser
|
|
265
|
+
* side and this command writes it (and its metadata) to disk on the Node side.
|
|
266
|
+
*/
|
|
267
|
+
const createArgosSnapshotCommand = (pluginOptions = {}) => {
|
|
268
|
+
return async (_ctx, name, content, options) => {
|
|
269
|
+
if (!name) throw new Error("The `name` argument is required.");
|
|
270
|
+
return writeSnapshotFile(name, content, {
|
|
271
|
+
root: pluginOptions.root,
|
|
272
|
+
...options
|
|
273
|
+
});
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
//#endregion
|
|
277
|
+
//#region src/reporter.ts
|
|
278
|
+
/**
|
|
279
|
+
* Vitest reporter that uploads the screenshots captured during the run to Argos.
|
|
280
|
+
*/
|
|
281
|
+
var ArgosReporter = class {
|
|
282
|
+
vitest;
|
|
283
|
+
config;
|
|
284
|
+
constructor(config) {
|
|
285
|
+
this.config = config;
|
|
286
|
+
}
|
|
287
|
+
onInit(vitest) {
|
|
288
|
+
this.vitest = vitest;
|
|
289
|
+
}
|
|
290
|
+
async onFinished() {
|
|
291
|
+
await this.onTestRunEnd();
|
|
292
|
+
}
|
|
293
|
+
async onTestRunEnd() {
|
|
294
|
+
if (this.vitest.config.watch) return;
|
|
295
|
+
const res = await upload({
|
|
296
|
+
files: [
|
|
297
|
+
"**/*.png",
|
|
298
|
+
"**/*.aria.yml",
|
|
299
|
+
"**/*.snapshot.*"
|
|
300
|
+
],
|
|
301
|
+
ignore: ["**/*.argos.json"],
|
|
302
|
+
...this.config
|
|
303
|
+
});
|
|
304
|
+
console.log(`✅ Argos build created: ${res.build.url}`);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
//#endregion
|
|
308
|
+
//#region src/plugin.ts
|
|
309
|
+
const cwd = process.cwd();
|
|
310
|
+
/**
|
|
311
|
+
* Vitest plugin that registers the `argosScreenshot` browser command and,
|
|
312
|
+
* optionally, the reporter that uploads the captured screenshots to Argos.
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* ```ts
|
|
316
|
+
* import { defineConfig } from "vitest/config";
|
|
317
|
+
* import { playwright } from "@vitest/browser-playwright";
|
|
318
|
+
* import { argosVitestPlugin } from "@argos-ci/vitest/plugin";
|
|
319
|
+
*
|
|
320
|
+
* export default defineConfig({
|
|
321
|
+
* plugins: [argosVitestPlugin({ uploadToArgos: true })],
|
|
322
|
+
* test: {
|
|
323
|
+
* browser: {
|
|
324
|
+
* enabled: true,
|
|
325
|
+
* provider: playwright(),
|
|
326
|
+
* instances: [{ browser: "chromium" }],
|
|
327
|
+
* },
|
|
328
|
+
* },
|
|
329
|
+
* });
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
function argosVitestPlugin(options) {
|
|
333
|
+
const { root: unresolvedRoot = "./screenshots", uploadToArgos, ...otherOptions } = options ?? {};
|
|
334
|
+
const root = resolve(cwd, unresolvedRoot);
|
|
335
|
+
return {
|
|
336
|
+
name: "@argos-ci/vitest",
|
|
337
|
+
configureVitest({ vitest }) {
|
|
338
|
+
if (uploadToArgos) vitest.config.reporters.push(new ArgosReporter({
|
|
339
|
+
...otherOptions,
|
|
340
|
+
root
|
|
341
|
+
}));
|
|
342
|
+
},
|
|
343
|
+
config() {
|
|
344
|
+
return {
|
|
345
|
+
optimizeDeps: { include: ["@argos-ci/vitest"] },
|
|
346
|
+
test: { browser: { commands: {
|
|
347
|
+
argosScreenshot: createArgosScreenshotCommand({
|
|
348
|
+
...otherOptions,
|
|
349
|
+
root
|
|
350
|
+
}),
|
|
351
|
+
argosSnapshot: createArgosSnapshotCommand({
|
|
352
|
+
...otherOptions,
|
|
353
|
+
root
|
|
354
|
+
})
|
|
355
|
+
} } }
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
//#endregion
|
|
361
|
+
export { ArgosReporter, argosVitestPlugin, createArgosScreenshotCommand, createArgosSnapshotCommand };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, resolve } from "node:path";
|
|
4
|
+
import { getSnapshotMimeType } from "@argos-ci/core";
|
|
5
|
+
import { createDirectory, getMetadataPath, getScreenshotName, readVersionFromPackage, writeMetadata } from "@argos-ci/util";
|
|
6
|
+
//#region src/version.ts
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
/**
|
|
9
|
+
* Get the version of the Argos Vitest SDK.
|
|
10
|
+
*/
|
|
11
|
+
async function getArgosVitestVersion() {
|
|
12
|
+
return readVersionFromPackage(require.resolve("@argos-ci/vitest/package.json"));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get the version of Vitest itself (used as the automation library for
|
|
16
|
+
* `argosSnapshot`, which does not rely on a browser).
|
|
17
|
+
*/
|
|
18
|
+
async function getVitestVersion() {
|
|
19
|
+
return readVersionFromPackage(require.resolve("vitest/package.json"));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Default extension of a serialized snapshot file.
|
|
23
|
+
*/
|
|
24
|
+
const DEFAULT_EXTENSION = ".txt";
|
|
25
|
+
/**
|
|
26
|
+
* Marker inserted before the extension so the reporter can reliably pick up
|
|
27
|
+
* snapshots (`**\/*.snapshot.*`) without matching screenshots or ARIA files.
|
|
28
|
+
*/
|
|
29
|
+
const SNAPSHOT_INFIX = ".snapshot";
|
|
30
|
+
/**
|
|
31
|
+
* Normalize a user-provided extension to a leading-dot form (`.txt`).
|
|
32
|
+
*/
|
|
33
|
+
function normalizeExtension(extension) {
|
|
34
|
+
return extension.startsWith(".") ? extension : `.${extension}`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Write a serialized snapshot (and its metadata) to disk and return the
|
|
38
|
+
* corresponding Argos attachments.
|
|
39
|
+
*
|
|
40
|
+
* This is the shared Node-side primitive used by both the browser command (which
|
|
41
|
+
* receives the already-serialized string over RPC) and the Node code path.
|
|
42
|
+
*/
|
|
43
|
+
async function writeSnapshotFile(name, content, options = {}) {
|
|
44
|
+
if (!name) throw new Error("The `name` argument is required.");
|
|
45
|
+
const root = options.root ?? "./screenshots";
|
|
46
|
+
const extension = normalizeExtension(options.extension ?? DEFAULT_EXTENSION);
|
|
47
|
+
const snapshotPath = resolve(root, `${getScreenshotName(name)}${SNAPSHOT_INFIX}${extension}`);
|
|
48
|
+
const [vitestVersion, sdkVersion] = await Promise.all([getVitestVersion(), getArgosVitestVersion()]);
|
|
49
|
+
const tags = options.tag ? Array.isArray(options.tag) ? options.tag : [options.tag] : void 0;
|
|
50
|
+
const metadata = {
|
|
51
|
+
automationLibrary: {
|
|
52
|
+
name: "vitest",
|
|
53
|
+
version: vitestVersion
|
|
54
|
+
},
|
|
55
|
+
sdk: {
|
|
56
|
+
name: "@argos-ci/vitest",
|
|
57
|
+
version: sdkVersion
|
|
58
|
+
},
|
|
59
|
+
...tags ? { tags } : {}
|
|
60
|
+
};
|
|
61
|
+
await createDirectory(dirname(snapshotPath));
|
|
62
|
+
await Promise.all([writeFile(snapshotPath, content, "utf-8"), writeMetadata(snapshotPath, metadata)]);
|
|
63
|
+
return [{
|
|
64
|
+
name: `argos/snapshot___${name}`,
|
|
65
|
+
contentType: getSnapshotMimeType(snapshotPath),
|
|
66
|
+
path: snapshotPath
|
|
67
|
+
}, {
|
|
68
|
+
name: `argos/snapshot/metadata___${name}`,
|
|
69
|
+
contentType: "application/json",
|
|
70
|
+
path: getMetadataPath(snapshotPath)
|
|
71
|
+
}];
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { writeSnapshotFile };
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@argos-ci/vitest",
|
|
3
|
+
"description": "Vitest SDK for visual testing with Argos.",
|
|
4
|
+
"version": "0.2.3",
|
|
5
|
+
"author": "Smooth Code",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/argos-ci/argos-javascript.git",
|
|
10
|
+
"directory": "packages/vitest"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://argos-ci.com/docs/sdks-reference/vitest",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/argos-ci/argos-javascript/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"vitest",
|
|
18
|
+
"vitest-plugin",
|
|
19
|
+
"vitest-browser",
|
|
20
|
+
"screenshot",
|
|
21
|
+
"capture",
|
|
22
|
+
"testing",
|
|
23
|
+
"visual testing",
|
|
24
|
+
"argos",
|
|
25
|
+
"regression",
|
|
26
|
+
"visual regression"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"type": "module",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/index.d.mts",
|
|
35
|
+
"import": "./dist/index.mjs",
|
|
36
|
+
"default": "./dist/index.mjs"
|
|
37
|
+
},
|
|
38
|
+
"./plugin": {
|
|
39
|
+
"types": "./dist/plugin.d.mts",
|
|
40
|
+
"import": "./dist/plugin.mjs",
|
|
41
|
+
"default": "./dist/plugin.mjs"
|
|
42
|
+
},
|
|
43
|
+
"./internal": {
|
|
44
|
+
"types": "./dist/internal.d.mts",
|
|
45
|
+
"import": "./dist/internal.mjs",
|
|
46
|
+
"default": "./dist/internal.mjs"
|
|
47
|
+
},
|
|
48
|
+
"./package.json": "./package.json"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=22.0.0"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@argos-ci/browser": "6.4.4",
|
|
55
|
+
"@argos-ci/core": "6.5.3",
|
|
56
|
+
"@argos-ci/playwright": "7.3.4",
|
|
57
|
+
"@argos-ci/util": "4.0.4"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"@vitest/browser": ">=4",
|
|
61
|
+
"@vitest/browser-playwright": ">=4",
|
|
62
|
+
"playwright": ">=1",
|
|
63
|
+
"vitest": ">=4"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@types/node": "catalog:",
|
|
67
|
+
"@vitest/browser": "^4.1.9",
|
|
68
|
+
"@vitest/browser-playwright": "^4.1.9",
|
|
69
|
+
"@vitest/pretty-format": "^4.1.9",
|
|
70
|
+
"playwright": "^1.61.0",
|
|
71
|
+
"vitest": "catalog:"
|
|
72
|
+
},
|
|
73
|
+
"scripts": {
|
|
74
|
+
"build": "tsdown",
|
|
75
|
+
"test": "vitest run --project unit",
|
|
76
|
+
"test-e2e": "vitest run --project e2e",
|
|
77
|
+
"install-playwright": "playwright install chromium --with-deps",
|
|
78
|
+
"build-e2e": "pnpm run install-playwright",
|
|
79
|
+
"e2e": "cross-env BUILD_NAME=\"argos-vitest-e2e-node-$NODE_VERSION-$OS\" UPLOAD_TO_ARGOS=true pnpm run test-e2e",
|
|
80
|
+
"check-types": "tsc",
|
|
81
|
+
"check-format": "prettier --check --ignore-unknown --ignore-path=./.gitignore --ignore-path=../../.gitignore --ignore-path=../../.prettierignore .",
|
|
82
|
+
"lint": "eslint ."
|
|
83
|
+
},
|
|
84
|
+
"gitHead": "1b25b7c1109a3d032d627c5f9d2f2c3937731aea"
|
|
85
|
+
}
|