@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.
@@ -0,0 +1,77 @@
1
+ import { ArgosAttachment, ArgosScreenshotOptions } from "@argos-ci/playwright";
2
+ import { BrowserCommandContext } from "vitest/node";
3
+ import { ViewportSize } from "@argos-ci/browser";
4
+
5
+ //#region src/iframe.d.ts
6
+ /**
7
+ * Selector of the iframe Vitest renders the test into on the orchestrator page.
8
+ */
9
+ declare const VITEST_IFRAME_SELECTOR = "iframe[data-vitest=\"true\"]";
10
+ /**
11
+ * ID of the Vitest "tester" element that wraps the iframe with a `scale(...)`
12
+ * transform.
13
+ */
14
+ declare const VITEST_TESTER_ID = "vitest-tester";
15
+ /**
16
+ * Remove the scale from the Vitest `#vitest-tester` element before taking a
17
+ * screenshot to avoid ending up with small screenshots.
18
+ * @returns A function to restore the scale after the screenshot.
19
+ */
20
+ declare function resetTesterScale(ctx: BrowserCommandContext): Promise<() => Promise<void>>;
21
+ /**
22
+ * Resize the Vitest iframe.
23
+ *
24
+ * The story/test renders inside an `<iframe data-vitest="true">` on the host
25
+ * page and we screenshot the iframe's `<body>`. Anything overflowing the iframe
26
+ * box is not painted, so the iframe must be sized to hold the content.
27
+ *
28
+ * @param size - The viewport size, `"default"` to keep the natural size, or
29
+ * `"initial"` to restore the size backed up on the first resize.
30
+ * @param options.fullPage - When `true`, grow the height to fit the content
31
+ * while keeping the viewport width (Playwright-style full page).
32
+ */
33
+ declare function setIframeViewportSize(ctx: BrowserCommandContext, size: ViewportSize | "default" | "initial", options?: {
34
+ fullPage?: boolean;
35
+ }): Promise<void>;
36
+ /**
37
+ * Grow the Vitest iframe to fit its content so nothing is clipped.
38
+ *
39
+ * This must run *after* `argosCSS` (which may inject a `zoom`) is applied,
40
+ * because `setIframeViewportSize` sizes the iframe *before* the content's final
41
+ * size is known. It only ever grows the iframe, never shrinks it.
42
+ *
43
+ * @param options.fitWidth - Also grow the iframe horizontally to paint content
44
+ * wider than the viewport. When `false`, only the height grows (to match
45
+ * Playwright's `fullPage` semantics: full height, viewport width).
46
+ */
47
+ declare function fitIframeToContent(ctx: BrowserCommandContext, options: {
48
+ fitWidth: boolean;
49
+ }): Promise<void>;
50
+ //#endregion
51
+ //#region src/screenshot.d.ts
52
+ /**
53
+ * Take a screenshot of the Vitest iframe body using the Playwright SDK.
54
+ *
55
+ * This is the shared primitive both the standalone command and Storybook build
56
+ * on. It:
57
+ * - strips the Vitest-specific `viewports`/`fullPage` options (they drive the
58
+ * iframe resize, not Playwright);
59
+ * - wraps `beforeScreenshot` so the content is grown to fit *after* `argosCSS`
60
+ * (and any user `beforeScreenshot`) has been applied — otherwise wide/tall
61
+ * content would be clipped;
62
+ * - captures the iframe's `<body>` via `@argos-ci/playwright`.
63
+ *
64
+ * @param config.fitWidth - Grow the iframe horizontally as well as vertically
65
+ * to fit the content (used when not capturing a fixed viewport width).
66
+ */
67
+ declare function screenshotFrame(ctx: BrowserCommandContext, name: string, options: ArgosScreenshotOptions, config: {
68
+ fitWidth: boolean;
69
+ }): Promise<ArgosAttachment[]>;
70
+ //#endregion
71
+ //#region src/version.d.ts
72
+ /**
73
+ * Get the version of the Argos Vitest SDK.
74
+ */
75
+ declare function getArgosVitestVersion(): Promise<string>;
76
+ //#endregion
77
+ export { VITEST_IFRAME_SELECTOR, VITEST_TESTER_ID, fitIframeToContent, getArgosVitestVersion, resetTesterScale, screenshotFrame, setIframeViewportSize };
@@ -0,0 +1,145 @@
1
+ import { createRequire } from "node:module";
2
+ import { argosScreenshot } from "@argos-ci/playwright";
3
+ import { readVersionFromPackage } from "@argos-ci/util";
4
+ //#region src/iframe.ts
5
+ /**
6
+ * Selector of the iframe Vitest renders the test into on the orchestrator page.
7
+ */
8
+ const VITEST_IFRAME_SELECTOR = "iframe[data-vitest=\"true\"]";
9
+ /**
10
+ * ID of the Vitest "tester" element that wraps the iframe with a `scale(...)`
11
+ * transform.
12
+ */
13
+ const VITEST_TESTER_ID = "vitest-tester";
14
+ /**
15
+ * Remove the scale from the Vitest `#vitest-tester` element before taking a
16
+ * screenshot to avoid ending up with small screenshots.
17
+ * @returns A function to restore the scale after the screenshot.
18
+ */
19
+ async function resetTesterScale(ctx) {
20
+ await ctx.page.evaluate((testerId) => {
21
+ const tester = document.getElementById(testerId);
22
+ if (!(tester instanceof HTMLElement)) return;
23
+ if (!tester.getAttribute("data-scale")) throw new Error("Vitest iframe data-scale attribute not found");
24
+ tester.dataset.bckTransform = tester.style.transform;
25
+ tester.style.transform = `scale(1)`;
26
+ }, VITEST_TESTER_ID);
27
+ return async () => {
28
+ await ctx.page.evaluate((testerId) => {
29
+ const tester = document.getElementById(testerId);
30
+ if (!(tester instanceof HTMLElement)) return;
31
+ tester.style.transform = tester.dataset.bckTransform ?? "";
32
+ }, VITEST_TESTER_ID);
33
+ };
34
+ }
35
+ /**
36
+ * Resize the Vitest iframe.
37
+ *
38
+ * The story/test renders inside an `<iframe data-vitest="true">` on the host
39
+ * page and we screenshot the iframe's `<body>`. Anything overflowing the iframe
40
+ * box is not painted, so the iframe must be sized to hold the content.
41
+ *
42
+ * @param size - The viewport size, `"default"` to keep the natural size, or
43
+ * `"initial"` to restore the size backed up on the first resize.
44
+ * @param options.fullPage - When `true`, grow the height to fit the content
45
+ * while keeping the viewport width (Playwright-style full page).
46
+ */
47
+ async function setIframeViewportSize(ctx, size, options = {}) {
48
+ await ctx.page.evaluate(({ size, fullPage, selector }) => {
49
+ const iframe = document.querySelector(selector);
50
+ if (!(iframe instanceof HTMLIFrameElement)) throw new Error("Vitest iframe not found");
51
+ if (!iframe.contentDocument) throw new Error("Vitest iframe contentDocument not found");
52
+ if (size === "initial") {
53
+ if (iframe.dataset.initialWidth && iframe.dataset.initialHeight) {
54
+ iframe.style.width = iframe.dataset.initialWidth;
55
+ iframe.style.height = iframe.dataset.initialHeight;
56
+ }
57
+ return;
58
+ }
59
+ if (!iframe.dataset.initialWidth && !iframe.dataset.initialHeight) {
60
+ iframe.dataset.initialWidth = iframe.style.width;
61
+ iframe.dataset.initialHeight = iframe.style.height;
62
+ }
63
+ if (size !== "default") iframe.style.width = `${size.width}px`;
64
+ if (fullPage) {
65
+ if (!iframe.contentWindow) throw new Error(`Can't access iframe window`);
66
+ const viewportHeight = size === "default" ? iframe.contentWindow.innerHeight : size.height;
67
+ iframe.style.height = "auto";
68
+ iframe.style.height = viewportHeight < iframe.contentDocument.body.offsetHeight ? `${iframe.contentDocument.body.offsetHeight}px` : "100%";
69
+ } else if (size !== "default") {
70
+ iframe.style.height = "auto";
71
+ iframe.style.height = `${size.height}px`;
72
+ }
73
+ }, {
74
+ size,
75
+ fullPage: options.fullPage ?? false,
76
+ selector: VITEST_IFRAME_SELECTOR
77
+ });
78
+ }
79
+ /**
80
+ * Grow the Vitest iframe to fit its content so nothing is clipped.
81
+ *
82
+ * This must run *after* `argosCSS` (which may inject a `zoom`) is applied,
83
+ * because `setIframeViewportSize` sizes the iframe *before* the content's final
84
+ * size is known. It only ever grows the iframe, never shrinks it.
85
+ *
86
+ * @param options.fitWidth - Also grow the iframe horizontally to paint content
87
+ * wider than the viewport. When `false`, only the height grows (to match
88
+ * Playwright's `fullPage` semantics: full height, viewport width).
89
+ */
90
+ async function fitIframeToContent(ctx, options) {
91
+ await ctx.page.evaluate(({ fitWidth, selector }) => {
92
+ const iframe = document.querySelector(selector);
93
+ if (!(iframe instanceof HTMLIFrameElement) || !iframe.contentDocument) return;
94
+ const { body, documentElement } = iframe.contentDocument;
95
+ const contentHeight = Math.max(body.scrollHeight, body.offsetHeight, documentElement.scrollHeight);
96
+ if (contentHeight > iframe.clientHeight) iframe.style.height = `${contentHeight}px`;
97
+ if (fitWidth) {
98
+ const contentWidth = Math.max(body.scrollWidth, body.offsetWidth, documentElement.scrollWidth);
99
+ if (contentWidth > iframe.clientWidth) iframe.style.width = `${contentWidth}px`;
100
+ }
101
+ }, {
102
+ fitWidth: options.fitWidth,
103
+ selector: VITEST_IFRAME_SELECTOR
104
+ });
105
+ }
106
+ //#endregion
107
+ //#region src/screenshot.ts
108
+ /**
109
+ * Take a screenshot of the Vitest iframe body using the Playwright SDK.
110
+ *
111
+ * This is the shared primitive both the standalone command and Storybook build
112
+ * on. It:
113
+ * - strips the Vitest-specific `viewports`/`fullPage` options (they drive the
114
+ * iframe resize, not Playwright);
115
+ * - wraps `beforeScreenshot` so the content is grown to fit *after* `argosCSS`
116
+ * (and any user `beforeScreenshot`) has been applied — otherwise wide/tall
117
+ * content would be clipped;
118
+ * - captures the iframe's `<body>` via `@argos-ci/playwright`.
119
+ *
120
+ * @param config.fitWidth - Grow the iframe horizontally as well as vertically
121
+ * to fit the content (used when not capturing a fixed viewport width).
122
+ */
123
+ async function screenshotFrame(ctx, name, options, config) {
124
+ const { viewports: _viewports, fullPage: _fullPage, ...rest } = options;
125
+ const userBeforeScreenshot = rest.beforeScreenshot;
126
+ const playwrightOptions = {
127
+ ...rest,
128
+ beforeScreenshot: async (api) => {
129
+ await userBeforeScreenshot?.(api);
130
+ await fitIframeToContent(ctx, { fitWidth: config.fitWidth });
131
+ }
132
+ };
133
+ return argosScreenshot(await ctx.frame(), name, playwrightOptions);
134
+ }
135
+ //#endregion
136
+ //#region src/version.ts
137
+ const require = createRequire(import.meta.url);
138
+ /**
139
+ * Get the version of the Argos Vitest SDK.
140
+ */
141
+ async function getArgosVitestVersion() {
142
+ return readVersionFromPackage(require.resolve("@argos-ci/vitest/package.json"));
143
+ }
144
+ //#endregion
145
+ export { VITEST_IFRAME_SELECTOR, VITEST_TESTER_ID, fitIframeToContent, getArgosVitestVersion, resetTesterScale, screenshotFrame, setIframeViewportSize };
@@ -0,0 +1,203 @@
1
+ import { ArgosScreenshotOptions } from "@argos-ci/playwright";
2
+ import { StabilizationPluginOptions, ViewportOption } from "@argos-ci/browser";
3
+ import { UploadParameters } from "@argos-ci/core";
4
+ import { Plugin } from "vitest/config";
5
+ import { BrowserCommand, Vitest } from "vitest/node";
6
+ import { Reporter } from "vitest/reporters";
7
+
8
+ //#region src/options.d.ts
9
+ /**
10
+ * Configuration for the Argos Vitest reporter.
11
+ * @see https://js-sdk-reference.argos-ci.com/interfaces/UploadParameters.html
12
+ */
13
+ type ArgosReporterConfig = UploadParameters;
14
+ /**
15
+ * Options passed when calling `argosScreenshot` from a browser test.
16
+ *
17
+ * These options cross the Vitest browser/node RPC boundary, so they must be
18
+ * JSON-serializable. Non-serializable options (`beforeScreenshot`,
19
+ * `afterScreenshot`, a `Locator`/`ElementHandle` `element`, …) can only be set
20
+ * on the plugin via {@link ArgosVitestPluginOptions}.
21
+ */
22
+ interface VitestScreenshotOptions {
23
+ /**
24
+ * String selector of the element to take a screenshot of.
25
+ * A `Locator`/`ElementHandle` cannot be used here because it can't be
26
+ * serialized across the browser/node boundary — set it on the plugin instead.
27
+ */
28
+ element?: string;
29
+ /**
30
+ * Viewports to take screenshots of.
31
+ * Implemented by resizing the Vitest iframe (Playwright's native `viewports`
32
+ * option does not work on a frame).
33
+ */
34
+ viewports?: ViewportOption[];
35
+ /**
36
+ * Capture the full page instead of fitting the screenshot to the content.
37
+ * - `false` (default): the iframe grows to fit the content in both
38
+ * dimensions, so nothing is clipped.
39
+ * - `true`: keep the viewport width and grow the height (Playwright-style
40
+ * full page).
41
+ * @default false
42
+ */
43
+ fullPage?: boolean;
44
+ /**
45
+ * Custom CSS evaluated during the screenshot process.
46
+ */
47
+ argosCSS?: string;
48
+ /**
49
+ * Sensitivity threshold between 0 and 1.
50
+ * The higher the threshold, the less sensitive the diff will be.
51
+ * @default 0.5
52
+ */
53
+ threshold?: number;
54
+ /**
55
+ * Tag or array of tags to attach to the screenshot.
56
+ */
57
+ tag?: string | string[];
58
+ /**
59
+ * Capture an ARIA snapshot along with the screenshot.
60
+ * @default false
61
+ */
62
+ ariaSnapshot?: boolean;
63
+ /**
64
+ * Disable hover effects by moving the mouse to the top-left corner.
65
+ * @default true
66
+ */
67
+ disableHover?: boolean;
68
+ /**
69
+ * Wait for the UI to stabilize before taking the screenshot.
70
+ * Set to `false` to disable stabilization or pass an object to customize it.
71
+ * @default true
72
+ */
73
+ stabilize?: boolean | StabilizationPluginOptions;
74
+ }
75
+ /**
76
+ * Options passed when calling `argosSnapshot`.
77
+ *
78
+ * `argosSnapshot` serializes any value to a file that Argos picks up and diffs,
79
+ * mimicking {@link https://vitest.dev/guide/snapshot Vitest snapshots}. Unlike
80
+ * `argosScreenshot`, it does not need a browser and works both in Vitest browser
81
+ * tests and in plain Node tests.
82
+ *
83
+ * These options must be JSON-serializable so they can cross the Vitest
84
+ * browser/node RPC boundary — the only exception is `serialize`, which is
85
+ * applied on the test side *before* the value is sent to Node.
86
+ */
87
+ interface VitestSnapshotOptions {
88
+ /**
89
+ * Folder where the snapshot is written.
90
+ *
91
+ * In Node tests this defaults to `"./screenshots"`. In browser tests it
92
+ * defaults to the plugin `root` and can be overridden per call.
93
+ * @default "./screenshots"
94
+ */
95
+ root?: string;
96
+ /**
97
+ * Extension of the snapshot file. It also determines how Argos renders and
98
+ * diffs the snapshot (e.g. `.txt`, `.json`, `.yml`, `.html`, `.md`).
99
+ * @default ".txt"
100
+ */
101
+ extension?: string;
102
+ /**
103
+ * Tag or array of tags to attach to the snapshot.
104
+ */
105
+ tag?: string | string[];
106
+ /**
107
+ * Custom serializer used when `content` is not already a string.
108
+ * Defaults to `@vitest/pretty-format` (the serializer Vitest itself uses).
109
+ */
110
+ serialize?: (content: unknown) => string;
111
+ }
112
+ /**
113
+ * Subset of {@link VitestSnapshotOptions} that can cross the Vitest
114
+ * browser/node RPC boundary (everything but `serialize`, which is applied
115
+ * before the value is sent to Node).
116
+ */
117
+ type SerializableSnapshotOptions = Omit<VitestSnapshotOptions, "serialize">;
118
+ /**
119
+ * Options for the Argos Vitest plugin.
120
+ *
121
+ * Accepts every option supported by the Playwright `argosScreenshot` function
122
+ * (including non-serializable ones like `beforeScreenshot`), all Argos upload
123
+ * parameters, plus the plugin-specific options below. These act as defaults for
124
+ * every screenshot and can be overridden per call with the serializable
125
+ * {@link VitestScreenshotOptions}.
126
+ */
127
+ interface ArgosVitestPluginOptions extends ArgosReporterConfig, ArgosScreenshotOptions {
128
+ /**
129
+ * Upload the report to Argos at the end of the run.
130
+ * @default false
131
+ */
132
+ uploadToArgos?: boolean;
133
+ }
134
+ //#endregion
135
+ //#region src/command.d.ts
136
+ /**
137
+ * Arguments of the `argosScreenshot` browser command.
138
+ * Only serializable values cross the browser/node RPC boundary.
139
+ */
140
+ type ArgosScreenshotCommandArgs = [name: string, options?: VitestScreenshotOptions];
141
+ /**
142
+ * Create the `argosScreenshot` browser command used to capture Argos
143
+ * screenshots from Vitest browser tests.
144
+ *
145
+ * Non-serializable options (`beforeScreenshot`, `afterScreenshot`, a
146
+ * `Locator`/`ElementHandle` `element`, …) come from `pluginOptions` (node side)
147
+ * and are merged with the serializable per-call options (per-call wins).
148
+ */
149
+ declare const createArgosScreenshotCommand: (pluginOptions?: ArgosVitestPluginOptions) => BrowserCommand<ArgosScreenshotCommandArgs>;
150
+ //#endregion
151
+ //#region src/snapshot-command.d.ts
152
+ /**
153
+ * Arguments of the `argosSnapshot` browser command.
154
+ * Only serializable values cross the browser/node RPC boundary — the value is
155
+ * already serialized to a string on the browser side.
156
+ */
157
+ type ArgosSnapshotCommandArgs = [name: string, content: string, options?: SerializableSnapshotOptions];
158
+ /**
159
+ * Create the `argosSnapshot` browser command used to write serialized snapshots
160
+ * from Vitest browser tests. The serialized string is produced on the browser
161
+ * side and this command writes it (and its metadata) to disk on the Node side.
162
+ */
163
+ declare const createArgosSnapshotCommand: (pluginOptions?: ArgosVitestPluginOptions) => BrowserCommand<ArgosSnapshotCommandArgs>;
164
+ //#endregion
165
+ //#region src/reporter.d.ts
166
+ /**
167
+ * Vitest reporter that uploads the screenshots captured during the run to Argos.
168
+ */
169
+ declare class ArgosReporter implements Reporter {
170
+ vitest: Vitest;
171
+ config: ArgosReporterConfig;
172
+ constructor(config: ArgosReporterConfig);
173
+ onInit(vitest: Vitest): void;
174
+ onFinished(): Promise<void>;
175
+ onTestRunEnd(): Promise<void>;
176
+ }
177
+ //#endregion
178
+ //#region src/plugin.d.ts
179
+ /**
180
+ * Vitest plugin that registers the `argosScreenshot` browser command and,
181
+ * optionally, the reporter that uploads the captured screenshots to Argos.
182
+ *
183
+ * @example
184
+ * ```ts
185
+ * import { defineConfig } from "vitest/config";
186
+ * import { playwright } from "@vitest/browser-playwright";
187
+ * import { argosVitestPlugin } from "@argos-ci/vitest/plugin";
188
+ *
189
+ * export default defineConfig({
190
+ * plugins: [argosVitestPlugin({ uploadToArgos: true })],
191
+ * test: {
192
+ * browser: {
193
+ * enabled: true,
194
+ * provider: playwright(),
195
+ * instances: [{ browser: "chromium" }],
196
+ * },
197
+ * },
198
+ * });
199
+ * ```
200
+ */
201
+ declare function argosVitestPlugin(options?: ArgosVitestPluginOptions): Plugin;
202
+ //#endregion
203
+ export { ArgosReporter, type ArgosReporterConfig, type ArgosScreenshotCommandArgs, type ArgosSnapshotCommandArgs, type ArgosVitestPluginOptions, type VitestScreenshotOptions, type VitestSnapshotOptions, argosVitestPlugin, createArgosScreenshotCommand, createArgosSnapshotCommand };