diff --git a/scripts/ci/main.ts b/scripts/ci/main.ts index f4c5ac3d1f21..744d4d308b7a 100644 --- a/scripts/ci/main.ts +++ b/scripts/ci/main.ts @@ -52,7 +52,7 @@ function generateConfig(workflow: Workflow) { jobs.push(fmt); } else { const sandboxes = getSandboxes(workflow); - const testStorybooks = getTestStorybooks(workflow); + const testStorybooks = getTestStorybooks(); const initEmpty = getInitEmpty(workflow); if (isWorkflowOrAbove(workflow, 'daily')) { diff --git a/scripts/ci/test-storybooks.ts b/scripts/ci/test-storybooks.ts index d8963ad886c5..a52d2c0f33e5 100644 --- a/scripts/ci/test-storybooks.ts +++ b/scripts/ci/test-storybooks.ts @@ -3,13 +3,7 @@ import { join } from 'path/posix'; import { build_linux } from './common-jobs.ts'; import { artifact, workflow } from './utils/helpers.ts'; -import { - type JobOrNoOpJob, - type Workflow, - defineJob, - defineNoOpJob, - isWorkflowOrAbove, -} from './utils/types.ts'; +import { type JobOrNoOpJob, defineJob, defineNoOpJob } from './utils/types.ts'; export function definePortableStoryTest(directory: string) { const working_directory = `test-storybooks/portable-stories-kitchen-sink/${directory}`; @@ -76,39 +70,6 @@ export function definePortableStoryTest(directory: string) { ); } -export function definePortableStoryTestVitest3() { - return defineJob( - 'test-storybooks-portable-vitest3', - () => ({ - executor: { - name: 'sb_playwright', - class: 'medium', - }, - steps: [ - ...workflow.restoreLinux(), - { - run: { - name: 'Install dependencies', - working_directory: 'test-storybooks/portable-stories-kitchen-sink/react-vitest-3', - command: 'yarn install --no-immutable', - environment: { - YARN_ENABLE_IMMUTABLE_INSTALLS: false, - }, - }, - }, - { - run: { - name: 'Run Playwright E2E tests', - working_directory: 'test-storybooks/portable-stories-kitchen-sink/react-vitest-3', - command: 'yarn playwright-e2e', - }, - }, - ], - }), - [testStorybooksNoOpJob] - ); -} - export function defineMcpTestStorybook() { return defineJob( 'test-storybooks-mcp', @@ -160,16 +121,12 @@ export function defineMcpTestStorybook() { export const testStorybooksNoOpJob = defineNoOpJob('test-storybooks', [build_linux]); -export function getTestStorybooks(workflow: Workflow) { +export function getTestStorybooks() { const testStorybooks: JobOrNoOpJob[] = ['react', 'vue3', 'svelte', 'nextjs'].map( definePortableStoryTest ); testStorybooks.push(defineMcpTestStorybook()); - if (isWorkflowOrAbove(workflow, 'merged')) { - testStorybooks.push(definePortableStoryTestVitest3()); - } - return testStorybooks; } diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.eslintrc.cjs b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.eslintrc.cjs deleted file mode 100644 index 29cb6d5a0877..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.eslintrc.cjs +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended', 'plugin:storybook/recommended'], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, -} diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.gitignore b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.gitignore deleted file mode 100644 index b88c8135abcd..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? -/test-results/ -/playwright-report/ -/blob-report/ -/playwright/.cache/ diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/aliased.ts b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/aliased.ts deleted file mode 100644 index 030574bfd2a6..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/aliased.ts +++ /dev/null @@ -1,4 +0,0 @@ -// This file is used to test that viteFinal is correctly loaded in Vitest -// main.ts defines this file as a resolve alias, which is used in the ViteFinalTest story - -export const aliasedFunction = () => true; diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/get-decorator-string.ts b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/get-decorator-string.ts deleted file mode 100644 index 794f14db5674..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/get-decorator-string.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const getDecoratorString = () => { - return "Global Decorator"; -}; diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/main.ts b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/main.ts deleted file mode 100644 index fa0ee95488bf..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/main.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { join } from "node:path"; - -import type { StorybookConfig } from "@storybook/react-vite"; - -const config: StorybookConfig = { - stories: ["../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)"], - addons: ["@storybook/addon-vitest", "@storybook/addon-a11y"], - framework: { - name: "@storybook/react-vite", - options: {}, - }, - core: { - disableWhatsNewNotifications: true, - }, - previewHead: (head = "") => `${head} - `, - staticDirs: [{ from: "./test-static-dirs", to: "test-static-dirs" }], - viteFinal: (config) => { - return { - ...config, - optimizeDeps: { - ...config.optimizeDeps, - include: [...(config.optimizeDeps?.include || [])], - }, - resolve: { - ...config.resolve, - alias: { - ...config.resolve?.alias, - "test-alias": join(import.meta.dirname, "aliased.ts"), - }, - }, - }; - }, - refs: { - "storybook@8.0.0": { - title: "Storybook 8.0.0", - url: "https://635781f3500dd2c49e189caf-gckybvsekn.chromatic.com/", - }, - "storybook@7.6.18": { - title: "Storybook 7.6.18", - url: "https://635781f3500dd2c49e189caf-oljwjdrftz.chromatic.com/", - }, - }, -}; - -export default config; diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/preview-head.html b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/preview-head.html deleted file mode 100644 index 61252d019910..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/preview-head.html +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/preview.tsx b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/preview.tsx deleted file mode 100644 index ee02f0c8d810..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/preview.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { Preview } from '@storybook/react-vite'; -import { getDecoratorString } from './get-decorator-string'; - -console.log('preview file is called!'); - -const preview: Preview = { - decorators: [ - (StoryFn) => ( -
-
{getDecoratorString()}
-
- -
- ), - ], - initialGlobals: { - locale: 'en', - }, - globalTypes: { - locale: { - description: 'Locale for components', - toolbar: { - title: 'Locale', - icon: 'circlehollow', - items: ['es', 'en'], - }, - }, - }, -}; - -export default preview; diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/setup-file-dependency.ts b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/setup-file-dependency.ts deleted file mode 100644 index 592e8f52b66b..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/setup-file-dependency.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const getString = () => { - return "initial string"; -}; diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/test-static-dirs/static.js b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/test-static-dirs/static.js deleted file mode 100644 index 56ff9f9c7ad4..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/test-static-dirs/static.js +++ /dev/null @@ -1,3 +0,0 @@ -// This file is used to test that staticDirs are correctly loaded in Vitest - -export const staticFunction = () => true; diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/vitest.setup.ts b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/vitest.setup.ts deleted file mode 100644 index 51fc3ac60150..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.storybook/vitest.setup.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { getString } from './setup-file-dependency'; - -console.log(getString()); diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.yarnrc b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.yarnrc deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.yarnrc.yml b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.yarnrc.yml deleted file mode 100644 index e02860e70d88..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/.yarnrc.yml +++ /dev/null @@ -1 +0,0 @@ -yarnPath: ../../../.yarn/releases/yarn-4.18.0.cjs diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/e2e-tests/component-testing.spec.ts b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/e2e-tests/component-testing.spec.ts deleted file mode 100644 index 6998b3c7793c..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/e2e-tests/component-testing.spec.ts +++ /dev/null @@ -1,822 +0,0 @@ -import { promises as fs } from "node:fs"; -import path from "node:path"; - -import { expect, test } from "@playwright/test"; - -import { SbPage } from "../../../../code/e2e-sandbox/util"; - -const STORYBOOK_URL = "http://localhost:6006"; -const TEST_STORY_PATH = path.resolve( - __dirname, - "..", - "stories", - "AddonTest.stories.tsx" -); -const UNHANDLED_ERRORS_STORY_PATH = path.resolve( - __dirname, - "..", - "stories", - "UnhandledErrors.stories.tsx" -); -const ADDON_TEST_DEPENDENCY_PATH = path.resolve( - __dirname, - "..", - "stories", - "get-button-string.ts" -); -const PREVIEW_DEPENDENCY_PATH = path.resolve( - __dirname, - "..", - ".storybook", - "get-decorator-string.ts" -); -const SETUP_FILE_DEPENDENCY_PATH = path.resolve( - __dirname, - "..", - ".storybook", - "setup-file-dependency.ts" -); - -const setForceFailureFlag = (content: string, value: boolean) => - content.replace(/forceFailure:\s*(true|false)/, `forceFailure: ${value}`); - -const modifiedFiles = new Map(); - -const modifyFile = async ( - filePath: string, - modify: (content: string) => string -) => { - const content = (await fs.readFile(filePath)).toString(); - const modifiedContent = modify(content); - await fs.writeFile(filePath, modifiedContent); - if (!modifiedFiles.has(filePath)) { - modifiedFiles.set(filePath, content); - } - - // the file change causes a HMR event, which causes a browser reload,and that can take a few seconds - await new Promise((resolve) => setTimeout(resolve, 2000)); -}; - -const restoreAllFiles = async () => { - for (const [filePath, originalContent] of modifiedFiles.entries()) { - await fs.writeFile(filePath, originalContent); - } - modifiedFiles.clear(); - // the file change causes a HMR event, which causes a browser reload,and that can take a few seconds - await new Promise((resolve) => setTimeout(resolve, 2000)); -}; - -test.describe("component testing", () => { - test.describe.configure({ mode: "serial" }); - test.beforeEach(async ({ page }) => { - const sbPage = new SbPage(page, expect); - - await page.goto(STORYBOOK_URL); - await page.evaluate(() => window.sessionStorage.clear()); - await sbPage.waitUntilLoaded(); - - const expandTestingModule = page.getByLabel("Expand testing module"); - if (await expandTestingModule.isVisible()) { - await expandTestingModule.click(); - } - }); - - test.afterEach(async ({ page }) => { - await page.click("body"); - try { - /** Sometimes the vitest instance fails, but the error-content is only shown in a modal. - * We click the link so the modal opens and we can close it. - * Having it open shortly is enough to have it be in the playwright trace, for debugging purposes. - */ - const descriptionButton = page.locator("#testing-module-description button"); - if ( - await descriptionButton.isVisible({ timeout: 4000 }).catch(() => false) - ) { - await descriptionButton.click({ timeout: 4000, force: true }); - - await page - .getByLabel("Close modal") - .click({ timeout: 4000, force: true }); - } - } catch { - // Ignore any errors when trying to open the modal - } - - await restoreAllFiles(); - - const expandTestingModule = page.getByLabel("Expand testing module"); - if (await expandTestingModule.isVisible()) { - await expandTestingModule.click(); - } - - // Make sure any popover is closed - await page.click("body"); - - // Ensure that all test results are removed and features are disabled, as previous tests might have enabled them - const clearStatusesButton = page.getByLabel("Clear all statuses"); - if (await clearStatusesButton.isVisible()) { - await clearStatusesButton.click(); - } - - const watchModeToggle = page.getByRole("switch", { name: "Watch mode" }); - if ( - (await watchModeToggle.isVisible()) && - (await watchModeToggle.getAttribute("aria-checked")) === "true" - ) { - await watchModeToggle.click(); - } - - const configs = [ - page.getByRole("checkbox", { name: "Coverage" }), - page.getByRole("checkbox", { name: "Accessibility" }), - ]; - for (const config of configs) { - if (await config.isChecked()) { - await config.click(); - } - } - }); - - test("should show discrepancy between test results", async ({ - page, - browserName, - }) => { - test.skip(browserName !== "chromium", `Skipping tests for ${browserName}`); - const sbPage = new SbPage(page, expect); - - await sbPage.navigateToStory("addons/group/test", "Mismatch Failure"); - - // For whatever reason, sometimes it takes longer for the story to load - const storyElement = sbPage - .getCanvasBodyElement() - .getByRole("button", { name: "test" }); - await expect(storyElement).toBeVisible({ timeout: 30000 }); - - await sbPage.viewAddonPanel("Interactions"); - - // For whatever reason, when visiting a story sometimes the story element is collapsed and that causes flake - const testStoryElement = await page.getByRole("button", { - name: "Test", - exact: true, - }); - if ((await testStoryElement.getAttribute("aria-expanded")) !== "true") { - await testStoryElement.click(); - } - - const testingModuleDescription = await page.locator( - "#testing-module-description" - ); - - const runTestsButton = await page.getByLabel("Start test run"); - await runTestsButton.click(); - - // Wait for test results to appear - await expect(testingModuleDescription).toHaveText(/Ran \d+ tests/, { - timeout: 60000, - }); - - const errorFilter = page.getByLabel( - /Filter main navigation to show \d+ tests with errors/ - ); - await expect(errorFilter).toBeVisible(); - - // Assert discrepancy: CLI pass + Browser fail - const failingStoryElement = page.locator( - '[data-item-id="addons-group-test--mismatch-failure"] [data-testid="tree-status-button"]' - ); - await expect(failingStoryElement).toHaveAttribute( - "aria-label", - "Test status: Success" - ); - await expect(sbPage.panelContent()).toContainText( - /This interaction test passed in the CLI, but the tests failed in this browser/ - ); - - // Assert discrepancy: CLI fail + Browser pass - await sbPage.navigateToStory("addons/group/test", "Mismatch Success"); - const successfulStoryElement = page.locator( - '[data-item-id="addons-group-test--mismatch-success"] [data-testid="tree-status-button"]' - ); - await expect(successfulStoryElement).toHaveAttribute( - "aria-label", - "Test status: Error" - ); - await expect(sbPage.panelContent()).toContainText( - /This interaction test passed in this browser, but the tests failed in the CLI/ - ); - }); - - test("should execute tests via testing module UI", async ({ - page, - browserName, - }) => { - test.skip(browserName !== "chromium", `Skipping tests for ${browserName}`); - await modifyFile(TEST_STORY_PATH, (content) => - setForceFailureFlag(content, true) - ); - - const sbPage = new SbPage(page, expect); - await sbPage.navigateToStory("addons/group/test", "Expected Failure"); - - // For whatever reason, sometimes it takes longer for the story to load - const storyElement = sbPage - .getCanvasBodyElement() - .getByRole("button", { name: "test" }); - await expect(storyElement).toBeVisible({ timeout: 30000 }); - - await expect(page.locator("#testing-module-title")).toHaveText( - "Run component tests" - ); - - const runTestsButton = await page.getByLabel("Start test run"); - const watchModeButton = await page.getByRole("switch", { - name: "Watch mode", - }); - await expect(runTestsButton).not.toHaveAttribute("aria-disabled", "true"); - await expect(watchModeButton).not.toHaveAttribute("aria-disabled", "true"); - - await runTestsButton.click(); - - // Wait for test results to appear - await expect(page.locator("#testing-module-description")).toHaveText( - /Ran \d+ tests/, - { timeout: 30000 } - ); - - await expect(runTestsButton).not.toHaveAttribute("aria-disabled", "true"); - await expect(watchModeButton).not.toHaveAttribute("aria-disabled", "true"); - - const errorFilter = page.getByLabel( - /Filter main navigation to show \d+ tests with errors/ - ); - await expect(errorFilter).toBeVisible(); - - // Assert for expected success - const successfulStoryElement = page.locator( - '[data-item-id="addons-group-test--expected-success"] [data-testid="tree-status-button"]' - ); - await expect(successfulStoryElement).toHaveAttribute( - "aria-label", - "Test status: Success" - ); - - // Assert for expected failure - const failingStoryElement = page.locator( - '[data-item-id="addons-group-test--expected-failure"] [data-testid="tree-status-button"]' - ); - await expect(failingStoryElement).toHaveAttribute( - "aria-label", - "Test status: Error" - ); - - // Assert that filter works as intended - await errorFilter.click(); - - const sidebarItems = page.locator( - '.sidebar-item[data-ref-id="storybook_internal"][data-nodetype="component"]' - ); - await expect(sidebarItems).toHaveCount(2); - }); - - test("should run tests in watch mode when a story file is changed", async ({ - page, - browserName, - }) => { - test.skip(browserName !== "chromium", `Skipping tests for ${browserName}`); - await modifyFile(TEST_STORY_PATH, (content) => - setForceFailureFlag(content, false) - ); - - const sbPage = new SbPage(page, expect); - await sbPage.navigateToStory("addons/group/test", "Expected Failure"); - - // For whatever reason, sometimes it takes longer for the story to load - const storyElement = sbPage - .getCanvasBodyElement() - .getByRole("button", { name: "test" }); - await expect(storyElement).toBeVisible({ timeout: 30000 }); - - await page.getByRole("switch", { name: "Watch mode" }).click(); - - // We shouldn't have to do an arbitrary wait, but because there is no UI for loading state yet, we have to - await page.waitForTimeout(8000); - await modifyFile(TEST_STORY_PATH, (content) => - setForceFailureFlag(content, true) - ); - - // Wait for test results to appear - const errorFilter = page.getByLabel( - /Filter main navigation to show \d+ tests with errors/ - ); - await expect(errorFilter).toBeVisible({ timeout: 30000 }); - - // Assert for expected success - const successfulStoryElement = page.locator( - '[data-item-id="addons-group-test--expected-success"] [data-testid="tree-status-button"]' - ); - await expect(successfulStoryElement).toHaveAttribute( - "aria-label", - "Test status: Success" - ); - - // Assert for expected failure - const failingStoryElement = page.locator( - '[data-item-id="addons-group-test--expected-failure"] [data-testid="tree-status-button"]' - ); - await expect(failingStoryElement).toHaveAttribute( - "aria-label", - "Test status: Error" - ); - - // Assert that filter works as intended - await errorFilter.click(); - - const sidebarItems = page.locator( - '.sidebar-item[data-ref-id="storybook_internal"][data-nodetype="component"]' - ); - await expect(sidebarItems).toHaveCount(1); - }); - - test("should run tests in watch mode when a story file's dependency is changed", async ({ - page, - browserName, - }) => { - test.skip(browserName !== "chromium", `Skipping tests for ${browserName}`); - - const sbPage = new SbPage(page, expect); - await sbPage.navigateToStory("addons/group/test", "Expected Failure"); - - // For whatever reason, sometimes it takes longer for the story to load - const storyElement = sbPage - .getCanvasBodyElement() - .getByRole("button", { name: "test" }); - await expect(storyElement).toBeVisible({ timeout: 30000 }); - - await page.getByRole("switch", { name: "Watch mode" }).click(); - - // We shouldn't have to do an arbitrary wait, but because there is no UI for loading state yet, we have to - await page.waitForTimeout(3000); - await modifyFile(ADDON_TEST_DEPENDENCY_PATH, (content) => - content.replace("test", "changed") - ); - - // Expect less than 10 tests to have run - await expect(page.locator("#testing-module-description")).toContainText( - /Ran \d tests/, - { timeout: 30000 } - ); - - // Assert for expected failure - const failingStoryElement = page.locator( - '[data-item-id="addons-group-test--expected-content"] [data-testid="tree-status-button"]' - ); - await expect(failingStoryElement).toHaveAttribute( - "aria-label", - "Test status: Error" - ); - }); - - test("should run all tests in watch mode when the preview file's dependency is changed", async ({ - page, - browserName, - }) => { - test.skip(browserName !== "chromium", `Skipping tests for ${browserName}`); - - const sbPage = new SbPage(page, expect); - await sbPage.navigateToStory("addons/group/test", "Expected Failure"); - - // For whatever reason, sometimes it takes longer for the story to load - const storyElement = sbPage - .getCanvasBodyElement() - .getByRole("button", { name: "test" }); - await expect(storyElement).toBeVisible({ timeout: 30000 }); - - await page.getByRole("switch", { name: "Watch mode" }).click(); - - // We shouldn't have to do an arbitrary wait, but because there is no UI for loading state yet, we have to - await page.waitForTimeout(3000); - await modifyFile(PREVIEW_DEPENDENCY_PATH, (content) => - content.replace("Global Decorator", "Changed Decorator") - ); - - // Expect at least 20 tests to have run - await expect(page.locator("#testing-module-description")).toContainText( - /Ran [2-9]\d tests/, - { timeout: 30000 } - ); - - // Assert for expected failure - const failingStoryElement = page.locator( - '[data-item-id="addons-group-test--expected-content"] [data-testid="tree-status-button"]' - ); - - await expect(failingStoryElement).toHaveAttribute( - "aria-label", - "Test status: Error" - ); - }); - - test("should run all tests in watch mode when the setup file's dependency is changed", async ({ - page, - browserName, - }) => { - test.skip(browserName !== "chromium", `Skipping tests for ${browserName}`); - - const sbPage = new SbPage(page, expect); - await sbPage.navigateToStory("addons/group/test", "Expected Failure"); - - // For whatever reason, sometimes it takes longer for the story to load - const storyElement = sbPage - .getCanvasBodyElement() - .getByRole("button", { name: "test" }); - await expect(storyElement).toBeVisible({ timeout: 30000 }); - - await page.getByRole("switch", { name: "Watch mode" }).click(); - - // We shouldn't have to do an arbitrary wait, but because there is no UI for loading state yet, we have to - await page.waitForTimeout(3000); - await modifyFile(SETUP_FILE_DEPENDENCY_PATH, (content) => - content.replace("initial string", "changed string") - ); - - // Expect at least 20 tests to have run - await expect(page.locator("#testing-module-description")).toContainText( - /Ran [2-9]\d tests/, - { timeout: 30000 } - ); - }); - - test("should collect coverage to testing module and HTML report", async ({ - page, - browserName, - }) => { - test.skip(browserName !== "chromium", `Skipping tests for ${browserName}`); - // Arrange - Prepare Storybook - await modifyFile(TEST_STORY_PATH, (content) => - setForceFailureFlag(content, false) - ); - - const sbPage = new SbPage(page, expect); - await sbPage.navigateToStory("addons/group/test", "Expected Failure"); - - const storyElement = sbPage - .getCanvasBodyElement() - .getByRole("button", { name: "test" }); - await expect(storyElement).toBeVisible({ timeout: 30000 }); - - // Assert - No coverage report initially - await expect(page.getByLabel("Open coverage report")).toHaveCount(0); - - // Act - Enable coverage and run tests - await page.getByLabel("Coverage", { exact: true }).click(); - // Wait for Vitest to have (re)started - await page.waitForTimeout(2000); - - await page.getByLabel("Start test run").click(); - - // Assert - Coverage report is collected and shown - await expect(page.getByLabel("Open coverage report")).toBeVisible({ - timeout: 30000, - }); - const sbPercentageText = await page - .getByLabel(/% coverage\)$/) - .textContent(); - expect(sbPercentageText).toMatch(/^\d+%$/); - const sbPercentage = Number.parseInt( - sbPercentageText!.replace("%", "") ?? "" - ); - expect(sbPercentage).toBeGreaterThanOrEqual(0); - expect(sbPercentage).toBeLessThanOrEqual(100); - - // Act - Open HTML coverage report - const coverageReportLink = await page.getByLabel("Open coverage report"); - // Remove target="_blank" attribute to open in the same tab - await coverageReportLink.evaluate((elem) => elem.removeAttribute("target")); - await page.getByLabel("Open coverage report").click(); - - // Assert - HTML coverage report is accessible and reports the same coverage percentage as Storybook - const htmlPercentageText = - (await page - .locator('span:has(+ :text("Statements"))') - .first() - .textContent()) ?? ""; - const htmlPercentage = Number.parseFloat( - htmlPercentageText.replace("% ", "") - ); - expect(Math.round(htmlPercentage)).toBe(sbPercentage); - - await page.goBack(); - }); - - test("should run focused test for a single story", async ({ - page, - browserName, - }) => { - test.skip(browserName !== "chromium", `Skipping tests for ${browserName}`); - // Arrange - Prepare Storybook - await modifyFile(TEST_STORY_PATH, (content) => - setForceFailureFlag(content, false) - ); - - const sbPage = new SbPage(page, expect); - await sbPage.navigateToStory("addons/group/test", "Expected Failure"); - - const storyElement = sbPage - .getCanvasBodyElement() - .getByRole("button", { name: "test" }); - await expect(storyElement).toBeVisible({ timeout: 30000 }); - - // Act - Open sidebar context menu, start focused test then close menu - await page - .locator('[data-item-id="addons-group-test--expected-failure"]') - .hover(); - await page - .locator( - '[data-item-id="addons-group-test--expected-failure"] button[data-testid="context-menu"]' - ) - .click(); - const sidebarContextMenu = page.getByRole("dialog"); - await sidebarContextMenu.getByLabel("Start test run").click(); - - // Assert - Only one test is running and reported - await expect( - sidebarContextMenu.locator("#testing-module-description") - ).toContainText("Ran 1 test", { timeout: 30000 }); - await expect( - sidebarContextMenu.getByLabel("Component tests passed") - ).toHaveCount(1); - await page.click("body"); - await expect( - page.locator( - '#storybook-explorer-menu [data-testid="tree-status-button"][aria-label="Test status: Success"]' - ) - ).toHaveCount(3); // 1 story, 1 component, 1 group - }); - - test("should show unhandled errors in the testing module", async ({ - page, - browserName, - }) => { - test.skip(browserName !== "chromium", `Skipping tests for ${browserName}`); - // Arrange - Prepare Storybook - await modifyFile(UNHANDLED_ERRORS_STORY_PATH, (content) => - setForceFailureFlag(content, true) - ); - - const sbPage = new SbPage(page, expect); - await sbPage.navigateToStory("example/unhandlederrors", "Success"); - - const storyElement = sbPage.getCanvasBodyElement().getByText("Hello world"); - await expect(storyElement).toBeVisible({ timeout: 30000 }); - - // Act - Open sidebar context menu and start focused test - await page.locator('[data-item-id="example-unhandlederrors"]').hover(); - await page - .locator( - '[data-item-id="example-unhandlederrors"] button[data-testid="context-menu"]' - ) - .click(); - const sidebarContextMenu = page.getByRole("dialog"); - await sidebarContextMenu.getByLabel("Start test run").click(); - - // HACK: the testing module popover has poor tracking of focus due to how many disabled - // buttons it has and how deeply it changes its UI on events. This would be solved once - // we move to a declarative menu, and there's an ongoing PR for that. Until then, we tab - // around to reset focus. - await page.keyboard.press("Tab"); - await page.keyboard.press("Escape"); - await page.click("body"); - await expect(sidebarContextMenu).not.toBeVisible(); - - // Assert - Tests are running and errors are reported - const errorLink = page.locator( - "#storybook-testing-module #testing-module-description button" - ); - await expect(errorLink).toContainText("View full error", { - timeout: 30000, - }); - await errorLink.click(); - - await expect(page.locator("pre")).toContainText( - "I THREW AN UNHANDLED ERROR!" - ); - await expect(page.locator("pre")).toContainText("This error originated in"); - await expect(page.locator("pre")).toContainText( - "The latest test that might've caused the error is" - ); - await page.getByLabel("Close modal").click(); - }); - - test("should run focused test for a component", async ({ - page, - browserName, - }) => { - test.skip(browserName !== "chromium", `Skipping tests for ${browserName}`); - // Arrange - Prepare Storybook - await modifyFile(TEST_STORY_PATH, (content) => - setForceFailureFlag(content, false) - ); - - const sbPage = new SbPage(page, expect); - await sbPage.navigateToStory("addons/group/test", "Expected Failure"); - - const storyElement = sbPage - .getCanvasBodyElement() - .getByRole("button", { name: "test" }); - await expect(storyElement).toBeVisible({ timeout: 30000 }); - - // Act - Open sidebar context menu and start focused test - await page.locator('[data-item-id="addons-group-test"]').hover(); - await page - .locator( - '[data-item-id="addons-group-test"] button[data-testid="context-menu"]' - ) - .click(); - const sidebarContextMenu = page.getByRole("dialog"); - await sidebarContextMenu.getByLabel("Start test run").click(); - - // Assert - Tests are running and reported - await expect( - sidebarContextMenu.locator("#testing-module-description") - ).toContainText("Ran 9 tests", { timeout: 30000 }); - // Assert - Failing test shows as a failed status - await expect( - sidebarContextMenu.getByLabel("Component tests failed") - ).toHaveCount(1); - await expect( - sidebarContextMenu.getByLabel( - "Component tests failed (1 errors or warnings so far)" - ) - ).toBeVisible(); - - // HACK: the testing module popover has poor tracking of focus due to how many disabled - // buttons it has and how deeply it changes its UI on events. This would be solved once - // we move to a declarative menu, and there's an ongoing PR for that. Until then, we tab - // around to reset focus. - await page.keyboard.press("Tab"); - await page.keyboard.press("Escape"); - await page.click("body"); - await expect(sidebarContextMenu).not.toBeVisible(); - - await page.click("body"); - await expect( - page.locator( - '#storybook-explorer-menu [data-testid="tree-status-button"][aria-label="Test status: Success"]' - ) - ).toHaveCount(8); - await expect( - page.locator( - '#storybook-explorer-menu [data-testid="tree-status-button"][aria-label="Test status: Error"]' - ) - ).toHaveCount(3); - }); - - test("should run focused test for a group", async ({ page, browserName }) => { - test.skip(browserName !== "chromium", `Skipping tests for ${browserName}`); - // Arrange - Prepare Storybook - await modifyFile(TEST_STORY_PATH, (content) => - setForceFailureFlag(content, false) - ); - - const sbPage = new SbPage(page, expect); - await sbPage.navigateToStory("addons/group/test", "Expected Failure"); - - const storyElement = sbPage - .getCanvasBodyElement() - .getByRole("button", { name: "test" }); - await expect(storyElement).toBeVisible({ timeout: 30000 }); - - // Act - Open sidebar context menu and start focused test - await page.locator('[data-item-id="addons-group"]').hover(); - await page - .locator( - '[data-item-id="addons-group"] button[data-testid="context-menu"]' - ) - .click(); - const sidebarContextMenu = page.getByRole("dialog"); - await sidebarContextMenu.getByLabel("Start test run").click(); - - // Assert - 1 failing test shows as a failed status - await expect( - sidebarContextMenu.getByLabel( - "Component tests failed (2 errors or warnings so far)" - ) - ).toBeVisible(); - await expect( - sidebarContextMenu.getByLabel("Component tests failed") - ).toHaveCount(1); - - // HACK: the testing module popover has poor tracking of focus due to how many disabled - // buttons it has and how deeply it changes its UI on events. This would be solved once - // we move to a declarative menu, and there's an ongoing PR for that. Until then, we tab - // around to reset focus. - await page.keyboard.press("Tab"); - await page.keyboard.press("Escape"); - await page.click("body"); - await expect(sidebarContextMenu).not.toBeVisible(); - - // Assert - Tests are running and reported - await expect(page.locator("#testing-module-description")).toContainText( - "Ran 11 tests", - { timeout: 30000 } - ); - await expect( - page.locator( - '#storybook-explorer-menu [data-testid="tree-status-button"][aria-label="Test status: Error"]' - ) - ).toHaveCount(4); // 1 visible/expanded story, 1 expanded component, 1 collapsed component, 1 group - }); - - test("should run focused tests without coverage, even when enabled", async ({ - page, - browserName, - }) => { - test.skip(browserName !== "chromium", `Skipping tests for ${browserName}`); - // Arrange - Prepare Storybook - await modifyFile(TEST_STORY_PATH, (content) => - setForceFailureFlag(content, false) - ); - - const sbPage = new SbPage(page, expect); - await sbPage.navigateToStory("example/button", "CSF 3 Primary"); - - const storyElement = sbPage - .getCanvasBodyElement() - .getByRole("button", { name: "foo" }); - await expect(storyElement).toBeVisible({ timeout: 30000 }); - - // Act - Enable coverage - await page.getByLabel("Coverage", { exact: true }).click(); - // Wait for Vitest to have (re)started - await page.waitForTimeout(2000); - - // Act - Open sidebar context menu and start focused test - await page - .locator('[data-item-id="example-button--csf-3-primary"]') - .hover(); - await page - .locator( - '[data-item-id="example-button--csf-3-primary"] button[data-testid="context-menu"]' - ) - .click(); - const sidebarContextMenu = page.getByRole("dialog"); - await sidebarContextMenu.getByLabel("Start test run").click(); - - // HACK: the testing module popover has poor tracking of focus due to how many disabled - // buttons it has and how deeply it changes its UI on events. This would be solved once - // we move to a declarative menu, and there's an ongoing PR for that. Until then, we tab - // around to reset focus. - await page.keyboard.press("Tab"); - await page.keyboard.press("Escape"); - await page.click("body"); - await expect(sidebarContextMenu).not.toBeVisible(); - - // Arrange - Wait for test to finish and unfocus sidebar context menu - await expect(page.locator("#testing-module-description")).toContainText( - "Ran 1 test", - { timeout: 30000 } - ); - await page.click("body"); - - // Assert - Coverage is not shown because Focused Tests shouldn't collect coverage - await expect(page.getByLabel("Open coverage report")).not.toBeVisible(); - - // Act - Run ALL tests - await page.getByLabel("Start test run").click(); - - // Arrange - Wait for tests to finish - await expect(page.locator("#testing-module-description")).toContainText( - /Ran \d{2,} tests/, - { timeout: 30000 } - ); - - // Assert - Coverage percentage is now collected and shown because running all tests automatically re-enables coverage - await expect(page.getByLabel("Open coverage report")).toBeVisible({ - timeout: 30000, - }); - const sbPercentageText = await page - .getByLabel(/% coverage\)$/) - .textContent(); - expect(sbPercentageText).toMatch(/^\d+%$/); - const sbPercentage = Number.parseInt( - sbPercentageText!.replace("%", "") ?? "" - ); - expect(sbPercentage).toBeGreaterThanOrEqual(0); - expect(sbPercentage).toBeLessThanOrEqual(100); - }); - - test.fixme( - "should still collect statuses even when the browser is closed", - () => {} - ); - - test.fixme( - "should have correct status count globally and in context menus", - () => {} - ); - - test.fixme( - "should open the correct component test and a11y panels when clicking on statuses", - () => {} - ); -}); diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/e2e-tests/composition.spec.ts b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/e2e-tests/composition.spec.ts deleted file mode 100644 index 55be83eeeae8..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/e2e-tests/composition.spec.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { expect, test } from '@playwright/test'; - -import { SbPage } from '../../../../code/e2e-sandbox/util'; - -const STORYBOOK_URL = 'http://localhost:6006'; - -test.describe('composition', () => { - // the composed storybook can be slow to load, so we need to increase the timeout - test.describe.configure({ mode: 'serial', timeout: 60000, retries: 2 }); - test('should filter and render composed stories', async ({ page }) => { - await page.goto(STORYBOOK_URL); - await new SbPage(page, expect).waitUntilLoaded(); - - // Expect that composed Storybooks are visible and loaded - await expect(page.getByTitle('Storybook 8.0.0')).toBeVisible(); - await expect(page.locator('[id="storybook\\@8\\.0\\.0_components-badge"]')).toBeVisible({ - timeout: 15000, - }); - await expect(page.getByTitle('Storybook 7.6.18')).toBeVisible(); - await expect(page.locator('[id="storybook\\@7\\.6\\.18_components-badge"]')).toBeVisible({ - timeout: 15000, - }); - - // Expect composed stories to be available in the sidebar - await page.locator('[id="storybook\\@8\\.0\\.0_components-badge"]').click(); - await expect( - page.locator('[id="storybook\\@8\\.0\\.0_components-badge--default"]') - ).toBeVisible(); - - await page.locator('[id="storybook\\@7\\.6\\.18_components-badge"]').click(); - await expect( - page - .locator('iframe[title="storybook-ref-storybook\\@7\\.6\\.18"]') - .contentFrame() - .locator('#storybook-root') - .getByText('Default') - ).toBeVisible({ timeout: 15000 }); - - // Expect composed stories `to be available in the search - await page.getByPlaceholder('Find components').fill('Button primary'); - await expect( - page.getByRole('option', { - name: 'Primary Storybook 7.6.18 / @components / Button', - }) - ).toBeVisible(); - - const buttonStory = page.getByRole('option', { - name: 'Primary Storybook 8.0.0 / @blocks / examples / Button', - }); - await expect(buttonStory).toBeVisible(); - await buttonStory.click(); - - // Note: this could potentially be flaky due to it accessing a hosted Storybook - await expect( - page - .locator('iframe[title="storybook-ref-storybook\\@8\\.0\\.0"]') - .contentFrame() - .getByRole('button') - ).toBeVisible({ timeout: 15000 }); - }); - - test('should filter and render composed stories on mobile', async ({ page }) => { - page.setViewportSize({ width: 320, height: 800 }); - await page.goto(STORYBOOK_URL); - await new SbPage(page, expect).waitUntilLoaded(); - - await page.click('button[aria-label="Open navigation menu"]'); - - // scroll down to the bottom of the element getByText('Skip to canvasStorybookSearch') - - await page.getByTitle('Storybook 7.6.18').scrollIntoViewIfNeeded(); - - // Expect that composed Storybooks are visible - await expect(page.getByTitle('Storybook 8.0.0')).toBeVisible(); - await expect(page.getByTitle('Storybook 7.6.18')).toBeVisible(); - - // Expect composed stories to be available in the sidebar - await expect(page.locator('[id="storybook\\@8\\.0\\.0_components-badge"]')).toBeVisible(); - await page.locator('[id="storybook\\@8\\.0\\.0_components-badge"]').click(); - await expect( - page.locator('[id="storybook\\@8\\.0\\.0_components-badge--default"]') - ).toBeVisible(); - - await page.locator('[id="storybook\\@7\\.6\\.18_components-badge"]').click(); - await page.locator('[id="storybook\\@7\\.6\\.18_components-badge--default"]').click(); - await expect( - page - .locator('iframe[title="storybook-ref-storybook\\@7\\.6\\.18"]') - .contentFrame() - .locator('#storybook-root') - .getByText('Default') - ).toBeVisible({ timeout: 15000 }); - - await page.click('button[aria-label="Open navigation menu"]'); - - // Expect composed stories `to be available in the search - await page.getByPlaceholder('Find components').fill('Button primary'); - await expect( - page.getByRole('option', { - name: 'Primary Storybook 7.6.18 / @components / Button', - }) - ).toBeVisible(); - - const buttonStory = page.getByRole('option', { - name: 'Primary Storybook 8.0.0 / @blocks / examples / Button', - }); - await expect(buttonStory).toBeVisible(); - await buttonStory.click(); - - // Note: this could potentially be flaky due to it accessing a hosted Storybook - await expect( - page - .locator('iframe[title="storybook-ref-storybook\\@8\\.0\\.0"]') - .contentFrame() - .getByRole('button') - ).toBeVisible({ timeout: 15000 }); - }); -}); diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/e2e-tests/save-from-controls.spec.ts b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/e2e-tests/save-from-controls.spec.ts deleted file mode 100644 index 6ceb1d0e89f5..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/e2e-tests/save-from-controls.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { expect, test } from "@playwright/test"; -import process from "process"; - -import { SbPage } from "../../../../code/e2e-sandbox/util"; - -const STORYBOOK_URL = "http://localhost:6006"; -const type = process.env.STORYBOOK_TYPE || "dev"; - -test.describe("save-from-controls", () => { - test.describe.configure({ mode: "serial" }); - test.skip( - type === "build", - `Skipping save-from-controls tests for production Storybooks` - ); - - test("Should be able to update a story", async ({ page, browserName }) => { - // this is needed because the e2e test will generate a new file in the system - // which we don't know of its location (it runs in different sandboxes) - // so we just create a random id to make it easier to run tests - const id = Math.random().toString(36).substring(7); - - test.skip( - browserName !== "chromium", - `Skipping save-from-controls tests for ${browserName}` - ); - - await page.goto(STORYBOOK_URL + "/?path=/story/example-mybutton--primary"); - const sbPage = new SbPage(page, expect); - await sbPage.waitUntilLoaded(); - - await sbPage.viewAddonPanel("Controls"); - - // Update an arg - const label = sbPage.panelContent().locator("textarea[name=children]"); - await label.fill(`"Updated ${id}"`); - await label.blur(); - - // Assert the footer is shown - await sbPage - .panelContent() - .locator('[data-short-label="Unsaved changes"]') - .isVisible(); - - // update the story - await sbPage - .panelContent() - .locator("button") - .getByText("Update story") - .click(); - - // Assert the file is saved - const notification1 = sbPage.page.getByTitle("Story saved"); - await expect(notification1).toBeVisible(); - - // dismiss - await notification1.click(); - await notification1.isHidden(); - - // Update an arg - const newStoryValue = `"Copied ${id}"`; - await label.fill(newStoryValue); - await label.blur(); - - // Assert the footer is shown - await sbPage - .panelContent() - .locator('[data-short-label="Unsaved changes"]') - .isVisible(); - - const buttons = sbPage - .panelContent() - .locator('[aria-label="Create new story with these settings"]'); - - // clone the story - await buttons.click(); - - await sbPage.page - .getByPlaceholder("Story export name") - .fill("ClonedStory" + id); - await sbPage.page.getByRole("button", { exact: true, name: "Create" }).click(); - - // Assert the file is saved - const notification2 = sbPage.page.getByTitle("Story created"); - await expect(notification2).toBeVisible(); - await notification2.click(); - - // Assert the Button components is rendered in the preview - await expect(sbPage.previewRoot()).toContainText(newStoryValue.replace(/"/g, '')); - }); -}); diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/package.json b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/package.json deleted file mode 100644 index 86675ed07e8d..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/package.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "name": "portable-stories-react-vitest-3", - "version": "0.0.0", - "private": true, - "scripts": { - "build": "tsc && vite build", - "dev": "vite", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "playwright-e2e": "playwright test -c playwright-e2e.config.ts", - "preview": "vite preview", - "storybook": "NODE_OPTIONS=\"--preserve-symlinks --preserve-symlinks-main\" storybook dev -p 6006", - "vitest": "SKIP_FAIL_ON_PURPOSE=true vitest run" - }, - "resolutions": { - "@playwright/test": "1.58.2", - "@storybook/addon-a11y": "file:../../../code/addons/a11y", - "@storybook/addon-docs": "file:../../../code/addons/docs", - "@storybook/addon-jest": "file:../../../code/addons/jest", - "@storybook/addon-links": "file:../../../code/addons/links", - "@storybook/addon-themes": "file:../../../code/addons/themes", - "@storybook/addon-vitest": "file:../../../code/addons/vitest", - "@storybook/angular": "file:../../../code/frameworks/angular", - "@storybook/builder-vite": "file:../../../code/builders/builder-vite", - "@storybook/builder-webpack5": "file:../../../code/builders/builder-webpack5", - "@storybook/codemod": "file:../../../code/lib/codemod", - "@storybook/core-webpack": "portal:../../../code/lib/core-webpack", - "@storybook/ember": "portal:../../../code/frameworks/ember", - "@storybook/html": "file:../../../code/renderers/html", - "@storybook/nextjs": "file:../../../code/frameworks/nextjs", - "@storybook/preact": "file:../../../code/renderers/preact", - "@storybook/preact-vite": "file:../../../code/frameworks/preact-vite", - "@storybook/preset-create-react-app": "file:../../../code/presets/create-react-app", - "@storybook/preset-react-webpack": "file:../../../code/presets/react-webpack", - "@storybook/preset-server-webpack": "file:../../../code/presets/server-webpack", - "@storybook/react": "file:../../../code/renderers/react", - "@storybook/react-dom-shim": "file:../../../code/lib/react-dom-shim", - "@storybook/react-vite": "file:../../../code/frameworks/react-vite", - "@storybook/react-webpack5": "file:../../../code/frameworks/react-webpack5", - "@storybook/server": "file:../../../code/renderers/server", - "@storybook/server-webpack5": "file:../../../code/frameworks/server-webpack5", - "@storybook/svelte": "file:../../../code/renderers/svelte", - "@storybook/svelte-vite": "file:../../../code/frameworks/svelte-vite", - "@storybook/sveltekit": "file:../../../code/frameworks/sveltekit", - "@storybook/vue3": "file:../../../code/renderers/vue3", - "@storybook/vue3-vite": "file:../../../code/frameworks/vue3-vite", - "@storybook/web-components": "file:../../../code/renderers/web-components", - "@storybook/web-components-vite": "file:../../../code/frameworks/web-components-vite", - "eslint-plugin-storybook": "file:../../../code/lib/eslint-plugin", - "playwright": "1.58.2", - "storybook": "portal:../../../code/core" - }, - "dependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - }, - "devDependencies": { - "@playwright/test": "1.58.2", - "@storybook/addon-a11y": "^8.0.0", - "@storybook/addon-vitest": "^8.0.0", - "@storybook/react": "^8.0.0", - "@storybook/react-vite": "^8.0.0", - "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.2.0", - "@types/identity-obj-proxy": "^3", - "@types/react": "^19.0.8", - "@types/react-dom": "^19.0.3", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", - "@vitejs/plugin-react": "^4.2.1", - "@vitest/browser": "^3.2.4", - "@vitest/coverage-v8": "^3.2.4", - "@vitest/ui": "^3.2.4", - "eslint": "^8.56.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.5", - "eslint-plugin-storybook": "^0.11.4", - "identity-obj-proxy": "^3.0.0", - "storybook": "^8.0.0", - "typescript": "^5.8.3", - "vite": "^5.1.1", - "vitest": "^3.2.4" - }, - "nx": { - "includedScripts": [] - } -} diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/playwright-e2e.config.ts b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/playwright-e2e.config.ts deleted file mode 100644 index c1b06b44c09b..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/playwright-e2e.config.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { defineConfig, devices } from "@playwright/test"; -import path from "node:path"; - -/** - * See https://playwright.dev/docs/test-configuration. - */ -export default defineConfig({ - testDir: "./e2e-tests", - outputDir: "./test-results", - /* Maximum time one test can run for. */ - timeout: (process.env.CI ? 60 : 30) * 1000, - /* Run tests in files in parallel */ - fullyParallel: false, - - /* Fail the build on CI if you accidentally left test.only in the source code. */ - forbidOnly: !!process.env.CI, - retries: 0, - /* Run tests serially to avoid side effects */ - workers: 1, - /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: [ - ["line"], - [ - "junit", - { - embedAnnotationsAsProperties: true, - outputFile: path.join(__dirname, "..", "..", "..", "test-results", "react-vitest3-e2e-ui.xml"), - }, - ], - ], - /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ - use: { - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: "retain-on-failure", - // video: "retain-on-failure", - // headless: false, - }, - - /* Configure projects for major browsers */ - projects: [ - { - name: "chromium", - use: { ...devices["Desktop Chrome"] }, - }, - // { - // name: 'firefox', - // use: { ...devices['Desktop Firefox'] }, - // }, - // { - // name: 'webkit', - // use: { ...devices['Desktop Safari'] }, - // }, - ], - - webServer: { - command: "yarn storybook", - url: "http://127.0.0.1:6006", - reuseExistingServer: true, - }, -}); diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/pre-e2e.js b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/pre-e2e.js deleted file mode 100644 index a684b479b9ce..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/pre-e2e.js +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint-disable no-undef */ -/* eslint-disable @typescript-eslint/no-var-requires */ -const fs = require("node:fs"); -const path = require("node:path"); - -const testStoryPath = path.resolve("stories/AddonTest.stories.tsx"); - -console.log(`Pre-e2e script: clearing ${testStoryPath}`); -const storyContent = fs.readFileSync(testStoryPath).toString(); -fs.writeFileSync(testStoryPath, storyContent.replace("forceFailure: true", "forceFailure: false")); \ No newline at end of file diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/project.json b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/project.json deleted file mode 100644 index d1879b795c01..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/project.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "portable-stories-kitchen-sink/react-vitest-3", - "$schema": "../../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "targets": { - "e2e-ui": {} - }, - "tags": ["ci:normal", "ci:merged", "ci:daily"] -} diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/AddonTest.stories.tsx b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/AddonTest.stories.tsx deleted file mode 100644 index fcb59a0ff94e..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/AddonTest.stories.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { expect, within } from 'storybook/test'; -import { Meta, type StoryObj } from '@storybook/react-vite'; -import { instrument } from 'storybook/internal/instrumenter'; -import { Component } from './AddonTest'; - -declare global { - // eslint-disable-next-line no-var - var __vitest_browser__: boolean; -} - -const meta = { - title: 'Addons/Group/Test', - component: Component, -} as Meta; - -export default meta; - -type Story = StoryObj; - -const { pass } = instrument({ - pass: async () => {}, -}, { intercept: true }) - -export const ExpectedFailure: Story = { - args: { - forceFailure: false, - }, - play: async ({ args }) => { - await pass(); - if(args.forceFailure) { - throw new Error('Expected failure'); - } - } -}; - -export const ExpectedSuccess: Story = { - play: async () => { - await pass(); - } -}; - -export const ExpectedContent: Story = { - play: async ({ canvasElement }) => { - const button = within(canvasElement).getByRole('button'); - await expect(button).toHaveTextContent('test'); - const decoratorString = within(canvasElement).getByTestId('decorator-string'); - await expect(decoratorString).toHaveTextContent('Global Decorator'); - } -}; - -export const LongRunning: Story = { - loaders: [async () => new Promise((resolve) => setTimeout(resolve, 800))], -}; - -// Tests will pass in browser, but fail in CLI -export const MismatchFailure: Story = { - play: async () => { - await pass(); - if(!globalThis.__vitest_browser__) { - throw new Error('Expected failure'); - } - } -}; - -// Tests will fail in browser, but pass in CLI -export const MismatchSuccess: Story = { - play: async () => { - await pass(); - if(globalThis.__vitest_browser__) { - throw new Error('Unexpected success'); - } - }, - tags: ['fail-on-purpose'], -}; - -export const PreviewHeadTest: Story = { - play: async () => { - const styles = window.getComputedStyle(document.body); - // set in preview-head.html - expect(styles.backgroundColor).toBe('rgb(250, 250, 210)'); - // set in main.js#previewHead - expect(styles.borderColor).toBe('rgb(255, 0, 0)'); - } -}; - -export const StaticDirTest: Story = { - play: async () => { - const path = '/test-static-dirs/static.js'; - const { staticFunction } = await import(/* @vite-ignore */path); - expect(staticFunction()).toBe(true); - } -} - -export const ViteFinalTest: Story = { - play: async () => { - // @ts-expect-error TS doesn't know about the alias - const { aliasedFunction } = await import('test-alias'); - expect(aliasedFunction()).toBe(true); - } -} diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/AddonTest.tsx b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/AddonTest.tsx deleted file mode 100644 index 01850675e5c7..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/AddonTest.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { getButtonString } from './get-button-string'; - -export const Component = () => { - return ; -}; diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/Button.stories.tsx b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/Button.stories.tsx deleted file mode 100644 index c40e163b0d32..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/Button.stories.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { expect, fn } from 'storybook/test'; -import type { Meta, StoryFn as CSF2Story, StoryObj as CSF3Story } from '@storybook/react-vite'; - -import type { ButtonProps } from './Button'; -import { Button } from './Button'; - -const meta = { - title: 'Example/Button', - component: Button, - argTypes: { - backgroundColor: { control: 'color' }, - }, -} satisfies Meta; - -export default meta; - -const Template: CSF2Story = (args) => - - ); -}; -CSF2StoryWithLocale.storyName = 'WithLocale'; - -export const CSF2StoryWithParamsAndDecorator: CSF2Story = (args) => { - return - ); -}; diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/MyButton.stories.tsx b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/MyButton.stories.tsx deleted file mode 100644 index d79c54c2f1c0..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/MyButton.stories.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { Meta, StoryObj as CSF3Story } from '@storybook/react-vite'; - -import type { ButtonProps } from './Button'; -import { Button } from './Button'; - -const meta = { - title: 'Example/MyButton', - component: Button, - tags: ['!test'], - argTypes: { - backgroundColor: { control: 'color' }, - }, -} satisfies Meta; - -export default meta; - -export const Primary: CSF3Story = { - args: { children: 'foo' }, -}; diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/OtherComponent.stories.tsx b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/OtherComponent.stories.tsx deleted file mode 100644 index cf35c3f8e98e..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/OtherComponent.stories.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Meta, type StoryObj } from '@storybook/react-vite'; - -const Component = () => - -const meta = { - title: 'Addons/Group/Other', - component: Component, -} as Meta; - -export default meta; - -type Story = StoryObj; - -export const Passes: Story = { -}; - -export const Fails: Story = { - play: async () => { - throw new Error('Expected failure'); - }, - tags: ['fail-on-purpose'], -}; diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/UnhandledErrors.stories.tsx b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/UnhandledErrors.stories.tsx deleted file mode 100644 index 49a5db3a6ba0..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/UnhandledErrors.stories.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react-vite'; - -async function unhandledRejection() { - throwError('I THREW AN UNHANDLED REJECTION!'); -} - -function unhandledError() { - throwError('I THREW AN UNHANDLED ERROR!'); -} - -function throwError(message: string) { - throw new Error(message); -} -const meta = { - title: 'Example/UnhandledErrors', - args: { - errorType: null, - forceFailure: false, - }, - component: ({ errorType, forceFailure }) => { - if (forceFailure) { - if (errorType === 'rejection') { - setTimeout(unhandledRejection, 0); - } else if (errorType === 'error') { - setTimeout(unhandledError, 0); - } - } - return 'Hello world'; - }, -} as Meta<{ errorType: 'rejection' | 'error' | null; forceFailure?: boolean }>; -export default meta; -type Story = StoryObj; - -export const UnhandledError: Story = { - args: { - errorType: 'error', - }, -}; - -export const UnhandledRejection: Story = { - args: { - errorType: 'rejection', - }, -}; - -export const Success: Story = {}; diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/button.css b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/button.css deleted file mode 100644 index dc91dc76370b..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/button.css +++ /dev/null @@ -1,30 +0,0 @@ -.storybook-button { - font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; - font-weight: 700; - border: 0; - border-radius: 3em; - cursor: pointer; - display: inline-block; - line-height: 1; -} -.storybook-button--primary { - color: white; - background-color: #1ea7fd; -} -.storybook-button--secondary { - color: #333; - background-color: transparent; - box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; -} -.storybook-button--small { - font-size: 12px; - padding: 10px 16px; -} -.storybook-button--medium { - font-size: 14px; - padding: 11px 20px; -} -.storybook-button--large { - font-size: 16px; - padding: 12px 24px; -} diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/get-button-string.ts b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/get-button-string.ts deleted file mode 100644 index 7c9d9ab153d0..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/stories/get-button-string.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const getButtonString = () => { - return "test"; -}; diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/tsconfig.json b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/tsconfig.json deleted file mode 100644 index 0bcaa35b673d..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["stories", "e2e-tests"], - "references": [{ "path": "./tsconfig.node.json" }] -} diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/tsconfig.node.json b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/tsconfig.node.json deleted file mode 100644 index 97ede7ee6f2d..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/tsconfig.node.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "skipLibCheck": true, - "module": "ESNext", - "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true, - "strict": true - }, - "include": ["vite.config.ts"] -} diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/vite.config.ts b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/vite.config.ts deleted file mode 100644 index 9cc50ead1c0a..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/vite.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react"; - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], -}); diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/vitest.workspace.ts b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/vitest.workspace.ts deleted file mode 100644 index d09ebcb58a9b..000000000000 --- a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/vitest.workspace.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { defineWorkspace } from "vitest/config"; -import { storybookTest } from "@storybook/addon-vitest/vitest-plugin"; - -export default defineWorkspace([ - { - extends: "vite.config.ts", - plugins: [ - storybookTest( - process.env.SKIP_FAIL_ON_PURPOSE - ? { - tags: { - exclude: ["fail-on-purpose"], - }, - } - : undefined - ), - ], - test: { - name: "storybook", - pool: "threads", - deps: { - optimizer: { - web: { - enabled: false, - }, - }, - }, - browser: { - enabled: true, - provider: "playwright", - headless: true, - instances: [ - { - browser: "chromium", - }, - ], - }, - setupFiles: ["./.storybook/vitest.setup.ts"], - environment: "jsdom", - }, - }, -]); diff --git a/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/yarn.lock b/test-storybooks/portable-stories-kitchen-sink/react-vitest-3/yarn.lock deleted file mode 100644 index e69de29bb2d1..000000000000