@snapvisor/storybook 6.1.8

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 ADDED
@@ -0,0 +1,8 @@
1
+ Copyright 2026 Devino Solutions Inc. (Snapvisor modifications)
2
+ Copyright 2022 Smooth Code
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5
+
6
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7
+
8
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ <p align="center">
2
+ <a href="https://argos-ci.com/?utm_source=github&utm_medium=logo" target="_blank">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/argos-ci/argos/main/resources/logos/github-readme-logo-dark.png">
5
+ <img alt="Argos" src="https://raw.githubusercontent.com/argos-ci/argos/main/resources/logos/github-readme-logo-light.png" width="360" height="70">
6
+ </picture>
7
+ </a>
8
+ </p>
9
+
10
+ <p align="center"><strong>The open source visual testing platform for AI-native engineering teams.</strong></p>
11
+
12
+ # Official Argos Storybook integration
13
+
14
+ [![npm version](https://img.shields.io/npm/v/@snapvisor/storybook.svg)](https://www.npmjs.com/package/@snapvisor/storybook)
15
+ [![npm dm](https://img.shields.io/npm/dm/@snapvisor/storybook.svg)](https://www.npmjs.com/package/@snapvisor/storybook)
16
+ [![npm dt](https://img.shields.io/npm/dt/@snapvisor/storybook.svg)](https://www.npmjs.com/package/@snapvisor/storybook)
17
+
18
+ Capture and review visual changes of your [Storybook](https://storybook.js.org/) stories with Argos. It runs your stories in a real browser and uploads a screenshot of each one to Argos in your CI.
19
+
20
+ Visit the [Storybook SDK documentation](https://argos-ci.com/docs/reference/storybook) for guides, the API reference, and more.
21
+
22
+ ## Installation
23
+
24
+ ```sh
25
+ npm install --save-dev @snapvisor/storybook
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ The recommended way to run visual tests is with the [Storybook Vitest addon](https://storybook.js.org/docs/writing-tests/integrations/vitest-addon). Register the Argos plugin in your Vitest config:
31
+
32
+ ```ts
33
+ // vitest.config.ts
34
+ import { defineConfig } from "vitest/config";
35
+ import { argosVitestPlugin } from "@snapvisor/storybook/vitest-plugin";
36
+
37
+ export default defineConfig({
38
+ plugins: [
39
+ argosVitestPlugin({
40
+ // Upload the screenshots to Argos only on CI.
41
+ uploadToArgos: process.env.CI === "true",
42
+ }),
43
+ ],
44
+ });
45
+ ```
46
+
47
+ A screenshot is captured for every story automatically. To capture additional screenshots (for example, at different steps of an interaction), call `argosScreenshot` inside a story's `play` function:
48
+
49
+ ```ts
50
+ // Button.stories.tsx
51
+ import { argosScreenshot } from "@snapvisor/storybook/vitest";
52
+
53
+ export const Example: Story = {
54
+ play: async (ctx) => {
55
+ await argosScreenshot(ctx, "example");
56
+ },
57
+ };
58
+ ```
59
+
60
+ > Using the [Storybook Test Runner](https://storybook.js.org/docs/writing-tests/integrations/test-runner) instead? Import `argosScreenshot` from `@snapvisor/storybook/test-runner` and call it from the `postVisit` hook. See the [Test Runner quickstart](https://argos-ci.com/docs/quickstart/storybook-quickstart/storybook-test-runner-quickstart).
61
+
62
+ ## Links
63
+
64
+ - [Official SDK Docs](https://argos-ci.com/docs/reference/storybook)
65
+ - [Quickstart](https://argos-ci.com/docs/quickstart/storybook-quickstart)
66
+ - [Discord](https://argos-ci.com/discord)
@@ -0,0 +1,5 @@
1
+ const argosScreenshot = async (...args) => {
2
+ const { argosScreenshot } = await import("./test-runner.mjs");
3
+ return argosScreenshot(...args);
4
+ };
5
+ exports.argosScreenshot = argosScreenshot;
@@ -0,0 +1,60 @@
1
+ import { TestContext } from "@storybook/test-runner";
2
+ import { ArgosScreenshotOptions as ArgosScreenshotOptions$1 } from "@snapvisor/playwright";
3
+ import { Page } from "playwright";
4
+
5
+ //#region src/utils/parameters.d.ts
6
+ type StorybookGlobals = Record<string, any>;
7
+ type FitToContent = {
8
+ /**
9
+ * Padding around the content in pixels.
10
+ * @default 16
11
+ */
12
+ padding: number;
13
+ /**
14
+ * Zoom level for the content.
15
+ * @default 2
16
+ */
17
+ zoom: number;
18
+ };
19
+ /**
20
+ * Argos parameters in Storybook.
21
+ */
22
+ interface ArgosStorybookParameters {
23
+ /**
24
+ * Modes for the story.
25
+ */
26
+ modes?: Record<string, StorybookGlobals>;
27
+ /**
28
+ * Fit to content option for the story.
29
+ */
30
+ fitToContent?: boolean | Partial<FitToContent>;
31
+ }
32
+ //#endregion
33
+ //#region src/utils/screenshot.d.ts
34
+ type ArgosScreenshotOptions = Omit<ArgosScreenshotOptions$1, "viewports">;
35
+ //#endregion
36
+ //#region src/test-runner.d.ts
37
+ /**
38
+ * Stabilize the UI and takes a screenshot of the application under test.
39
+ *
40
+ * @example argosScreenshot(page, context, options)
41
+ * @see https://argos-ci.com/docs/reference/playwright
42
+ */
43
+ declare function argosScreenshot(
44
+ /**
45
+ * Playwright `page` object.
46
+ */
47
+
48
+ page: Page,
49
+ /**
50
+ * Context of the test.
51
+ */
52
+
53
+ context: TestContext,
54
+ /**
55
+ * Options for the screenshot.
56
+ */
57
+
58
+ options?: ArgosScreenshotOptions): Promise<void>;
59
+ //#endregion
60
+ export { type ArgosScreenshotOptions, type ArgosStorybookParameters, argosScreenshot };
@@ -0,0 +1,311 @@
1
+ import { createRequire } from "node:module";
2
+ import { getStoryContext, waitForPageReady } from "@storybook/test-runner";
3
+ import { DO_NOT_USE_setMetadataConfig, argosScreenshot as argosScreenshot$1 } from "@snapvisor/playwright";
4
+ import { readVersionFromPackage } from "@snapvisor/util";
5
+ //#region src/utils/metadata.ts
6
+ const require = createRequire(import.meta.url);
7
+ /**
8
+ * Get the version of the Argos Playwright SDK.
9
+ */
10
+ async function getArgosStorybookVersion() {
11
+ return readVersionFromPackage(require.resolve("@snapvisor/storybook/package.json"));
12
+ }
13
+ //#endregion
14
+ //#region src/utils/storyMetadata.ts
15
+ function toArray(v) {
16
+ return Array.isArray(v) ? v : v ? [v] : [];
17
+ }
18
+ function mergeTags(...tags) {
19
+ const merged = tags.flatMap((tags) => toArray(tags));
20
+ return Array.from(new Set(merged)).filter((tag) => typeof tag === "string" && tag !== "");
21
+ }
22
+ /**
23
+ * Return whether a story context has a play function.
24
+ * Handles both the `play` field (plain story function) and the legacy
25
+ * `playFunction` field present in older Storybook test-runner contexts.
26
+ */
27
+ function hasPlay(storyContext) {
28
+ if (!storyContext || typeof storyContext !== "object") return false;
29
+ return "play" in storyContext && typeof storyContext.play === "function" || "playFunction" in storyContext && typeof storyContext.playFunction === "function";
30
+ }
31
+ /**
32
+ * Build the story metadata object that is attached to a screenshot.
33
+ *
34
+ * @param story - The story shape (id, tags, play flag).
35
+ * @param storyMode - The Storybook mode name (e.g. "dark", "mobile") as defined in `parameters.argos.modes`
36
+ */
37
+ function getStoryMetadata(story, storyMode) {
38
+ return {
39
+ id: story.id,
40
+ tags: story.tags ?? [],
41
+ mode: storyMode ?? void 0,
42
+ play: Boolean(story.play)
43
+ };
44
+ }
45
+ //#endregion
46
+ //#region src/utils/parameters.ts
47
+ /**
48
+ * Get the default viewport size from the Storybook parameters.
49
+ */
50
+ function getDefaultViewport(parameters) {
51
+ const defaultViewport = parameters?.viewport?.defaultViewport;
52
+ if (defaultViewport) return getViewport(parameters, defaultViewport);
53
+ return null;
54
+ }
55
+ /**
56
+ * Get the viewport size from the Storybook parameters.
57
+ */
58
+ function getViewport(parameters, viewportName) {
59
+ if (typeof viewportName === "number") return {
60
+ width: viewportName,
61
+ height: 720
62
+ };
63
+ const viewports = parameters?.viewport?.viewports;
64
+ if (viewports && viewportName in viewports) {
65
+ if ("styles" in viewports[viewportName] && viewports[viewportName].styles) {
66
+ const width = parseInt(viewports[viewportName].styles.width, 10);
67
+ const height = parseInt(viewports[viewportName].styles.height, 10);
68
+ if (!isNaN(width) && !isNaN(height)) return {
69
+ width,
70
+ height
71
+ };
72
+ }
73
+ }
74
+ return null;
75
+ }
76
+ /**
77
+ * Get the Argos parameters from the Storybook context.
78
+ */
79
+ function getArgosParameters(parameters) {
80
+ if ("argos" in parameters && parameters.argos && typeof parameters.argos === "object") return parameters.argos;
81
+ if ("chromatic" in parameters && parameters.chromatic && typeof parameters.chromatic === "object") return parameters.chromatic;
82
+ return null;
83
+ }
84
+ const DEFAULT_FIT_TO_CONTENT = {
85
+ padding: 16,
86
+ zoom: 2
87
+ };
88
+ function getFitToContentFromParameters(parameters) {
89
+ const argosParameters = getArgosParameters(parameters);
90
+ if (argosParameters && "fitToContent" in argosParameters) {
91
+ if (typeof argosParameters.fitToContent === "boolean") return argosParameters.fitToContent ? DEFAULT_FIT_TO_CONTENT : null;
92
+ if (typeof argosParameters.fitToContent === "object" && argosParameters.fitToContent) return {
93
+ ...DEFAULT_FIT_TO_CONTENT,
94
+ ...argosParameters.fitToContent
95
+ };
96
+ }
97
+ return DEFAULT_FIT_TO_CONTENT;
98
+ }
99
+ //#endregion
100
+ //#region src/utils/screenshot.ts
101
+ /**
102
+ * Take a screenshot in the context of a Storybook story.
103
+ */
104
+ async function storybookArgosScreenshot(handler, context, options) {
105
+ const argosOptions = {
106
+ ...options,
107
+ stabilize: options?.stabilize ?? {
108
+ waitForAriaBusy: false,
109
+ ...typeof options?.stabilize === "object" ? options.stabilize : {}
110
+ }
111
+ };
112
+ const version = await getArgosStorybookVersion();
113
+ const storyUrl = `http://localhost:6006/?path=/story/${context.story.id}`;
114
+ const metadata = {
115
+ sdk: {
116
+ name: "@snapvisor/storybook",
117
+ version
118
+ },
119
+ playwrightLibraries: context.playwrightLibraries,
120
+ url: storyUrl,
121
+ test: context.test,
122
+ story: getStoryMetadata(context.story)
123
+ };
124
+ const modes = getArgosParameters(context.story.parameters)?.modes;
125
+ const allAttachments = [];
126
+ if (context.mode === "automatic") if (modes) for (const [name, globals] of Object.entries(modes)) {
127
+ if (globals.disabled) continue;
128
+ await handler.evaluate((name) => {
129
+ globalThis.__ARGOS_CURRENT_MODE = name;
130
+ }, name);
131
+ const attachments = await runHooksAndScreenshot({
132
+ handler,
133
+ context,
134
+ metadata,
135
+ options: argosOptions,
136
+ suffix: getModeSuffix(name),
137
+ mode: name,
138
+ globals: {
139
+ ...context.story.globals,
140
+ ...globals
141
+ }
142
+ });
143
+ allAttachments.push(...attachments);
144
+ await context.setViewportSize("initial");
145
+ await handler.evaluate(() => {
146
+ delete globalThis.__ARGOS_CURRENT_MODE;
147
+ });
148
+ }
149
+ else {
150
+ const attachments = await runHooksAndScreenshot({
151
+ handler,
152
+ context,
153
+ metadata,
154
+ options: argosOptions,
155
+ mode: null,
156
+ globals: context.story.globals ?? {}
157
+ });
158
+ await context.setViewportSize("initial");
159
+ allAttachments.push(...attachments);
160
+ }
161
+ else if (context.mode === "manual") {
162
+ const currentMode = await handler.evaluate(() => {
163
+ return globalThis.__ARGOS_CURRENT_MODE || null;
164
+ });
165
+ if (modes && !currentMode) return;
166
+ await markStoryAsRendered(handler, context.story.id);
167
+ DO_NOT_USE_setMetadataConfig({
168
+ ...metadata,
169
+ story: getStoryMetadata(context.story, currentMode)
170
+ });
171
+ await argosScreenshot$1(handler, composeName(context.name, getModeSuffix(currentMode)), options);
172
+ }
173
+ await context.afterScreenshot?.({
174
+ handler,
175
+ globals: {}
176
+ });
177
+ return allAttachments;
178
+ }
179
+ /**
180
+ * Run the story with the given globals.
181
+ */
182
+ async function runStory(args) {
183
+ const { handler, globals, storyId } = args;
184
+ await handler.evaluate(async (globals) => {
185
+ if ("__ARGOS_STORYBOOK_STORY" in globalThis) {
186
+ const storyFn = globalThis.__ARGOS_STORYBOOK_STORY;
187
+ if (!storyFn) throw new Error("@snapvisor/storybook: Unable to find `__ARGOS_STORYBOOK_STORY`.");
188
+ const canvasElement = (() => {
189
+ document.getElementById("storybook-root")?.remove();
190
+ const canvasElement = document.createElement("div");
191
+ canvasElement.id = "storybook-root";
192
+ document.body.appendChild(canvasElement);
193
+ return canvasElement;
194
+ })();
195
+ await storyFn.run({
196
+ globals,
197
+ canvasElement
198
+ });
199
+ return;
200
+ }
201
+ const channel = (() => {
202
+ if ("__STORYBOOK_PREVIEW__" in globalThis) return globalThis.__STORYBOOK_PREVIEW__.channel;
203
+ return null;
204
+ })();
205
+ if (!channel) throw new Error("@snapvisor/storybook: Unable to find `__STORYBOOK_PREVIEW__`.");
206
+ const initialGlobals = channel.last("globalsUpdated")?.[0].initialGlobals;
207
+ channel.emit("updateGlobals", { globals: {
208
+ ...initialGlobals,
209
+ ...globals
210
+ } });
211
+ }, globals);
212
+ await markStoryAsRendered(handler, storyId);
213
+ }
214
+ /**
215
+ * Notify Storybook that the story has been rendered.
216
+ * It will run all the necessary hooks (like pseudo-states).
217
+ */
218
+ async function markStoryAsRendered(handler, storyId) {
219
+ await handler.evaluate((storyId) => {
220
+ if ("__ARGOS_STORYBOOK_STORY" in globalThis) {
221
+ const addons = globalThis.__STORYBOOK_ADDONS_PREVIEW;
222
+ if (!addons) throw new Error("@snapvisor/storybook: Unable to find `__STORYBOOK_ADDONS_PREVIEW`.");
223
+ addons.getChannel().emit("storyRendered", storyId);
224
+ }
225
+ }, storyId);
226
+ }
227
+ /**
228
+ * Wait for the page to be ready and take a screenshot.
229
+ */
230
+ async function runHooksAndScreenshot(args) {
231
+ const { handler, context, options, globals, metadata, mode } = args;
232
+ await runStory({
233
+ handler,
234
+ globals,
235
+ storyId: context.story.id
236
+ });
237
+ const viewport = (globals.viewport ? getViewport(context.story.parameters, globals.viewport) : null) ?? getDefaultViewport(context.story.parameters) ?? "default";
238
+ await context.setViewportSize(viewport);
239
+ DO_NOT_USE_setMetadataConfig({
240
+ ...metadata,
241
+ story: getStoryMetadata(context.story, mode),
242
+ viewport: viewport && viewport !== "default" ? viewport : void 0
243
+ });
244
+ await context.beforeScreenshot?.({
245
+ handler,
246
+ globals
247
+ });
248
+ return argosScreenshot$1(handler, composeName(context.name, args.suffix), options);
249
+ }
250
+ function getModeSuffix(mode) {
251
+ return mode ? ` mode-[${mode}]` : "";
252
+ }
253
+ function composeName(name, suffix) {
254
+ return name + (suffix ?? "");
255
+ }
256
+ //#endregion
257
+ //#region src/test-runner.ts
258
+ const DEFAULT_PLAYWRIGHT_VIEWPORT_SIZE = {
259
+ width: 1280,
260
+ height: 720
261
+ };
262
+ /**
263
+ * Stabilize the UI and takes a screenshot of the application under test.
264
+ *
265
+ * @example argosScreenshot(page, context, options)
266
+ * @see https://argos-ci.com/docs/reference/playwright
267
+ */
268
+ async function argosScreenshot(page, context, options) {
269
+ const storyContext = await getStoryContext(page, context);
270
+ const fitToContent = getFitToContentFromParameters(storyContext.parameters);
271
+ await storybookArgosScreenshot(page, {
272
+ mode: "automatic",
273
+ name: storyContext.id,
274
+ playwrightLibraries: ["@storybook/test-runner"],
275
+ beforeScreenshot: async ({ handler }) => {
276
+ await waitForPageReady(handler);
277
+ },
278
+ story: {
279
+ id: storyContext.id,
280
+ tags: mergeTags(storyContext.tags, storyContext.parameters.tags),
281
+ play: hasPlay(storyContext),
282
+ parameters: storyContext.parameters,
283
+ globals: null
284
+ },
285
+ setViewportSize: async (size) => {
286
+ const actualSize = await page.viewportSize();
287
+ const absoluteSize = size === "default" || size === "initial" ? DEFAULT_PLAYWRIGHT_VIEWPORT_SIZE : size;
288
+ if (!actualSize || actualSize.height !== absoluteSize.height || actualSize.width !== absoluteSize.width) {
289
+ await page.setViewportSize(absoluteSize);
290
+ await page.waitForFunction(({ width, height }) => window.innerWidth === width && window.innerHeight === height, {
291
+ width: absoluteSize.width,
292
+ height: absoluteSize.height
293
+ });
294
+ }
295
+ }
296
+ }, applyFitToContent(options, fitToContent));
297
+ }
298
+ /**
299
+ * Apply fitToContent options to the screenshot options.
300
+ */
301
+ function applyFitToContent(options, fitToContent) {
302
+ if (!fitToContent) return options;
303
+ const { padding, zoom } = fitToContent;
304
+ return {
305
+ ...options,
306
+ element: "#storybook-root",
307
+ argosCSS: `#storybook-root { padding: ${padding}px; width: fit-content; height: fit-content; zoom: ${zoom}; }` + (options?.argosCSS ?? "")
308
+ };
309
+ }
310
+ //#endregion
311
+ export { argosScreenshot };
@@ -0,0 +1,54 @@
1
+ import { ArgosScreenshotOptions as ArgosScreenshotOptions$1, MetadataConfig } from "@snapvisor/playwright";
2
+ import { ArgosReporterConfig } from "@snapvisor/vitest/plugin";
3
+ import { Plugin } from "vitest/config";
4
+ import { BrowserCommand } from "vitest/node";
5
+ import { Frame, Page, ViewportSize } from "playwright";
6
+
7
+ //#region src/utils/parameters.d.ts
8
+ type StorybookGlobals = Record<string, any>;
9
+ //#endregion
10
+ //#region src/utils/screenshot.d.ts
11
+ type StorybookScreenshotContext<Handler extends Page | Frame> = {
12
+ /**
13
+ * - "manual" means the `argosScreenshot` has been called in the story play function.
14
+ * - "automatic" means the screenshot is taken automatically after each test.
15
+ */
16
+ mode: "manual" | "automatic";
17
+ name: string;
18
+ playwrightLibraries: string[];
19
+ test?: MetadataConfig["test"];
20
+ setViewportSize: (size: ViewportSize | "default" | "initial") => Promise<void>;
21
+ beforeScreenshot?: (input: {
22
+ handler: Handler;
23
+ globals: StorybookGlobals;
24
+ }) => Promise<void>;
25
+ afterScreenshot?: (input: {
26
+ handler: Handler;
27
+ globals: StorybookGlobals;
28
+ }) => Promise<void>;
29
+ story: {
30
+ id: string;
31
+ tags?: string[];
32
+ play?: boolean;
33
+ parameters: Record<string, any>;
34
+ globals: StorybookGlobals | null;
35
+ };
36
+ };
37
+ type ArgosScreenshotOptions = Omit<ArgosScreenshotOptions$1, "viewports">;
38
+ //#endregion
39
+ //#region src/vitest-plugin.d.ts
40
+ type ArgosScreenshotCommandArgs = [Pick<StorybookScreenshotContext<Frame>, "name" | "story" | "test" | "mode">];
41
+ interface ArgosVitestPluginOptions extends ArgosReporterConfig, ArgosScreenshotOptions {
42
+ /**
43
+ * Upload the report to Argos.
44
+ * @default true
45
+ */
46
+ uploadToArgos?: boolean;
47
+ }
48
+ /**
49
+ * Create a command for taking Argos screenshots in Vitest.
50
+ */
51
+ declare const createArgosScreenshotCommand: (pluginOptions?: ArgosVitestPluginOptions) => BrowserCommand<ArgosScreenshotCommandArgs>;
52
+ declare function argosVitestPlugin(options?: ArgosVitestPluginOptions): Plugin;
53
+ //#endregion
54
+ export { ArgosScreenshotCommandArgs, type ArgosScreenshotOptions, ArgosVitestPluginOptions, argosVitestPlugin, createArgosScreenshotCommand };
@@ -0,0 +1,312 @@
1
+ import { createRequire } from "node:module";
2
+ import { DO_NOT_USE_setMetadataConfig, argosScreenshot } from "@snapvisor/playwright";
3
+ import { readVersionFromPackage } from "@snapvisor/util";
4
+ import { ArgosReporter } from "@snapvisor/vitest/plugin";
5
+ import { fitIframeToContent, resetTesterScale, setIframeViewportSize } from "@snapvisor/vitest/internal";
6
+ import { dirname, resolve } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ //#region src/utils/metadata.ts
9
+ const require = createRequire(import.meta.url);
10
+ /**
11
+ * Get the version of the Argos Playwright SDK.
12
+ */
13
+ async function getArgosStorybookVersion() {
14
+ return readVersionFromPackage(require.resolve("@snapvisor/storybook/package.json"));
15
+ }
16
+ //#endregion
17
+ //#region src/utils/storyMetadata.ts
18
+ /**
19
+ * Build the story metadata object that is attached to a screenshot.
20
+ *
21
+ * @param story - The story shape (id, tags, play flag).
22
+ * @param storyMode - The Storybook mode name (e.g. "dark", "mobile") as defined in `parameters.argos.modes`
23
+ */
24
+ function getStoryMetadata(story, storyMode) {
25
+ return {
26
+ id: story.id,
27
+ tags: story.tags ?? [],
28
+ mode: storyMode ?? void 0,
29
+ play: Boolean(story.play)
30
+ };
31
+ }
32
+ //#endregion
33
+ //#region src/utils/parameters.ts
34
+ /**
35
+ * Get the default viewport size from the Storybook parameters.
36
+ */
37
+ function getDefaultViewport(parameters) {
38
+ const defaultViewport = parameters?.viewport?.defaultViewport;
39
+ if (defaultViewport) return getViewport(parameters, defaultViewport);
40
+ return null;
41
+ }
42
+ /**
43
+ * Get the viewport size from the Storybook parameters.
44
+ */
45
+ function getViewport(parameters, viewportName) {
46
+ if (typeof viewportName === "number") return {
47
+ width: viewportName,
48
+ height: 720
49
+ };
50
+ const viewports = parameters?.viewport?.viewports;
51
+ if (viewports && viewportName in viewports) {
52
+ if ("styles" in viewports[viewportName] && viewports[viewportName].styles) {
53
+ const width = parseInt(viewports[viewportName].styles.width, 10);
54
+ const height = parseInt(viewports[viewportName].styles.height, 10);
55
+ if (!isNaN(width) && !isNaN(height)) return {
56
+ width,
57
+ height
58
+ };
59
+ }
60
+ }
61
+ return null;
62
+ }
63
+ /**
64
+ * Get the Argos parameters from the Storybook context.
65
+ */
66
+ function getArgosParameters(parameters) {
67
+ if ("argos" in parameters && parameters.argos && typeof parameters.argos === "object") return parameters.argos;
68
+ if ("chromatic" in parameters && parameters.chromatic && typeof parameters.chromatic === "object") return parameters.chromatic;
69
+ return null;
70
+ }
71
+ const DEFAULT_FIT_TO_CONTENT = {
72
+ padding: 16,
73
+ zoom: 2
74
+ };
75
+ function getFitToContentFromParameters(parameters) {
76
+ const argosParameters = getArgosParameters(parameters);
77
+ if (argosParameters && "fitToContent" in argosParameters) {
78
+ if (typeof argosParameters.fitToContent === "boolean") return argosParameters.fitToContent ? DEFAULT_FIT_TO_CONTENT : null;
79
+ if (typeof argosParameters.fitToContent === "object" && argosParameters.fitToContent) return {
80
+ ...DEFAULT_FIT_TO_CONTENT,
81
+ ...argosParameters.fitToContent
82
+ };
83
+ }
84
+ return DEFAULT_FIT_TO_CONTENT;
85
+ }
86
+ //#endregion
87
+ //#region src/utils/screenshot.ts
88
+ /**
89
+ * Take a screenshot in the context of a Storybook story.
90
+ */
91
+ async function storybookArgosScreenshot(handler, context, options) {
92
+ const argosOptions = {
93
+ ...options,
94
+ stabilize: options?.stabilize ?? {
95
+ waitForAriaBusy: false,
96
+ ...typeof options?.stabilize === "object" ? options.stabilize : {}
97
+ }
98
+ };
99
+ const version = await getArgosStorybookVersion();
100
+ const storyUrl = `http://localhost:6006/?path=/story/${context.story.id}`;
101
+ const metadata = {
102
+ sdk: {
103
+ name: "@snapvisor/storybook",
104
+ version
105
+ },
106
+ playwrightLibraries: context.playwrightLibraries,
107
+ url: storyUrl,
108
+ test: context.test,
109
+ story: getStoryMetadata(context.story)
110
+ };
111
+ const modes = getArgosParameters(context.story.parameters)?.modes;
112
+ const allAttachments = [];
113
+ if (context.mode === "automatic") if (modes) for (const [name, globals] of Object.entries(modes)) {
114
+ if (globals.disabled) continue;
115
+ await handler.evaluate((name) => {
116
+ globalThis.__ARGOS_CURRENT_MODE = name;
117
+ }, name);
118
+ const attachments = await runHooksAndScreenshot({
119
+ handler,
120
+ context,
121
+ metadata,
122
+ options: argosOptions,
123
+ suffix: getModeSuffix(name),
124
+ mode: name,
125
+ globals: {
126
+ ...context.story.globals,
127
+ ...globals
128
+ }
129
+ });
130
+ allAttachments.push(...attachments);
131
+ await context.setViewportSize("initial");
132
+ await handler.evaluate(() => {
133
+ delete globalThis.__ARGOS_CURRENT_MODE;
134
+ });
135
+ }
136
+ else {
137
+ const attachments = await runHooksAndScreenshot({
138
+ handler,
139
+ context,
140
+ metadata,
141
+ options: argosOptions,
142
+ mode: null,
143
+ globals: context.story.globals ?? {}
144
+ });
145
+ await context.setViewportSize("initial");
146
+ allAttachments.push(...attachments);
147
+ }
148
+ else if (context.mode === "manual") {
149
+ const currentMode = await handler.evaluate(() => {
150
+ return globalThis.__ARGOS_CURRENT_MODE || null;
151
+ });
152
+ if (modes && !currentMode) return;
153
+ await markStoryAsRendered(handler, context.story.id);
154
+ DO_NOT_USE_setMetadataConfig({
155
+ ...metadata,
156
+ story: getStoryMetadata(context.story, currentMode)
157
+ });
158
+ await argosScreenshot(handler, composeName(context.name, getModeSuffix(currentMode)), options);
159
+ }
160
+ await context.afterScreenshot?.({
161
+ handler,
162
+ globals: {}
163
+ });
164
+ return allAttachments;
165
+ }
166
+ /**
167
+ * Run the story with the given globals.
168
+ */
169
+ async function runStory(args) {
170
+ const { handler, globals, storyId } = args;
171
+ await handler.evaluate(async (globals) => {
172
+ if ("__ARGOS_STORYBOOK_STORY" in globalThis) {
173
+ const storyFn = globalThis.__ARGOS_STORYBOOK_STORY;
174
+ if (!storyFn) throw new Error("@snapvisor/storybook: Unable to find `__ARGOS_STORYBOOK_STORY`.");
175
+ const canvasElement = (() => {
176
+ document.getElementById("storybook-root")?.remove();
177
+ const canvasElement = document.createElement("div");
178
+ canvasElement.id = "storybook-root";
179
+ document.body.appendChild(canvasElement);
180
+ return canvasElement;
181
+ })();
182
+ await storyFn.run({
183
+ globals,
184
+ canvasElement
185
+ });
186
+ return;
187
+ }
188
+ const channel = (() => {
189
+ if ("__STORYBOOK_PREVIEW__" in globalThis) return globalThis.__STORYBOOK_PREVIEW__.channel;
190
+ return null;
191
+ })();
192
+ if (!channel) throw new Error("@snapvisor/storybook: Unable to find `__STORYBOOK_PREVIEW__`.");
193
+ const initialGlobals = channel.last("globalsUpdated")?.[0].initialGlobals;
194
+ channel.emit("updateGlobals", { globals: {
195
+ ...initialGlobals,
196
+ ...globals
197
+ } });
198
+ }, globals);
199
+ await markStoryAsRendered(handler, storyId);
200
+ }
201
+ /**
202
+ * Notify Storybook that the story has been rendered.
203
+ * It will run all the necessary hooks (like pseudo-states).
204
+ */
205
+ async function markStoryAsRendered(handler, storyId) {
206
+ await handler.evaluate((storyId) => {
207
+ if ("__ARGOS_STORYBOOK_STORY" in globalThis) {
208
+ const addons = globalThis.__STORYBOOK_ADDONS_PREVIEW;
209
+ if (!addons) throw new Error("@snapvisor/storybook: Unable to find `__STORYBOOK_ADDONS_PREVIEW`.");
210
+ addons.getChannel().emit("storyRendered", storyId);
211
+ }
212
+ }, storyId);
213
+ }
214
+ /**
215
+ * Wait for the page to be ready and take a screenshot.
216
+ */
217
+ async function runHooksAndScreenshot(args) {
218
+ const { handler, context, options, globals, metadata, mode } = args;
219
+ await runStory({
220
+ handler,
221
+ globals,
222
+ storyId: context.story.id
223
+ });
224
+ const viewport = (globals.viewport ? getViewport(context.story.parameters, globals.viewport) : null) ?? getDefaultViewport(context.story.parameters) ?? "default";
225
+ await context.setViewportSize(viewport);
226
+ DO_NOT_USE_setMetadataConfig({
227
+ ...metadata,
228
+ story: getStoryMetadata(context.story, mode),
229
+ viewport: viewport && viewport !== "default" ? viewport : void 0
230
+ });
231
+ await context.beforeScreenshot?.({
232
+ handler,
233
+ globals
234
+ });
235
+ return argosScreenshot(handler, composeName(context.name, args.suffix), options);
236
+ }
237
+ function getModeSuffix(mode) {
238
+ return mode ? ` mode-[${mode}]` : "";
239
+ }
240
+ function composeName(name, suffix) {
241
+ return name + (suffix ?? "");
242
+ }
243
+ //#endregion
244
+ //#region src/vitest-plugin.ts
245
+ /**
246
+ * Create a command for taking Argos screenshots in Vitest.
247
+ */
248
+ const createArgosScreenshotCommand = (pluginOptions) => {
249
+ const screenshotOptions = pluginOptions ?? {};
250
+ return async (ctx, testContext) => {
251
+ const frame = await ctx.frame();
252
+ const fitToContent = getFitToContentFromParameters(testContext.story.parameters);
253
+ const after = await resetTesterScale(ctx);
254
+ const options = applyFitToContent(screenshotOptions, fitToContent);
255
+ const userBeforeScreenshot = options?.beforeScreenshot;
256
+ const optionsWithFit = {
257
+ ...options,
258
+ beforeScreenshot: async (api) => {
259
+ await userBeforeScreenshot?.(api);
260
+ await fitIframeToContent(ctx, { fitWidth: Boolean(fitToContent) });
261
+ }
262
+ };
263
+ const attachments = await storybookArgosScreenshot(frame, {
264
+ ...testContext,
265
+ playwrightLibraries: ["@storybook/addon-vitest"],
266
+ setViewportSize: async (size) => {
267
+ await setIframeViewportSize(ctx, size, { fullPage: screenshotOptions.fullPage ?? !fitToContent });
268
+ }
269
+ }, optionsWithFit);
270
+ await after();
271
+ return attachments;
272
+ };
273
+ };
274
+ /**
275
+ * Apply fitToContent options to the screenshot options.
276
+ */
277
+ function applyFitToContent(options, fitToContent) {
278
+ if (!fitToContent) return options;
279
+ const { padding, zoom } = fitToContent;
280
+ return {
281
+ ...options,
282
+ element: "body",
283
+ argosCSS: `body { padding: ${padding}px; width: fit-content; height: fit-content; min-height: initial; zoom: ${zoom}; }` + (options?.argosCSS ?? "")
284
+ };
285
+ }
286
+ const cwd = process.cwd();
287
+ function argosVitestPlugin(options) {
288
+ const { root: unresolvedRoot = "./screenshots", uploadToArgos, ...otherOptions } = options ?? {};
289
+ const root = resolve(cwd, unresolvedRoot);
290
+ const setupFile = resolve(dirname(fileURLToPath(import.meta.url)), "./vitest-setup-file.mjs");
291
+ return {
292
+ name: "@snapvisor/storybook/vitest-plugin",
293
+ configureVitest({ vitest, project }) {
294
+ project.config.setupFiles.push(setupFile);
295
+ if (uploadToArgos) vitest.config.reporters.push(new ArgosReporter({
296
+ ...otherOptions,
297
+ root
298
+ }));
299
+ },
300
+ config() {
301
+ return {
302
+ optimizeDeps: { include: ["@snapvisor/storybook/internal/vitest-setup-file"] },
303
+ test: { browser: { commands: { argosStorybookScreenshot: createArgosScreenshotCommand({
304
+ ...otherOptions,
305
+ root
306
+ }) } } }
307
+ };
308
+ }
309
+ };
310
+ }
311
+ //#endregion
312
+ export { argosVitestPlugin, createArgosScreenshotCommand };
@@ -0,0 +1,57 @@
1
+ import { afterEach } from "vitest";
2
+ //#region src/utils/storyMetadata.ts
3
+ function toArray(v) {
4
+ return Array.isArray(v) ? v : v ? [v] : [];
5
+ }
6
+ function mergeTags(...tags) {
7
+ const merged = tags.flatMap((tags) => toArray(tags));
8
+ return Array.from(new Set(merged)).filter((tag) => typeof tag === "string" && tag !== "");
9
+ }
10
+ /**
11
+ * Return whether a story context has a play function.
12
+ * Handles both the `play` field (plain story function) and the legacy
13
+ * `playFunction` field present in older Storybook test-runner contexts.
14
+ */
15
+ function hasPlay(storyContext) {
16
+ if (!storyContext || typeof storyContext !== "object") return false;
17
+ return "play" in storyContext && typeof storyContext.play === "function" || "playFunction" in storyContext && typeof storyContext.playFunction === "function";
18
+ }
19
+ //#endregion
20
+ //#region src/vitest.ts
21
+ /**
22
+ * Setup Argos hooks for Vitest.
23
+ */
24
+ function setupArgos(api) {
25
+ api.afterEach(async (ctx) => {
26
+ const story = "story" in ctx ? ctx.story : null;
27
+ if (!story) throw new Error(`@snapvisor/storybook/vitest-plugin should be used with @storybook/addon-vitest/vitest-plugin`);
28
+ const { server } = await import("vitest/browser");
29
+ globalThis.__ARGOS_STORYBOOK_STORY = story;
30
+ await server.commands.argosStorybookScreenshot({
31
+ mode: "automatic",
32
+ name: story.id,
33
+ story: {
34
+ id: story.id,
35
+ tags: mergeTags(story.tags, story.parameters.tags),
36
+ play: hasPlay(story),
37
+ parameters: story.parameters,
38
+ globals: story.globals
39
+ },
40
+ test: {
41
+ id: ctx.task.id,
42
+ title: ctx.task.name,
43
+ titlePath: [ctx.task.file.name, ctx.task.name],
44
+ location: {
45
+ line: ctx.task.location?.line ?? 1,
46
+ column: ctx.task.location?.column ?? 1,
47
+ file: ctx.task.file.filepath
48
+ }
49
+ }
50
+ });
51
+ });
52
+ }
53
+ //#endregion
54
+ //#region src/vitest-setup-file.ts
55
+ setupArgos({ afterEach });
56
+ //#endregion
57
+ export {};
@@ -0,0 +1,62 @@
1
+ import * as vitest from "vitest";
2
+ import { ArgosAttachment, ArgosScreenshotOptions as ArgosScreenshotOptions$1, MetadataConfig } from "@snapvisor/playwright";
3
+ import { Frame, Page, ViewportSize } from "playwright";
4
+ //#region src/utils/parameters.d.ts
5
+ type StorybookGlobals = Record<string, any>;
6
+ //#endregion
7
+ //#region src/utils/screenshot.d.ts
8
+ type StorybookScreenshotContext<Handler extends Page | Frame> = {
9
+ /**
10
+ * - "manual" means the `argosScreenshot` has been called in the story play function.
11
+ * - "automatic" means the screenshot is taken automatically after each test.
12
+ */
13
+ mode: "manual" | "automatic";
14
+ name: string;
15
+ playwrightLibraries: string[];
16
+ test?: MetadataConfig["test"];
17
+ setViewportSize: (size: ViewportSize | "default" | "initial") => Promise<void>;
18
+ beforeScreenshot?: (input: {
19
+ handler: Handler;
20
+ globals: StorybookGlobals;
21
+ }) => Promise<void>;
22
+ afterScreenshot?: (input: {
23
+ handler: Handler;
24
+ globals: StorybookGlobals;
25
+ }) => Promise<void>;
26
+ story: {
27
+ id: string;
28
+ tags?: string[];
29
+ play?: boolean;
30
+ parameters: Record<string, any>;
31
+ globals: StorybookGlobals | null;
32
+ };
33
+ };
34
+ type ArgosScreenshotOptions = Omit<ArgosScreenshotOptions$1, "viewports">;
35
+ //#endregion
36
+ //#region src/vitest-plugin.d.ts
37
+ type ArgosScreenshotCommandArgs = [Pick<StorybookScreenshotContext<Frame>, "name" | "story" | "test" | "mode">];
38
+ //#endregion
39
+ //#region src/vitest.d.ts
40
+ declare module "vitest/browser" {
41
+ interface BrowserCommands {
42
+ argosStorybookScreenshot: (...args: ArgosScreenshotCommandArgs) => Promise<ArgosAttachment[]>;
43
+ }
44
+ }
45
+ /**
46
+ * Setup Argos hooks for Vitest.
47
+ */
48
+ declare function setupArgos(api: {
49
+ afterEach: typeof vitest.afterEach;
50
+ }): void;
51
+ /**
52
+ * Take an Argos screenshot of a story in Vitest.
53
+ */
54
+ declare function argosScreenshot(story: {
55
+ parameters: Record<string, any>;
56
+ globals: StorybookGlobals | null;
57
+ id: string;
58
+ tags?: string[];
59
+ play?: unknown;
60
+ }, name: string): Promise<void>;
61
+ //#endregion
62
+ export { type ArgosScreenshotOptions, argosScreenshot, setupArgos };
@@ -0,0 +1,82 @@
1
+ //#region src/utils/storyMetadata.ts
2
+ function toArray(v) {
3
+ return Array.isArray(v) ? v : v ? [v] : [];
4
+ }
5
+ function mergeTags(...tags) {
6
+ const merged = tags.flatMap((tags) => toArray(tags));
7
+ return Array.from(new Set(merged)).filter((tag) => typeof tag === "string" && tag !== "");
8
+ }
9
+ /**
10
+ * Return whether a story context has a play function.
11
+ * Handles both the `play` field (plain story function) and the legacy
12
+ * `playFunction` field present in older Storybook test-runner contexts.
13
+ */
14
+ function hasPlay(storyContext) {
15
+ if (!storyContext || typeof storyContext !== "object") return false;
16
+ return "play" in storyContext && typeof storyContext.play === "function" || "playFunction" in storyContext && typeof storyContext.playFunction === "function";
17
+ }
18
+ //#endregion
19
+ //#region src/vitest.ts
20
+ /**
21
+ * Setup Argos hooks for Vitest.
22
+ */
23
+ function setupArgos(api) {
24
+ api.afterEach(async (ctx) => {
25
+ const story = "story" in ctx ? ctx.story : null;
26
+ if (!story) throw new Error(`@snapvisor/storybook/vitest-plugin should be used with @storybook/addon-vitest/vitest-plugin`);
27
+ const { server } = await import("vitest/browser");
28
+ globalThis.__ARGOS_STORYBOOK_STORY = story;
29
+ await server.commands.argosStorybookScreenshot({
30
+ mode: "automatic",
31
+ name: story.id,
32
+ story: {
33
+ id: story.id,
34
+ tags: mergeTags(story.tags, story.parameters.tags),
35
+ play: hasPlay(story),
36
+ parameters: story.parameters,
37
+ globals: story.globals
38
+ },
39
+ test: {
40
+ id: ctx.task.id,
41
+ title: ctx.task.name,
42
+ titlePath: [ctx.task.file.name, ctx.task.name],
43
+ location: {
44
+ line: ctx.task.location?.line ?? 1,
45
+ column: ctx.task.location?.column ?? 1,
46
+ file: ctx.task.file.filepath
47
+ }
48
+ }
49
+ });
50
+ });
51
+ }
52
+ /**
53
+ * Take an Argos screenshot of a story in Vitest.
54
+ */
55
+ async function argosScreenshot(story, name) {
56
+ if (!await checkIsVitestEnv()) return;
57
+ const { server } = await import("vitest/browser");
58
+ await server.commands.argosStorybookScreenshot({
59
+ mode: "manual",
60
+ name: `${story.id}/${name}`,
61
+ story: {
62
+ id: story.id,
63
+ tags: mergeTags(story.tags, story.parameters.tags),
64
+ play: hasPlay(story),
65
+ parameters: story.parameters,
66
+ globals: story.globals
67
+ }
68
+ });
69
+ }
70
+ /**
71
+ * Check if we are running in a Vitest environment.
72
+ */
73
+ async function checkIsVitestEnv() {
74
+ try {
75
+ await import("vitest");
76
+ return true;
77
+ } catch {
78
+ return false;
79
+ }
80
+ }
81
+ //#endregion
82
+ export { argosScreenshot, setupArgos };
package/package.json ADDED
@@ -0,0 +1,104 @@
1
+ {
2
+ "name": "@snapvisor/storybook",
3
+ "description": "Visual testing for Storybook test runner.",
4
+ "version": "6.1.8",
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/storybook"
11
+ },
12
+ "homepage": "https://argos-ci.com/docs/reference/storybook",
13
+ "keywords": [
14
+ "storybook",
15
+ "storybook-addon",
16
+ "screenshot",
17
+ "capture",
18
+ "testing",
19
+ "visual testing",
20
+ "argos",
21
+ "regression",
22
+ "visual regression"
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "type": "module",
28
+ "exports": {
29
+ "./test-runner": {
30
+ "types": "./dist/test-runner.d.mts",
31
+ "import": "./dist/test-runner.mjs",
32
+ "require": "./dist/test-runner.cjs",
33
+ "default": "./dist/test-runner.cjs"
34
+ },
35
+ "./vitest": {
36
+ "types": "./dist/vitest.d.mts",
37
+ "import": "./dist/vitest.mjs",
38
+ "default": "./dist/vitest.mjs"
39
+ },
40
+ "./vitest-plugin": {
41
+ "types": "./dist/vitest-plugin.d.mts",
42
+ "import": "./dist/vitest-plugin.mjs",
43
+ "default": "./dist/vitest-plugin.mjs"
44
+ },
45
+ "./internal/vitest-setup-file": "./dist/vitest-setup-file.mjs",
46
+ "./package.json": "./package.json"
47
+ },
48
+ "engines": {
49
+ "node": ">=22.0.0"
50
+ },
51
+ "dependencies": {
52
+ "@snapvisor/core": "6.6.2",
53
+ "@snapvisor/playwright": "7.3.7",
54
+ "@snapvisor/util": "4.0.4",
55
+ "@snapvisor/vitest": "0.3.2"
56
+ },
57
+ "devDependencies": {
58
+ "@snapvisor/cli": "6.4.1",
59
+ "@snapvisor/util": "workspace:*",
60
+ "@storybook/addon-docs": "^10.4.6",
61
+ "@storybook/addon-links": "^10.4.6",
62
+ "@storybook/addon-themes": "^10.4.6",
63
+ "@storybook/addon-vitest": "^10.4.6",
64
+ "@storybook/react-vite": "^10.4.6",
65
+ "@storybook/test-runner": "^0.24.4",
66
+ "@types/node": "catalog:",
67
+ "@types/react": "^19.2.17",
68
+ "@types/react-dom": "^19.2.3",
69
+ "@vitest/browser": "^4.1.9",
70
+ "@vitest/browser-playwright": "^4.1.9",
71
+ "@vitest/coverage-v8": "^4.1.9",
72
+ "@vitest/ui": "^4.1.9",
73
+ "http-server": "^14.1.1",
74
+ "playwright": "^1.61.0",
75
+ "prop-types": "^15.8.1",
76
+ "react": "^19.2.7",
77
+ "react-dom": "^19.2.7",
78
+ "storybook": "^10.4.6",
79
+ "storybook-addon-pseudo-states": "^10.4.6",
80
+ "vitest": "catalog:",
81
+ "wait-on": "^9.0.10"
82
+ },
83
+ "scripts": {
84
+ "build": "tsdown && cp ./src/test-runner.cjs ./dist",
85
+ "storybook": "storybook dev -p 6006",
86
+ "build-storybook": "storybook build",
87
+ "test-storybook": "cross-env NODE_NO_WARNINGS=1 NODE_OPTIONS=--experimental-vm-modules test-storybook",
88
+ "install-playwright": "playwright install chromium --with-deps",
89
+ "argos-upload-runner": "argos upload screenshots --build-name \"argos-storybook-test-runner-e2e-node-$NODE_VERSION-$OS\"",
90
+ "serve-storybook": "http-server storybook-static --port 6006 --silent",
91
+ "wait-storybook": "wait-on tcp:127.0.0.1:6006",
92
+ "build-e2e-runner": "pnpm run build-storybook --quiet && pnpm run install-playwright",
93
+ "build-e2e": "pnpm run build-e2e-runner",
94
+ "test-e2e-runner": "pnpm run test-storybook && pnpm run argos-upload-runner",
95
+ "test-e2e-vitest": "vitest run",
96
+ "e2e-runner": "pnpm dlx concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"pnpm run serve-storybook\" \"pnpm run wait-storybook && pnpm run test-e2e-runner\"",
97
+ "e2e-vitest": "cross-env BUILD_NAME=\"argos-storybook-vitest-e2e-node-$NODE_VERSION-$OS\" UPLOAD_TO_ARGOS=true pnpm run test-e2e-vitest",
98
+ "e2e": "pnpm run e2e-runner && rm -rf ./screenshots && pnpm run e2e-vitest",
99
+ "check-types": "tsc",
100
+ "check-format": "prettier --check --ignore-unknown --ignore-path=./.gitignore --ignore-path=../../.gitignore --ignore-path=../../.prettierignore .",
101
+ "lint": "eslint ."
102
+ },
103
+ "gitHead": "7c74326b53f6f513ac4087385b88e8fbd7e92eac"
104
+ }