;
-}> = ({ context, state }) => {
- const api = useStorybookApi();
- const [isDisabled, setDisabled] = useState(false);
-
- const id = useRef(context.id);
- id.current = context.id;
-
- const Icon = state.running ? StopAltHollowIcon : PlayHollowIcon;
-
- useEffect(() => {
- setDisabled(false);
- }, [state.running]);
-
- const onClick = useCallback(
- (event: SyntheticEvent) => {
- setDisabled(true);
- event.stopPropagation();
- if (state.running) {
- api.cancelTestProvider(TEST_PROVIDER_ID);
- } else {
- api.runTestProvider(TEST_PROVIDER_ID, { entryId: id.current });
- }
- },
- [api, state.running]
- );
-
- const theme = useTheme();
-
- return (
- {
- // stopPropagation to prevent the parent from closing the context menu, which is the default behavior onClick
- event.stopPropagation();
- }}
- >
- }
- center={}
- right={
-
- }
- />
-
- );
-};
diff --git a/code/addons/test/src/components/Description.tsx b/code/addons/test/src/components/Description.tsx
index 58a80dbfdccc..3779ab641934 100644
--- a/code/addons/test/src/components/Description.tsx
+++ b/code/addons/test/src/components/Description.tsx
@@ -4,6 +4,7 @@ import { Link as LinkComponent } from 'storybook/internal/components';
import { type TestProviderConfig, type TestProviderState } from 'storybook/internal/core-events';
import { styled } from 'storybook/internal/theming';
+import type { TestResultResult } from '../node/reporter';
import { GlobalErrorContext } from './GlobalErrorModal';
import { RelativeTime } from './RelativeTime';
@@ -19,11 +20,13 @@ const PositiveText = styled.span(({ theme }) => ({
color: theme.color.positiveText,
}));
-interface DescriptionProps extends ComponentProps {
+interface DescriptionProps extends Omit, 'results'> {
state: TestProviderConfig & TestProviderState;
+ entryId?: string;
+ results?: TestResultResult[];
}
-export function Description({ state, ...props }: DescriptionProps) {
+export function Description({ state, entryId, results, ...props }: DescriptionProps) {
const isMounted = React.useRef(false);
const [isUpdated, setUpdated] = React.useState(false);
const { setModalOpen } = React.useContext(GlobalErrorContext);
@@ -48,15 +51,17 @@ export function Description({ state, ...props }: DescriptionProps) {
description = state.progress
? `Testing... ${state.progress.numPassedTests}/${state.progress.numTotalTests}`
: 'Starting...';
+ } else if (entryId && results?.length) {
+ description = `Ran ${results.length} ${results.length === 1 ? 'test' : 'tests'}`;
} else if (state.failed && !errorMessage) {
description = 'Failed';
} else if (state.crashed || (state.failed && errorMessage)) {
- description = (
- <>
- setModalOpen(true)}>
- {state.error?.name || 'View full error'}
-
- >
+ description = setModalOpen ? (
+ setModalOpen(true)}>
+ {state.error?.name || 'View full error'}
+
+ ) : (
+ state.error?.name || 'Failed'
);
} else if (state.progress?.finishedAt) {
description = (
diff --git a/code/addons/test/src/components/TestProviderRender.tsx b/code/addons/test/src/components/TestProviderRender.tsx
index 139d114047bc..dd410ae22022 100644
--- a/code/addons/test/src/components/TestProviderRender.tsx
+++ b/code/addons/test/src/components/TestProviderRender.tsx
@@ -37,7 +37,6 @@ import { type Config, type Details, PANEL_ID } from '../constants';
import { type TestStatus } from '../node/reporter';
import { Description } from './Description';
import { TestStatusIcon } from './TestStatusIcon';
-import { Title } from './Title';
const Container = styled.div({
display: 'flex',
@@ -58,6 +57,12 @@ const Info = styled.div({
minWidth: 0,
});
+const Title = styled.div<{ crashed?: boolean }>(({ crashed, theme }) => ({
+ fontSize: theme.typography.size.s1,
+ fontWeight: crashed ? 'bold' : 'normal',
+ color: crashed ? theme.color.negativeText : theme.color.defaultText,
+}));
+
const Actions = styled.div({
display: 'flex',
gap: 2,
@@ -98,7 +103,7 @@ const statusMap: Record['statu
warning: 'warning',
passed: 'positive',
skipped: 'unknown',
- pending: 'unknown',
+ pending: 'pending',
};
export const TestProviderRender: FC<
@@ -191,11 +196,7 @@ export const TestProviderRender: FC<
})
.sort((a, b) => statusOrder.indexOf(a.status) - statusOrder.indexOf(b.status));
- const status = state.running
- ? 'unknown'
- : state.failed
- ? 'failed'
- : (results[0]?.status ?? 'unknown');
+ const status = results[0]?.status ?? (state.running ? 'pending' : 'unknown');
const openPanel = (id: string, panelId: string) => {
api.selectStory(id);
@@ -207,8 +208,15 @@ export const TestProviderRender: FC<
-
-
+
+ {state.crashed ? 'Local tests failed' : 'Run local tests'}
+
+
diff --git a/code/addons/test/src/components/TestStatusIcon.stories.tsx b/code/addons/test/src/components/TestStatusIcon.stories.tsx
index 3a38df50a801..e46d22e015d6 100644
--- a/code/addons/test/src/components/TestStatusIcon.stories.tsx
+++ b/code/addons/test/src/components/TestStatusIcon.stories.tsx
@@ -16,6 +16,12 @@ export const Unknown: Story = {
},
};
+export const Pending: Story = {
+ args: {
+ status: 'pending',
+ },
+};
+
export const Positive: Story = {
args: {
status: 'positive',
diff --git a/code/addons/test/src/components/TestStatusIcon.tsx b/code/addons/test/src/components/TestStatusIcon.tsx
index 7b201ce768c5..536b603dbfba 100644
--- a/code/addons/test/src/components/TestStatusIcon.tsx
+++ b/code/addons/test/src/components/TestStatusIcon.tsx
@@ -1,7 +1,7 @@
import { styled } from 'storybook/internal/theming';
export const TestStatusIcon = styled.div<{
- status: 'positive' | 'warning' | 'negative' | 'critical' | 'unknown';
+ status: 'pending' | 'positive' | 'warning' | 'negative' | 'critical' | 'unknown';
percentage?: number;
}>(
({ percentage }) => ({
@@ -13,6 +13,12 @@ export const TestStatusIcon = styled.div<{
: 'var(--status-color)',
borderRadius: '50%',
}),
+ ({ status, theme }) =>
+ status === 'pending' && {
+ animation: `${theme.animation.glow} 1.5s ease-in-out infinite`,
+ '--status-color': theme.color.mediumdark,
+ '--status-background': `${theme.color.mediumdark}66`,
+ },
({ status, theme }) =>
status === 'positive' && {
'--status-color': theme.color.positive,
diff --git a/code/addons/test/src/components/Title.tsx b/code/addons/test/src/components/Title.tsx
deleted file mode 100644
index fecb454cffd8..000000000000
--- a/code/addons/test/src/components/Title.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import React, { type ComponentProps } from 'react';
-
-import { type TestProviderConfig, type TestProviderState } from 'storybook/internal/core-events';
-import { styled } from 'storybook/internal/theming';
-
-const Wrapper = styled.div<{ crashed?: boolean }>(({ crashed, theme }) => ({
- fontSize: theme.typography.size.s1,
- fontWeight: crashed ? 'bold' : 'normal',
- color: crashed ? theme.color.negativeText : theme.color.defaultText,
-}));
-
-export const Title = ({
- state,
- ...props
-}: { state: TestProviderConfig & TestProviderState } & ComponentProps) => {
- return (
-
- {state.crashed || state.failed ? 'Local tests failed' : 'Run local tests'}
-
- );
-};
diff --git a/code/core/src/manager/components/sidebar/ContextMenu.tsx b/code/core/src/manager/components/sidebar/ContextMenu.tsx
index ac4aeb671d55..3392c7674eed 100644
--- a/code/core/src/manager/components/sidebar/ContextMenu.tsx
+++ b/code/core/src/manager/components/sidebar/ContextMenu.tsx
@@ -22,6 +22,7 @@ const empty = {
const PositionedWithTooltip = styled(WithTooltip)({
position: 'absolute',
right: 0,
+ zIndex: 1,
});
const FloatingStatusButton = styled(StatusButton)({
From 55240ff73d7095ddf0e787fc969ed1d3229eb607 Mon Sep 17 00:00:00 2001
From: Gert Hengeveld
Date: Mon, 23 Dec 2024 15:43:04 +0100
Subject: [PATCH 5/7] Update tests
---
.../components/TestProviderRender.stories.tsx | 2 +-
.../react/e2e-tests/component-testing.spec.ts | 26 +++++++++----------
2 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/code/addons/test/src/components/TestProviderRender.stories.tsx b/code/addons/test/src/components/TestProviderRender.stories.tsx
index 1189248f3d53..ebee7bf6a279 100644
--- a/code/addons/test/src/components/TestProviderRender.stories.tsx
+++ b/code/addons/test/src/components/TestProviderRender.stories.tsx
@@ -233,7 +233,7 @@ export const Editing: Story = {
play: async ({ canvasElement }) => {
const screen = within(canvasElement);
- screen.getByLabelText(/Open settings/).click();
+ screen.getByLabelText(/Show settings/).click();
},
};
diff --git a/test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts b/test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
index 9e88b7d0a579..ada0a6001721 100644
--- a/test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
+++ b/test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
@@ -137,7 +137,7 @@ test.describe("component testing", () => {
await expect(testingModuleDescription).toContainText('Not run');
const runTestsButton = await page.getByLabel('Start Component Tests')
- const watchModeButton = await page.getByLabel('Enable watch mode for Component tests')
+ const watchModeButton = await page.getByLabel('Enable watch mode')
await expect(runTestsButton).toBeEnabled();
await expect(watchModeButton).toBeEnabled();
@@ -201,7 +201,7 @@ test.describe("component testing", () => {
.getByRole("button", { name: "test" });
await expect(storyElement).toBeVisible({ timeout: 30000 });
- await page.getByLabel("Enable watch mode for Component tests").click();
+ await page.getByLabel("Enable 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);
@@ -209,7 +209,7 @@ test.describe("component testing", () => {
await page.waitForTimeout(500);
// Cleanup, to ensure watch mode is disabled in the other tests
- await page.getByLabel("Disable watch mode for Component tests").click();
+ await page.getByLabel("Disable watch mode").click();
// Wait for test results to appear
const errorFilter = page.getByLabel("Toggle errors");
@@ -265,10 +265,10 @@ test.describe("component testing", () => {
await expect(page.getByLabel("Open coverage report")).toHaveCount(0);
// Act - Enable coverage and run tests
- await page.getByLabel("Open settings for Component tests").click();
+ await page.getByLabel("Show settings").click();
await page.getByLabel("Coverage").click();
await expect(page.getByText("Settings updated")).toBeVisible({ timeout: 3000 });
- await page.getByLabel("Close settings for Component tests").click();
+ await page.getByLabel("Hide settings").click();
// Wait for Vitest to have (re)started
await page.waitForTimeout(2000);
@@ -296,7 +296,7 @@ test.describe("component testing", () => {
// Cleanup - Disable coverage again
await page.goBack();
await expandButton.click();
- await page.getByLabel("Open settings for Component tests").click();
+ await page.getByLabel("Show settings").click();
await page.getByLabel("Coverage").click();
await expect(page.getByText("Settings updated")).toBeVisible({ timeout: 3000 });
});
@@ -425,10 +425,10 @@ test.describe("component testing", () => {
await expect(storyElement).toBeVisible({ timeout: 30000 });
// Act - Enable coverage
- await page.getByLabel("Open settings for Component tests").click();
+ await page.getByLabel("Show settings").click();
await page.getByLabel("Coverage").click();
await expect(page.getByText("Settings updated")).toBeVisible({ timeout: 3000 });
- await page.getByLabel("Close settings for Component tests").click();
+ await page.getByLabel("Hide settings").click();
// Wait for Vitest to have (re)started
await page.waitForTimeout(2000);
@@ -437,20 +437,20 @@ test.describe("component testing", () => {
await page.locator('[data-item-id="example-button--csf-3-primary"] div[data-testid="context-menu"] button').click();
const sidebarContextMenu = page.getByTestId('tooltip');
await sidebarContextMenu.getByLabel('Start Component tests').click();
-
+
// Arrange - Wait for test to finish and unfocus sidebar context menu
await expect(sidebarContextMenu.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 Component tests").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(/percent coverage$/).textContent();
From a0702ec56490c71e6127028386fb9c173820e125 Mon Sep 17 00:00:00 2001
From: Gert Hengeveld
Date: Mon, 23 Dec 2024 16:29:39 +0100
Subject: [PATCH 6/7] Use fireEvent rather than userEvent
---
.../components/sidebar/SidebarBottom.stories.tsx | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/code/core/src/manager/components/sidebar/SidebarBottom.stories.tsx b/code/core/src/manager/components/sidebar/SidebarBottom.stories.tsx
index 22cf6e68ef96..d8d0936d5f27 100644
--- a/code/core/src/manager/components/sidebar/SidebarBottom.stories.tsx
+++ b/code/core/src/manager/components/sidebar/SidebarBottom.stories.tsx
@@ -1,11 +1,10 @@
-import React, { type FC, Fragment, useEffect, useState } from 'react';
+import React, { type FC, useEffect, useState } from 'react';
import { Addon_TypesEnum } from '@storybook/core/types';
import type { Meta, StoryObj } from '@storybook/react';
-import { expect, fn, waitFor, within } from '@storybook/test';
+import { expect, fireEvent, fn, waitFor, within } from '@storybook/test';
import { type API, ManagerContext } from '@storybook/core/manager-api';
-import { userEvent } from '@storybook/testing-library';
import { SidebarBottomBase } from './SidebarBottom';
@@ -156,18 +155,18 @@ export const DynamicHeight: StoryObj = {
const screen = await within(canvasElement);
const toggleButton = await screen.getByLabelText(/Expand/);
- await userEvent.click(toggleButton);
+ await fireEvent.click(toggleButton);
const content = await screen.findByText('CUSTOM CONTENT WITH DYNAMIC HEIGHT');
const collapse = await screen.getByTestId('collapse');
await expect(content).toBeVisible();
- await userEvent.click(toggleButton);
+ await fireEvent.click(toggleButton);
await waitFor(() => expect(collapse.getBoundingClientRect()).toHaveProperty('height', 0));
- await userEvent.click(toggleButton);
+ await fireEvent.click(toggleButton);
await waitFor(() => expect(collapse.getBoundingClientRect()).not.toHaveProperty('height', 0));
},
From b82d3b0eee82aaf52049e8155233dde78d9eb009 Mon Sep 17 00:00:00 2001
From: Gert Hengeveld
Date: Mon, 23 Dec 2024 16:56:03 +0100
Subject: [PATCH 7/7] Update E2E tests
---
.../react/e2e-tests/component-testing.spec.ts | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts b/test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
index ada0a6001721..c9ddd5ceba4a 100644
--- a/test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
+++ b/test-storybooks/portable-stories-kitchen-sink/react/e2e-tests/component-testing.spec.ts
@@ -74,7 +74,7 @@ test.describe("component testing", () => {
await expect(testingModuleDescription).toContainText('Not run');
- const runTestsButton = await page.getByLabel('Start Component tests')
+ const runTestsButton = await page.getByLabel('Start test run')
await runTestsButton.click();
await expect(testingModuleDescription).toContainText('Testing', { timeout: 60000 });
@@ -136,7 +136,7 @@ test.describe("component testing", () => {
await expect(testingModuleDescription).toContainText('Not run');
- const runTestsButton = await page.getByLabel('Start Component Tests')
+ const runTestsButton = await page.getByLabel('Start test run')
const watchModeButton = await page.getByLabel('Enable watch mode')
await expect(runTestsButton).toBeEnabled();
await expect(watchModeButton).toBeEnabled();
@@ -272,7 +272,7 @@ test.describe("component testing", () => {
// Wait for Vitest to have (re)started
await page.waitForTimeout(2000);
- await page.getByLabel("Start Component tests").click();
+ await page.getByLabel("Start test run").click();
// Assert - Coverage report is collected and shown
await expect(page.getByLabel("Open coverage report")).toBeVisible({ timeout: 30000 });
@@ -324,7 +324,7 @@ test.describe("component testing", () => {
await page.locator('[data-item-id="addons-group-test--expected-failure"]').hover();
await page.locator('[data-item-id="addons-group-test--expected-failure"] div[data-testid="context-menu"] button').click();
const sidebarContextMenu = page.getByTestId('tooltip');
- await sidebarContextMenu.getByLabel('Start Component tests').click();
+ 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 });
@@ -356,7 +356,7 @@ test.describe("component testing", () => {
await page.locator('[data-item-id="addons-group-test"]').hover();
await page.locator('[data-item-id="addons-group-test"] div[data-testid="context-menu"] button').click();
const sidebarContextMenu = page.getByTestId('tooltip');
- await sidebarContextMenu.getByLabel('Start Component tests').click();
+ await sidebarContextMenu.getByLabel('Start test run').click();
// Assert - Tests are running and reported
await expect(sidebarContextMenu.locator('#testing-module-description')).toContainText('Ran 8 tests', { timeout: 30000 });
@@ -392,7 +392,7 @@ test.describe("component testing", () => {
await page.locator('[data-item-id="addons-group"]').hover();
await page.locator('[data-item-id="addons-group"] div[data-testid="context-menu"] button').click();
const sidebarContextMenu = page.getByTestId('tooltip');
- await sidebarContextMenu.getByLabel('Start Component tests').click();
+ await sidebarContextMenu.getByLabel('Start test run').click();
// Assert - Tests are running and reported
await expect(sidebarContextMenu.locator('#testing-module-description')).toContainText('Ran 10 test', { timeout: 30000 });
@@ -436,7 +436,7 @@ test.describe("component testing", () => {
await page.locator('[data-item-id="example-button--csf-3-primary"]').hover();
await page.locator('[data-item-id="example-button--csf-3-primary"] div[data-testid="context-menu"] button').click();
const sidebarContextMenu = page.getByTestId('tooltip');
- await sidebarContextMenu.getByLabel('Start Component tests').click();
+ await sidebarContextMenu.getByLabel('Start test run').click();
// Arrange - Wait for test to finish and unfocus sidebar context menu
await expect(sidebarContextMenu.locator('#testing-module-description')).toContainText('Ran 1 test', { timeout: 30000 });
@@ -446,7 +446,7 @@ test.describe("component testing", () => {
await expect(page.getByLabel("Open coverage report")).not.toBeVisible();
// Act - Run ALL tests
- await page.getByLabel("Start Component tests").click();
+ 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 });