From 2d2943b7822187de506ae30880e72ddb6b746a7b Mon Sep 17 00:00:00 2001 From: InfiniteXyy Date: Wed, 18 Sep 2024 15:03:28 +0800 Subject: [PATCH 1/6] feat[vitest-plugin]: support read story name for vitest test description --- .../vitest-plugin/transformer.test.ts | 36 +++++++++++++++++++ .../csf-tools/vitest-plugin/transformer.ts | 26 +++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/code/core/src/csf-tools/vitest-plugin/transformer.test.ts b/code/core/src/csf-tools/vitest-plugin/transformer.test.ts index 72a1d05ab403..4c974a08ca36 100644 --- a/code/core/src/csf-tools/vitest-plugin/transformer.test.ts +++ b/code/core/src/csf-tools/vitest-plugin/transformer.test.ts @@ -213,6 +213,42 @@ describe('transformer', () => { `); }); + it("should use the story's explicitly settled name if it's present", async () => { + const code = ` + export default { + component: Button, + } + export const Primary = { + name: "basic Primary Button scenario", + args: { + label: 'Primary Button', + }, + }; + `; + + const result = await transform({ code }); + + expect(result.code).toMatchInlineSnapshot(` + import { test as _test, expect as _expect } from "vitest"; + import { testStory as _testStory } from "@storybook/experimental-addon-test/internal/test-utils"; + const _meta = { + component: Button, + title: "automatic/calculated/title" + }; + export default _meta; + export const Primary = { + name: "basic Primary Button scenario", + args: { + label: 'Primary Button' + } + }; + const _isRunningFromThisFile = import.meta.url.includes(globalThis.__vitest_worker__.filepath ?? _expect.getState().testPath); + if (_isRunningFromThisFile) { + _test("basic Primary Button scenario", _testStory("Primary", Primary, _meta, [])); + } + `); + }); + it('should add test statement to const declared exported stories', async () => { const code = ` export default {}; diff --git a/code/core/src/csf-tools/vitest-plugin/transformer.ts b/code/core/src/csf-tools/vitest-plugin/transformer.ts index 51ea1169f6c3..ed1e768bfc91 100644 --- a/code/core/src/csf-tools/vitest-plugin/transformer.ts +++ b/code/core/src/csf-tools/vitest-plugin/transformer.ts @@ -207,10 +207,34 @@ export async function vitestTransform({ exportName: string; node: t.Node; }): t.ExpressionStatement => { + // Get test name from the story's name property, and fallback to exportName + const testName = (function getTestName(): string { + if ( + node.type === 'ExportNamedDeclaration' && + node.declaration?.type === 'VariableDeclaration' + ) { + const storyDeclarator = node.declaration.declarations[0]; + if (storyDeclarator.init?.type === 'ObjectExpression') { + for (const prop of storyDeclarator.init.properties) { + if ( + // Filter out the "name" property and return its value + prop.type === 'ObjectProperty' && + prop.key.type === 'Identifier' && + prop.value.type === 'StringLiteral' && + prop.key.name === 'name' + ) { + return prop.value.value; + } + } + } + } + return exportName; + })(); + // Create the _test expression directly using the exportName identifier const testStoryCall = t.expressionStatement( t.callExpression(vitestTestId, [ - t.stringLiteral(exportName), + t.stringLiteral(testName), t.callExpression(testStoryId, [ t.stringLiteral(exportName), t.identifier(exportName), From 5ac38c3bef606d8db7b64b90497478e53d6e9043 Mon Sep 17 00:00:00 2001 From: InfiniteXyy Date: Wed, 18 Sep 2024 15:20:04 +0800 Subject: [PATCH 2/6] chore: update vitest document --- docs/writing-tests/vitest-plugin.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/writing-tests/vitest-plugin.mdx b/docs/writing-tests/vitest-plugin.mdx index 09940ef230d9..b0a5fae00377 100644 --- a/docs/writing-tests/vitest-plugin.mdx +++ b/docs/writing-tests/vitest-plugin.mdx @@ -379,6 +379,10 @@ We recommend running tests in a browser using Playwright, but you can use WebDri We recommend using Chromium, because it is most likely to best match the experience of a majority of your users. However, you can use other browsers by adjusting the [browser name in the Vitest configuration file](https://vitest.dev/config/#browser-name). Note that [Playwright and WebDriverIO support different browsers](https://vitest.dev/guide/browser/#browser-option-types). +### How do I customized the test description + +We are using the exportName of a story definition by default, but you can provide a `name` property for the story to customize the test description. This is useful when you want have places, brackets or other special characters in the test description. + ## API ### Exports From a70830b17f912da3690a891bb64a1f7bcad3014e Mon Sep 17 00:00:00 2001 From: InfiniteXyy Date: Wed, 18 Sep 2024 15:27:57 +0800 Subject: [PATCH 3/6] chore: remove iife --- .../csf-tools/vitest-plugin/transformer.ts | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/code/core/src/csf-tools/vitest-plugin/transformer.ts b/code/core/src/csf-tools/vitest-plugin/transformer.ts index ed1e768bfc91..01cf6a1bf804 100644 --- a/code/core/src/csf-tools/vitest-plugin/transformer.ts +++ b/code/core/src/csf-tools/vitest-plugin/transformer.ts @@ -208,28 +208,27 @@ export async function vitestTransform({ node: t.Node; }): t.ExpressionStatement => { // Get test name from the story's name property, and fallback to exportName - const testName = (function getTestName(): string { - if ( - node.type === 'ExportNamedDeclaration' && - node.declaration?.type === 'VariableDeclaration' - ) { - const storyDeclarator = node.declaration.declarations[0]; - if (storyDeclarator.init?.type === 'ObjectExpression') { - for (const prop of storyDeclarator.init.properties) { - if ( - // Filter out the "name" property and return its value - prop.type === 'ObjectProperty' && - prop.key.type === 'Identifier' && - prop.value.type === 'StringLiteral' && - prop.key.name === 'name' - ) { - return prop.value.value; - } + let testName = exportName; + if ( + node.type === 'ExportNamedDeclaration' && + node.declaration?.type === 'VariableDeclaration' + ) { + const storyDeclarator = node.declaration.declarations[0]; + if (storyDeclarator.init?.type === 'ObjectExpression') { + // Find the "name" property and set its value to testName + for (const prop of storyDeclarator.init.properties) { + if ( + prop.type === 'ObjectProperty' && + prop.key.type === 'Identifier' && + prop.value.type === 'StringLiteral' && + prop.key.name === 'name' + ) { + testName = prop.value.value; + break; } } } - return exportName; - })(); + } // Create the _test expression directly using the exportName identifier const testStoryCall = t.expressionStatement( From 188271777e9edc0076f7399eddaecdd5487cb722 Mon Sep 17 00:00:00 2001 From: InfiniteXyy Date: Sat, 21 Sep 2024 17:17:25 +0800 Subject: [PATCH 4/6] chore: refactor with parsedStory --- .../vitest-plugin/transformer.test.ts | 98 +++++++++++++------ .../csf-tools/vitest-plugin/transformer.ts | 34 ++----- docs/writing-tests/vitest-plugin.mdx | 7 +- 3 files changed, 79 insertions(+), 60 deletions(-) diff --git a/code/core/src/csf-tools/vitest-plugin/transformer.test.ts b/code/core/src/csf-tools/vitest-plugin/transformer.test.ts index 4c974a08ca36..fdbfd2bb5b43 100644 --- a/code/core/src/csf-tools/vitest-plugin/transformer.test.ts +++ b/code/core/src/csf-tools/vitest-plugin/transformer.test.ts @@ -213,40 +213,76 @@ describe('transformer', () => { `); }); - it("should use the story's explicitly settled name if it's present", async () => { - const code = ` - export default { - component: Button, - } - export const Primary = { - name: "basic Primary Button scenario", - args: { - label: 'Primary Button', - }, - }; - `; + describe("use the story's name as test title", () => { + it('should support csf v3 with object config', async () => { + const code = ` + export default { component: Button } + export const Primary = { name: "custom name" };`; + const result = await transform({ code }); + + expect(result.code).toMatchInlineSnapshot(` + import { test as _test, expect as _expect } from "vitest"; + import { testStory as _testStory } from "@storybook/experimental-addon-test/internal/test-utils"; + const _meta = { + component: Button, + title: "automatic/calculated/title" + }; + export default _meta; + export const Primary = { + name: "custom name" + }; + const _isRunningFromThisFile = import.meta.url.includes(globalThis.__vitest_worker__.filepath ?? _expect.getState().testPath); + if (_isRunningFromThisFile) { + _test("custom name", _testStory("Primary", Primary, _meta, [])); + } + `); + }); - const result = await transform({ code }); + it('should support csf v3 with function config', async () => { + const code = ` + export default { component: Button }; + export const Story = () => { name: 'custom name' };`; + const result = await transform({ code }); + expect(result.code).toMatchInlineSnapshot(` + import { test as _test, expect as _expect } from "vitest"; + import { testStory as _testStory } from "@storybook/experimental-addon-test/internal/test-utils"; + const _meta = { + component: Button, + title: "automatic/calculated/title" + }; + export default _meta; + export const Story = () => { + name: 'custom name'; + }; + const _isRunningFromThisFile = import.meta.url.includes(globalThis.__vitest_worker__.filepath ?? _expect.getState().testPath); + if (_isRunningFromThisFile) { + _test("Story", _testStory("Story", Story, _meta, [])); + } + `); + }); - expect(result.code).toMatchInlineSnapshot(` - import { test as _test, expect as _expect } from "vitest"; - import { testStory as _testStory } from "@storybook/experimental-addon-test/internal/test-utils"; - const _meta = { - component: Button, - title: "automatic/calculated/title" - }; - export default _meta; - export const Primary = { - name: "basic Primary Button scenario", - args: { - label: 'Primary Button' + it('should support csf v1/v2', async () => { + const code = ` + export default { component: Button } + export const Story = () => {} + Story.storyName = 'custom name';`; + const result = await transform({ code: code }); + expect(result.code).toMatchInlineSnapshot(` + import { test as _test, expect as _expect } from "vitest"; + import { testStory as _testStory } from "@storybook/experimental-addon-test/internal/test-utils"; + const _meta = { + component: Button, + title: "automatic/calculated/title" + }; + export default _meta; + export const Story = () => {}; + Story.storyName = 'custom name'; + const _isRunningFromThisFile = import.meta.url.includes(globalThis.__vitest_worker__.filepath ?? _expect.getState().testPath); + if (_isRunningFromThisFile) { + _test("custom name", _testStory("Story", Story, _meta, [])); } - }; - const _isRunningFromThisFile = import.meta.url.includes(globalThis.__vitest_worker__.filepath ?? _expect.getState().testPath); - if (_isRunningFromThisFile) { - _test("basic Primary Button scenario", _testStory("Primary", Primary, _meta, [])); - } - `); + `); + }); }); it('should add test statement to const declared exported stories', async () => { diff --git a/code/core/src/csf-tools/vitest-plugin/transformer.ts b/code/core/src/csf-tools/vitest-plugin/transformer.ts index 01cf6a1bf804..778ea752f1d0 100644 --- a/code/core/src/csf-tools/vitest-plugin/transformer.ts +++ b/code/core/src/csf-tools/vitest-plugin/transformer.ts @@ -202,38 +202,17 @@ export async function vitestTransform({ const getTestStatementForStory = ({ exportName, + testTitle, node, }: { exportName: string; + testTitle: string; node: t.Node; }): t.ExpressionStatement => { - // Get test name from the story's name property, and fallback to exportName - let testName = exportName; - if ( - node.type === 'ExportNamedDeclaration' && - node.declaration?.type === 'VariableDeclaration' - ) { - const storyDeclarator = node.declaration.declarations[0]; - if (storyDeclarator.init?.type === 'ObjectExpression') { - // Find the "name" property and set its value to testName - for (const prop of storyDeclarator.init.properties) { - if ( - prop.type === 'ObjectProperty' && - prop.key.type === 'Identifier' && - prop.value.type === 'StringLiteral' && - prop.key.name === 'name' - ) { - testName = prop.value.value; - break; - } - } - } - } - // Create the _test expression directly using the exportName identifier const testStoryCall = t.expressionStatement( t.callExpression(vitestTestId, [ - t.stringLiteral(testName), + t.stringLiteral(testTitle), t.callExpression(testStoryId, [ t.stringLiteral(exportName), t.identifier(exportName), @@ -262,10 +241,9 @@ export async function vitestTransform({ return; } - return getTestStatementForStory({ - exportName, - node, - }); + // use the story's name as the test title for vitest, and fallback to exportName + const testTitle = parsed._stories[exportName].name ?? exportName; + return getTestStatementForStory({ testTitle, exportName, node }); }) .filter((st) => !!st) as t.ExpressionStatement[]; diff --git a/docs/writing-tests/vitest-plugin.mdx b/docs/writing-tests/vitest-plugin.mdx index b0a5fae00377..2bf22f19f080 100644 --- a/docs/writing-tests/vitest-plugin.mdx +++ b/docs/writing-tests/vitest-plugin.mdx @@ -381,7 +381,12 @@ We recommend using Chromium, because it is most likely to best match the experie ### How do I customized the test description -We are using the exportName of a story definition by default, but you can provide a `name` property for the story to customize the test description. This is useful when you want have places, brackets or other special characters in the test description. +By default, the export name of a story is mapped to the test name. You can provide a `name` property for the story to customize the test description, allowing you to write more descriptive names. This is useful when you want use spaces, brackets or other special characters in the test description. +```js +export const Story = { + name: 'custom, descriptive name' +}; +``` ## API From d2a98f190d51f2b249fe74187e3018a0b33c502d Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Mon, 23 Sep 2024 11:42:39 +0200 Subject: [PATCH 5/6] update tests --- .../vitest-plugin/transformer.test.ts | 27 ++----------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/code/core/src/csf-tools/vitest-plugin/transformer.test.ts b/code/core/src/csf-tools/vitest-plugin/transformer.test.ts index fdbfd2bb5b43..84d9ac273718 100644 --- a/code/core/src/csf-tools/vitest-plugin/transformer.test.ts +++ b/code/core/src/csf-tools/vitest-plugin/transformer.test.ts @@ -214,7 +214,7 @@ describe('transformer', () => { }); describe("use the story's name as test title", () => { - it('should support csf v3 with object config', async () => { + it('should support CSF v3 via name property', async () => { const code = ` export default { component: Button } export const Primary = { name: "custom name" };`; @@ -238,30 +238,7 @@ describe('transformer', () => { `); }); - it('should support csf v3 with function config', async () => { - const code = ` - export default { component: Button }; - export const Story = () => { name: 'custom name' };`; - const result = await transform({ code }); - expect(result.code).toMatchInlineSnapshot(` - import { test as _test, expect as _expect } from "vitest"; - import { testStory as _testStory } from "@storybook/experimental-addon-test/internal/test-utils"; - const _meta = { - component: Button, - title: "automatic/calculated/title" - }; - export default _meta; - export const Story = () => { - name: 'custom name'; - }; - const _isRunningFromThisFile = import.meta.url.includes(globalThis.__vitest_worker__.filepath ?? _expect.getState().testPath); - if (_isRunningFromThisFile) { - _test("Story", _testStory("Story", Story, _meta, [])); - } - `); - }); - - it('should support csf v1/v2', async () => { + it('should support CSF v1/v2 via storyName property', async () => { const code = ` export default { component: Button } export const Story = () => {} From f665c36e3d02e621ea989f502c23dfbbc5321d50 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Mon, 23 Sep 2024 17:48:40 +0200 Subject: [PATCH 6/6] update faq --- docs/writing-tests/vitest-plugin.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/writing-tests/vitest-plugin.mdx b/docs/writing-tests/vitest-plugin.mdx index 2bf22f19f080..1a0e4b9708b2 100644 --- a/docs/writing-tests/vitest-plugin.mdx +++ b/docs/writing-tests/vitest-plugin.mdx @@ -379,9 +379,10 @@ We recommend running tests in a browser using Playwright, but you can use WebDri We recommend using Chromium, because it is most likely to best match the experience of a majority of your users. However, you can use other browsers by adjusting the [browser name in the Vitest configuration file](https://vitest.dev/config/#browser-name). Note that [Playwright and WebDriverIO support different browsers](https://vitest.dev/guide/browser/#browser-option-types). -### How do I customized the test description +### How do I customize a test name? + +By default, the export name of a story is mapped to the test name. To create a more descriptive test description, you can provide a `name` property for the story. This allows you to include spaces, brackets, or other special characters. -By default, the export name of a story is mapped to the test name. You can provide a `name` property for the story to customize the test description, allowing you to write more descriptive names. This is useful when you want use spaces, brackets or other special characters in the test description. ```js export const Story = { name: 'custom, descriptive name'