diff --git a/code/addons/test/src/components/TestProviderRender.stories.tsx b/code/addons/test/src/components/TestProviderRender.stories.tsx index 019939ab181..fa5456ce69c 100644 --- a/code/addons/test/src/components/TestProviderRender.stories.tsx +++ b/code/addons/test/src/components/TestProviderRender.stories.tsx @@ -14,15 +14,6 @@ import { store as mockStore } from '../manager-store.mock'; import { TestProviderRender } from './TestProviderRender'; const managerContext: any = { - state: { - testProviders: { - 'test-provider-id': { - id: 'test-provider-id', - name: 'Test Provider', - type: Addon_TypesEnum.experimental_TEST_PROVIDER, - }, - }, - }, api: { getDocsUrl: fn(({ subpath }) => `https://storybook.js.org/docs/${subpath}`).mockName( 'api::getDocsUrl' diff --git a/code/addons/test/src/manager.tsx b/code/addons/test/src/manager.tsx index 506acfff020..3b68403057a 100644 --- a/code/addons/test/src/manager.tsx +++ b/code/addons/test/src/manager.tsx @@ -46,8 +46,6 @@ addons.register(ADDON_ID, (api) => { addons.add(TEST_PROVIDER_ID, { type: Addon_TypesEnum.experimental_TEST_PROVIDER, - runnable: true, - name: 'Component tests', render: () => { const [isModalOpen, setModalOpen] = useState(false); const { @@ -94,6 +92,6 @@ addons.register(ADDON_ID, (api) => { } return ; }, - } satisfies Omit); + }); } }); diff --git a/code/core/src/core-events/data/testing-module.ts b/code/core/src/core-events/data/testing-module.ts deleted file mode 100644 index 8814c3d09e8..00000000000 --- a/code/core/src/core-events/data/testing-module.ts +++ /dev/null @@ -1,73 +0,0 @@ -import type { Addon_TestProviderState, Addon_TestProviderType } from 'storybook/internal/types'; - -type DateNow = number; - -export type TestProviderId = Addon_TestProviderType['id']; -export type TestProviderConfig = Addon_TestProviderType; -export type TestProviderState
> = - Addon_TestProviderState
; - -export type TestProviders = Record; - -export type TestingModuleRunRequestPayload = { - providerId: TestProviderId; - // TODO: Avoid needing to do a fetch request server-side to retrieve the index - indexUrl: string; // e.g. http://localhost:6006/index.json - storyIds?: string[]; // ['button--primary', 'button--secondary'] -}; - -export type TestingModuleProgressReportPayload = - | { - providerId: TestProviderId; - status: 'success' | 'pending' | 'cancelled'; - cancellable?: boolean; - progress?: TestingModuleProgressReportProgress; - details?: { [key: string]: any }; - } - | { - providerId: TestProviderId; - status: 'failed'; - progress?: TestingModuleProgressReportProgress; - details?: { [key: string]: any }; - error: { - name: string; - message: string; - stack?: string; - }; - } - | { - providerId: TestProviderId; - details: { [key: string]: any }; - }; - -export type TestingModuleCrashReportPayload = { - providerId: TestProviderId; - error: { - message: string; - }; -}; - -export type TestingModuleProgressReportProgress = { - startedAt: DateNow; - finishedAt?: DateNow; - numTotalTests?: number; - numPassedTests?: number; - numFailedTests?: number; - numPendingTests?: number; - percentageCompleted?: number; -}; - -export type Status = 'success' | 'failed' | 'pending'; - -export type TestingModuleCancelTestRunRequestPayload = { - providerId: TestProviderId; -}; - -export type TestingModuleCancelTestRunResponsePayload = - | { - status: 'success'; - } - | { - status: 'failed'; - message: string; - }; diff --git a/code/core/src/core-events/index.ts b/code/core/src/core-events/index.ts index faad4b6ae09..1018576c25e 100644 --- a/code/core/src/core-events/index.ts +++ b/code/core/src/core-events/index.ts @@ -85,12 +85,6 @@ enum events { ARGTYPES_INFO_RESPONSE = 'argtypesInfoResponse', CREATE_NEW_STORYFILE_REQUEST = 'createNewStoryfileRequest', CREATE_NEW_STORYFILE_RESPONSE = 'createNewStoryfileResponse', - - TESTING_MODULE_CRASH_REPORT = 'testingModuleCrashReport', - TESTING_MODULE_PROGRESS_REPORT = 'testingModuleProgressReport', - TESTING_MODULE_RUN_REQUEST = 'testingModuleRunRequest', - TESTING_MODULE_CANCEL_TEST_RUN_REQUEST = 'testingModuleCancelTestRunRequest', - TESTING_MODULE_CANCEL_TEST_RUN_RESPONSE = 'testingModuleCancelTestRunResponse', } // Enables: `import Events from ...` @@ -155,11 +149,6 @@ export const { SAVE_STORY_RESPONSE, ARGTYPES_INFO_REQUEST, ARGTYPES_INFO_RESPONSE, - TESTING_MODULE_CRASH_REPORT, - TESTING_MODULE_PROGRESS_REPORT, - TESTING_MODULE_RUN_REQUEST, - TESTING_MODULE_CANCEL_TEST_RUN_REQUEST, - TESTING_MODULE_CANCEL_TEST_RUN_RESPONSE, } = events; export * from './data/create-new-story'; @@ -168,5 +157,4 @@ export * from './data/argtypes-info'; export * from './data/request-response'; export * from './data/save-story'; export * from './data/whats-new'; -export * from './data/testing-module'; export * from './data/phases'; diff --git a/code/core/src/core-server/presets/common-preset.ts b/code/core/src/core-server/presets/common-preset.ts index d8a561894d4..14689792902 100644 --- a/code/core/src/core-server/presets/common-preset.ts +++ b/code/core/src/core-server/presets/common-preset.ts @@ -24,13 +24,6 @@ import type { import { dedent } from 'ts-dedent'; -import { TEST_PROVIDER_ID as ADDON_TEST_PROVIDER_ID } from '../../../../addons/test/src/constants'; -import { - TESTING_MODULE_CRASH_REPORT, - TESTING_MODULE_PROGRESS_REPORT, - type TestingModuleCrashReportPayload, - type TestingModuleProgressReportPayload, -} from '../../core-events'; import { cleanPaths, sanitizeError } from '../../telemetry/sanitize'; import { initCreateNewStoryChannel } from '../server-channel/create-new-story-channel'; import { initFileSearchChannel } from '../server-channel/file-search-channel'; @@ -285,55 +278,6 @@ export const experimental_serverChannel = async ( initFileSearchChannel(channel, options, coreOptions); initCreateNewStoryChannel(channel, options, coreOptions); - if (!options.disableTelemetry) { - channel.on( - TESTING_MODULE_PROGRESS_REPORT, - async (payload: TestingModuleProgressReportPayload) => { - if (payload.providerId === ADDON_TEST_PROVIDER_ID) { - // addon-test does its own telemetry - return; - } - const status = 'status' in payload ? payload.status : undefined; - const progress = 'progress' in payload ? payload.progress : undefined; - const error = 'error' in payload ? payload.error : undefined; - - if ((status === 'success' || status === 'cancelled') && progress?.finishedAt) { - await telemetry('testing-module-completed-report', { - provider: payload.providerId, - duration: progress?.finishedAt - progress?.startedAt, - numTotalTests: progress?.numTotalTests, - numFailedTests: progress?.numFailedTests, - numPassedTests: progress?.numPassedTests, - status, - }); - } - - if (status === 'failed') { - await telemetry('testing-module-completed-report', { - provider: payload.providerId, - status: 'failed', - ...(options.enableCrashReports && { - error: error && sanitizeError(error), - }), - }); - } - } - ); - - channel.on(TESTING_MODULE_CRASH_REPORT, async (payload: TestingModuleCrashReportPayload) => { - if (payload.providerId === ADDON_TEST_PROVIDER_ID) { - // addon-test does its own telemetry - return; - } - await telemetry('testing-module-crash-report', { - provider: payload.providerId, - ...(options.enableCrashReports && { - error: cleanPaths(payload.error.message), - }), - }); - }); - } - return channel; }; diff --git a/code/core/src/manager-api/modules/experimental_testmodule.ts b/code/core/src/manager-api/modules/experimental_testmodule.ts deleted file mode 100644 index 6d3bc9df953..00000000000 --- a/code/core/src/manager-api/modules/experimental_testmodule.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { - TESTING_MODULE_CANCEL_TEST_RUN_REQUEST, - TESTING_MODULE_RUN_REQUEST, - type TestProviderId, - type TestProviderState, - type TestProviders, - type TestingModuleRunRequestPayload, -} from 'storybook/internal/core-events'; -import { Addon_TypesEnum, type StoryId } from 'storybook/internal/types'; - -import invariant from 'tiny-invariant'; - -import type { ModuleFn } from '../lib/types'; - -export type SubState = { - testProviders: TestProviders; -}; - -const initialTestProviderState: TestProviderState = { - details: {} as { [key: string]: any }, - cancellable: false, - cancelling: false, - running: false, - failed: false, - crashed: false, -}; - -interface RunOptions { - entryId?: StoryId; -} - -export type SubAPI = { - getTestProviderState(id: string): TestProviderState | undefined; - updateTestProviderState(id: TestProviderId, update: Partial): void; - clearTestProviderState(id: TestProviderId): void; - runTestProvider(id: TestProviderId, options?: RunOptions): () => void; - cancelTestProvider(id: TestProviderId): void; -}; - -export const init: ModuleFn = ({ store, fullAPI }) => { - const state: SubState = { - testProviders: store.getState().testProviders || {}, - }; - - const api: SubAPI = { - getTestProviderState(id) { - const { testProviders } = store.getState(); - return testProviders?.[id]; - }, - updateTestProviderState(id, update) { - return store.setState( - ({ testProviders }) => { - const currentState = testProviders[id]; - const updatedState = currentState.stateUpdater?.(currentState, update) ?? { - ...currentState, - ...update, - details: { ...currentState.details, ...update.details }, - }; - return { testProviders: { ...testProviders, [id]: updatedState } }; - }, - { persistence: 'session' } - ); - }, - clearTestProviderState(id) { - const update = { - cancelling: false, - running: false, - failed: false, - crashed: false, - progress: undefined, - }; - return store.setState( - ({ testProviders }) => { - return { testProviders: { ...testProviders, [id]: { ...testProviders[id], ...update } } }; - }, - { persistence: 'session' } - ); - }, - runTestProvider(id, options) { - const index = store.getState().index; - invariant(index, 'The index is currently unavailable'); - - api.updateTestProviderState(id, { - running: true, - failed: false, - crashed: false, - progress: undefined, - }); - - const indexUrl = new URL('index.json', window.location.href).toString(); - - if (!options?.entryId) { - const payload: TestingModuleRunRequestPayload = { - providerId: id, - indexUrl, - }; - - fullAPI.emit(TESTING_MODULE_RUN_REQUEST, payload); - - return () => api.cancelTestProvider(id); - } - - const entry = index[options.entryId]; - invariant(entry, `No entry found in the index for id '${options.entryId}'`); - - const findStories = (entryId: StoryId, results: StoryId[] = []): StoryId[] => { - const node = index[entryId]; - if (node.type === 'story') { - results.push(node.id); - } else if ('children' in node) { - node.children.forEach((childId) => findStories(childId, results)); - } - return results; - }; - - const payload: TestingModuleRunRequestPayload = { - providerId: id, - indexUrl, - storyIds: findStories(options.entryId), - }; - fullAPI.emit(TESTING_MODULE_RUN_REQUEST, payload); - return () => api.cancelTestProvider(id); - }, - cancelTestProvider(id) { - api.updateTestProviderState(id, { cancelling: true }); - fullAPI.emit(TESTING_MODULE_CANCEL_TEST_RUN_REQUEST, { providerId: id }); - }, - }; - - const initModule = async () => { - const initialState: TestProviders = Object.fromEntries( - Object.entries(fullAPI.getElements(Addon_TypesEnum.experimental_TEST_PROVIDER)).map( - ([id, config]) => [ - id, - { - ...config, - ...initialTestProviderState, - ...(state?.testProviders?.[id] || {}), - running: false, - } as TestProviders[0], - ] - ) - ); - - store.setState({ testProviders: initialState }, { persistence: 'session' }); - }; - return { init: initModule, state, api }; -}; diff --git a/code/core/src/manager-api/root.tsx b/code/core/src/manager-api/root.tsx index 7b328f97d6a..67c241e87e7 100644 --- a/code/core/src/manager-api/root.tsx +++ b/code/core/src/manager-api/root.tsx @@ -49,7 +49,6 @@ import { noArrayMerge } from './lib/merge'; import type { ModuleFn } from './lib/types'; import * as addons from './modules/addons'; import * as channel from './modules/channel'; -import * as testProviders from './modules/experimental_testmodule'; import * as globals from './modules/globals'; import * as layout from './modules/layout'; import * as notifications from './modules/notifications'; @@ -79,7 +78,6 @@ export type State = layout.SubState & stories.SubState & refs.SubState & notifications.SubState & - testProviders.SubState & version.SubState & url.SubState & shortcuts.SubState & @@ -98,7 +96,6 @@ export type API = addons.SubAPI & globals.SubAPI & layout.SubAPI & notifications.SubAPI & - testProviders.SubAPI & shortcuts.SubAPI & settings.SubAPI & version.SubAPI & @@ -170,7 +167,6 @@ class ManagerProvider extends Component { addons, layout, notifications, - testProviders, settings, shortcuts, stories, diff --git a/code/core/src/manager/components/sidebar/ContextMenu.tsx b/code/core/src/manager/components/sidebar/ContextMenu.tsx index d9b964bdbd5..f4fb0ac86a8 100644 --- a/code/core/src/manager/components/sidebar/ContextMenu.tsx +++ b/code/core/src/manager/components/sidebar/ContextMenu.tsx @@ -2,12 +2,16 @@ import type { ComponentProps, FC, SyntheticEvent } from 'react'; import React, { useMemo, useState } from 'react'; import { TooltipLinkList, WithTooltip } from 'storybook/internal/components'; -import { type TestProviders } from 'storybook/internal/core-events'; -import { type API_HashEntry, Addon_TypesEnum } from 'storybook/internal/types'; +import { + type API_HashEntry, + type Addon_Collection, + type Addon_TestProviderType, + Addon_TypesEnum, +} from 'storybook/internal/types'; import { EllipsisIcon } from '@storybook/icons'; -import { useStorybookState } from 'storybook/manager-api'; +import { useStorybookApi } from 'storybook/manager-api'; import type { API } from 'storybook/manager-api'; import { styled } from 'storybook/theming'; @@ -55,12 +59,10 @@ export const useContextMenu = (context: API_HashEntry, links: Link[], api: API) * instead of a simple boolean to ensure that the links are recalculated */ const providerLinks = useMemo(() => { - const testProviders = api.getElements( - Addon_TypesEnum.experimental_TEST_PROVIDER - ) as any as TestProviders; + const registeredTestProviders = api.getElements(Addon_TypesEnum.experimental_TEST_PROVIDER); if (hoverCount) { - return generateTestProviderLinks(testProviders, context); + return generateTestProviderLinks(registeredTestProviders, context); } return []; }, [api, context, hoverCount]); @@ -109,8 +111,10 @@ const LiveContextMenu: FC<{ context: API_HashEntry } & ComponentProps { - const { testProviders } = useStorybookState(); - const providerLinks: Link[] = generateTestProviderLinks(testProviders, context); + const registeredTestProviders = useStorybookApi().getElements( + Addon_TypesEnum.experimental_TEST_PROVIDER + ); + const providerLinks: Link[] = generateTestProviderLinks(registeredTestProviders, context); const groups = Array.isArray(links[0]) ? (links as Link[][]) : [links as Link[]]; const all = groups.concat([providerLinks]); @@ -118,15 +122,15 @@ const LiveContextMenu: FC<{ context: API_HashEntry } & ComponentProps, context: API_HashEntry ): Link[] { - return Object.entries(testProviders) + return Object.entries(registeredTestProviders) .map(([testProviderId, state]) => { if (!state) { return null; } - const content = state.sidebarContextMenu?.({ context, state }); + const content = state.sidebarContextMenu?.({ context }); if (!content) { return null; diff --git a/code/core/src/manager/components/sidebar/LegacyRender.tsx b/code/core/src/manager/components/sidebar/LegacyRender.tsx deleted file mode 100644 index 707353c2e01..00000000000 --- a/code/core/src/manager/components/sidebar/LegacyRender.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import React from 'react'; - -import { Button, ProgressSpinner, TooltipNote, WithTooltip } from 'storybook/internal/components'; -import type { TestProviders } from 'storybook/internal/core-events'; - -import { PlayHollowIcon, StopAltIcon } from '@storybook/icons'; - -import { useStorybookApi } from 'storybook/manager-api'; -import { styled } from 'storybook/theming'; - -const Container = styled.div({ - display: 'flex', - justifyContent: 'space-between', - padding: '8px 0', -}); - -const Info = styled.div({ - display: 'flex', - flexDirection: 'column', - marginLeft: 8, -}); - -const Actions = styled.div({ - display: 'flex', - gap: 4, -}); - -const TitleWrapper = styled.div<{ crashed?: boolean }>(({ crashed, theme }) => ({ - fontSize: theme.typography.size.s1, - fontWeight: crashed ? 'bold' : 'normal', - color: crashed ? theme.color.negativeText : theme.color.defaultText, -})); - -const DescriptionWrapper = styled.div(({ theme }) => ({ - fontSize: theme.typography.size.s1, - color: theme.textMutedColor, -})); - -const Progress = styled(ProgressSpinner)({ - margin: 4, -}); - -const StopIcon = styled(StopAltIcon)({ - width: 10, -}); - -export const LegacyRender = ({ ...state }: TestProviders[keyof TestProviders]) => { - const Description = state.description!; - const Title = state.title!; - const api = useStorybookApi(); - - return ( - - - - - </TitleWrapper> - <DescriptionWrapper id="testing-module-description"> - <Description {...state} /> - </DescriptionWrapper> - </Info> - - <Actions> - {state.runnable && ( - <> - {state.running && state.cancellable ? ( - <WithTooltip - hasChrome={false} - trigger="hover" - tooltip={<TooltipNote note={`Stop ${state.name}`} />} - > - <Button - aria-label={`Stop ${state.name}`} - size="medium" - variant="ghost" - padding="none" - onClick={() => api.cancelTestProvider(state.id)} - disabled={state.cancelling} - > - <Progress - percentage={ - state.progress?.percentageCompleted ?? - (state.details as any)?.buildProgressPercentage - } - > - <StopIcon /> - </Progress> - </Button> - </WithTooltip> - ) : ( - <WithTooltip - hasChrome={false} - trigger="hover" - tooltip={<TooltipNote note={`Start ${state.name}`} />} - > - <Button - aria-label={`Start ${state.name}`} - size="medium" - variant="ghost" - padding="small" - onClick={() => api.runTestProvider(state.id)} - disabled={state.crashed || state.running} - > - <PlayHollowIcon /> - </Button> - </WithTooltip> - )} - </> - )} - </Actions> - </Container> - ); -}; diff --git a/code/core/src/manager/components/sidebar/Refs.stories.tsx b/code/core/src/manager/components/sidebar/Refs.stories.tsx index 8712302a4da..5042fc66311 100644 --- a/code/core/src/manager/components/sidebar/Refs.stories.tsx +++ b/code/core/src/manager/components/sidebar/Refs.stories.tsx @@ -10,7 +10,7 @@ import { mockDataset } from './mockdata'; import type { RefType } from './types'; const managerContext = { - state: { docsOptions: {}, testProviders: {} }, + state: { docsOptions: {} }, api: { on: fn().mockName('api::on'), off: fn().mockName('api::off'), diff --git a/code/core/src/manager/components/sidebar/Sidebar.stories.tsx b/code/core/src/manager/components/sidebar/Sidebar.stories.tsx index 75f465608de..d422baf27a4 100644 --- a/code/core/src/manager/components/sidebar/Sidebar.stories.tsx +++ b/code/core/src/manager/components/sidebar/Sidebar.stories.tsx @@ -35,7 +35,6 @@ const managerContext: any = { autodocs: 'tag', docsMode: false, }, - testProviders: {}, }, api: { emit: fn().mockName('api::emit'), diff --git a/code/core/src/manager/components/sidebar/SidebarBottom.stories.tsx b/code/core/src/manager/components/sidebar/SidebarBottom.stories.tsx index 26d834bcfd8..e98f8c64177 100644 --- a/code/core/src/manager/components/sidebar/SidebarBottom.stories.tsx +++ b/code/core/src/manager/components/sidebar/SidebarBottom.stories.tsx @@ -1,13 +1,16 @@ import React, { type FC, useEffect, useState } from 'react'; -import { Addon_TypesEnum } from 'storybook/internal/types'; +import { + type Addon_Collection, + type Addon_TestProviderType, + Addon_TypesEnum, +} from 'storybook/internal/types'; import type { Meta, StoryObj } from '@storybook/react-vite'; import { type API, ManagerContext } from 'storybook/manager-api'; import { expect, fireEvent, fn, waitFor, within } from 'storybook/test'; -import type { TestProviders } from '../../../core-events'; import type { TestProviderStateByProviderId } from '../../../shared/test-provider-store'; import { SidebarBottomBase } from './SidebarBottom'; @@ -52,32 +55,16 @@ const managerContext: any = { }, }; -const registeredTestProviders: TestProviders = { +const registeredTestProviders: Addon_Collection<Addon_TestProviderType> = { 'component-tests': { type: Addon_TypesEnum.experimental_TEST_PROVIDER, id: 'component-tests', - name: 'Component tests', render: () => <div>Component tests</div>, - runnable: true, - details: {}, - cancellable: true, - cancelling: false, - running: false, - failed: false, - crashed: false, }, 'visual-tests': { type: Addon_TypesEnum.experimental_TEST_PROVIDER, id: 'visual-tests', - name: 'Visual tests', render: () => <div>Visual tests</div>, - runnable: true, - details: {}, - cancellable: true, - cancelling: false, - running: false, - failed: false, - crashed: false, }, }; const testProviderStates: TestProviderStateByProviderId = { @@ -149,15 +136,7 @@ export const DynamicHeight: Story = { 'dynamic-height': { type: Addon_TypesEnum.experimental_TEST_PROVIDER, id: 'dynamic-height', - name: 'Dynamic height', render: () => <DynamicHeightDemo />, - runnable: true, - details: {}, - cancellable: true, - cancelling: false, - running: false, - failed: false, - crashed: false, }, }, }, diff --git a/code/core/src/manager/components/sidebar/SidebarBottom.tsx b/code/core/src/manager/components/sidebar/SidebarBottom.tsx index 1c7b78366c8..8a7cb06939c 100644 --- a/code/core/src/manager/components/sidebar/SidebarBottom.tsx +++ b/code/core/src/manager/components/sidebar/SidebarBottom.tsx @@ -1,13 +1,11 @@ import React, { Fragment, useEffect, useLayoutEffect, useRef, useState } from 'react'; import { - TESTING_MODULE_CRASH_REPORT, - TESTING_MODULE_PROGRESS_REPORT, - type TestProviders, - type TestingModuleCrashReportPayload, - type TestingModuleProgressReportPayload, -} from 'storybook/internal/core-events'; -import { type API_FilterFunction } from 'storybook/internal/types'; + type API_FilterFunction, + type Addon_Collection, + type Addon_TestProviderType, + Addon_TypesEnum, +} from 'storybook/internal/types'; import { experimental_useStatusStore, @@ -89,7 +87,7 @@ interface SidebarBottomProps { hasStatuses: boolean; isDevelopment?: boolean; testProviderStates: TestProviderStateByProviderId; - registeredTestProviders: TestProviders; + registeredTestProviders: Addon_Collection<Addon_TestProviderType>; onRunAll: () => void; } @@ -126,38 +124,6 @@ export const SidebarBottomBase = ({ api.experimental_setFilter('sidebar-bottom-filter', filter); }, [api, warningCount, errorCount, warningsActive, errorsActive]); - // Register listeners before the first render - useLayoutEffect(() => { - const onCrashReport = ({ providerId, ...details }: TestingModuleCrashReportPayload) => { - api.updateTestProviderState(providerId, { - error: { name: 'Crashed!', message: details.error.message }, - running: false, - crashed: true, - }); - }; - - const onProgressReport = async ({ - providerId, - ...result - }: TestingModuleProgressReportPayload) => { - const statusResult = 'status' in result ? result.status : undefined; - api.updateTestProviderState( - providerId, - statusResult === 'failed' - ? { ...result, running: false, failed: true } - : { ...result, running: statusResult === 'pending' } - ); - }; - - api.on(TESTING_MODULE_CRASH_REPORT, onCrashReport); - api.on(TESTING_MODULE_PROGRESS_REPORT, onProgressReport); - - return () => { - api.off(TESTING_MODULE_CRASH_REPORT, onCrashReport); - api.off(TESTING_MODULE_PROGRESS_REPORT, onProgressReport); - }; - }, [api, registeredTestProviders]); - if ( !warningCount && !errorCount && @@ -199,7 +165,8 @@ export const SidebarBottomBase = ({ export const SidebarBottom = ({ isDevelopment }: { isDevelopment?: boolean }) => { const api = useStorybookApi(); - const { notifications, testProviders: registeredTestProviders } = useStorybookState(); + const registeredTestProviders = api.getElements(Addon_TypesEnum.experimental_TEST_PROVIDER); + const { notifications } = useStorybookState(); const { hasStatuses, errorCount, warningCount } = experimental_useStatusStore((statuses) => { return Object.values(statuses).reduce( (result, storyStatuses) => { diff --git a/code/core/src/manager/components/sidebar/TestingModule.stories.tsx b/code/core/src/manager/components/sidebar/TestingModule.stories.tsx index a9a10afa4ca..3a0ccb67617 100644 --- a/code/core/src/manager/components/sidebar/TestingModule.stories.tsx +++ b/code/core/src/manager/components/sidebar/TestingModule.stories.tsx @@ -1,9 +1,12 @@ import React from 'react'; import type { Listener } from 'storybook/internal/channels'; -import { type TestProviders } from 'storybook/internal/core-events'; import type { TestProviderStateByProviderId } from 'storybook/internal/types'; -import { Addon_TypesEnum } from 'storybook/internal/types'; +import { + type Addon_Collection, + type Addon_TestProviderType, + Addon_TypesEnum, +} from 'storybook/internal/types'; import type { Meta, StoryObj } from '@storybook/react-vite'; @@ -19,38 +22,21 @@ const TestProvider = styled.div({ fontSize: 12, }); -const baseState = { - details: {}, - cancellable: false, - cancelling: false, - running: false, - failed: false, - crashed: false, -}; - -const registeredTestProviders: TestProviders = { +const registeredTestProviders: Addon_Collection<Addon_TestProviderType> = { 'component-tests': { type: Addon_TypesEnum.experimental_TEST_PROVIDER, id: 'component-tests', - name: 'Component tests', render: () => <TestProvider>Component tests</TestProvider>, - runnable: true, - ...baseState, }, 'visual-tests': { type: Addon_TypesEnum.experimental_TEST_PROVIDER, id: 'visual-tests', - name: 'Visual tests', render: () => <TestProvider>Visual tests</TestProvider>, - runnable: true, - ...baseState, }, linting: { type: Addon_TypesEnum.experimental_TEST_PROVIDER, id: 'linting', - name: 'Linting', render: () => <TestProvider>Linting</TestProvider>, - ...baseState, }, }; diff --git a/code/core/src/manager/components/sidebar/TestingModule.tsx b/code/core/src/manager/components/sidebar/TestingModule.tsx index e8845782700..999d5f3b73d 100644 --- a/code/core/src/manager/components/sidebar/TestingModule.tsx +++ b/code/core/src/manager/components/sidebar/TestingModule.tsx @@ -1,17 +1,19 @@ import React, { type SyntheticEvent, useCallback, useEffect, useRef, useState } from 'react'; +import { once } from 'storybook/internal/client-logger'; import { Button, IconButton, TooltipNote } from 'storybook/internal/components'; import { WithTooltip } from 'storybook/internal/components'; -import { type TestProviders } from 'storybook/internal/core-events'; +import type { + Addon_Collection, + Addon_TestProviderType, + TestProviderStateByProviderId, +} from 'storybook/internal/types'; import { ChevronSmallUpIcon, PlayAllHollowIcon, SweepIcon } from '@storybook/icons'; import { internal_fullTestProviderStore } from '#manager-stores'; import { keyframes, styled } from 'storybook/theming'; -import type { TestProviderStateByProviderId } from '../../../shared/test-provider-store'; -import { LegacyRender } from './LegacyRender'; - const DEFAULT_HEIGHT = 500; const spin = keyframes({ @@ -162,7 +164,7 @@ const TestProvider = styled.div(({ theme }) => ({ })); interface TestingModuleProps { - registeredTestProviders: TestProviders; + registeredTestProviders: Addon_Collection<Addon_TestProviderType>; testProviderStates: TestProviderStateByProviderId; hasStatuses: boolean; clearStatuses: () => void; @@ -271,11 +273,17 @@ export const TestingModule = ({ }} > <Content ref={contentRef}> - {Object.values(registeredTestProviders).map((state) => { - const { render: Render } = state; + {Object.values(registeredTestProviders).map((registeredTestProvider) => { + const { render: Render, id } = registeredTestProvider; + if (!Render) { + once.warn( + `No render function found for test provider with id '${id}', skipping...` + ); + return null; + } return ( - <TestProvider key={state.id} data-module-id={state.id}> - {Render ? <Render {...state} /> : <LegacyRender {...state} />} + <TestProvider key={id} data-module-id={id}> + <Render /> </TestProvider> ); })} diff --git a/code/core/src/manager/components/sidebar/Tree.stories.tsx b/code/core/src/manager/components/sidebar/Tree.stories.tsx index 20a6ad373f1..4e8a8cfdbad 100644 --- a/code/core/src/manager/components/sidebar/Tree.stories.tsx +++ b/code/core/src/manager/components/sidebar/Tree.stories.tsx @@ -1,5 +1,11 @@ import React, { useState } from 'react'; +import { + type Addon_Collection, + type Addon_TestProviderType, + Addon_TypesEnum, +} from 'storybook/internal/types'; + import type { Meta, StoryObj } from '@storybook/react-vite'; import { action } from 'storybook/actions'; @@ -17,43 +23,28 @@ const managerContext: any = { autodocs: 'tag', docsMode: false, }, - testProviders: { - 'component-tests': { - type: 'experimental_TEST_PROVIDER', - id: 'component-tests', - render: () => 'Component tests', - sidebarContextMenu: () => <div>TEST_PROVIDER_CONTEXT_CONTENT</div>, - runnable: true, - }, - 'visual-tests': { - type: 'experimental_TEST_PROVIDER', - id: 'visual-tests', - render: () => 'Visual tests', - sidebarContextMenu: () => null, - runnable: true, - }, - }, }, api: { on: fn().mockName('api::on'), off: fn().mockName('api::off'), emit: fn().mockName('api::emit'), - getElements: fn(() => ({ - 'component-tests': { - type: 'experimental_TEST_PROVIDER', - id: 'component-tests', - render: () => 'Component tests', - sidebarContextMenu: () => <div>TEST_PROVIDER_CONTEXT_CONTENT</div>, - runnable: true, - }, - 'visual-tests': { - type: 'experimental_TEST_PROVIDER', - id: 'visual-tests', - render: () => 'Visual tests', - sidebarContextMenu: () => null, - runnable: true, - }, - })), + getElements: fn( + () => + ({ + 'component-tests': { + type: Addon_TypesEnum.experimental_TEST_PROVIDER, + id: 'component-tests', + render: () => 'Component tests', + sidebarContextMenu: () => <div>TEST_PROVIDER_CONTEXT_CONTENT</div>, + }, + 'visual-tests': { + type: Addon_TypesEnum.experimental_TEST_PROVIDER, + id: 'visual-tests', + render: () => 'Visual tests', + sidebarContextMenu: () => null, + }, + }) satisfies Addon_Collection<Addon_TestProviderType> + ), }, }; diff --git a/code/core/src/manager/globals/exports.ts b/code/core/src/manager/globals/exports.ts index e160174c1f8..d3d5cf824da 100644 --- a/code/core/src/manager/globals/exports.ts +++ b/code/core/src/manager/globals/exports.ts @@ -588,11 +588,6 @@ export default { 'STORY_THREW_EXCEPTION', 'STORY_UNCHANGED', 'TELEMETRY_ERROR', - 'TESTING_MODULE_CANCEL_TEST_RUN_REQUEST', - 'TESTING_MODULE_CANCEL_TEST_RUN_RESPONSE', - 'TESTING_MODULE_CRASH_REPORT', - 'TESTING_MODULE_PROGRESS_REPORT', - 'TESTING_MODULE_RUN_REQUEST', 'TOGGLE_WHATS_NEW_NOTIFICATIONS', 'UNHANDLED_ERRORS_WHILE_PLAYING', 'UPDATE_GLOBALS', @@ -651,11 +646,6 @@ export default { 'STORY_THREW_EXCEPTION', 'STORY_UNCHANGED', 'TELEMETRY_ERROR', - 'TESTING_MODULE_CANCEL_TEST_RUN_REQUEST', - 'TESTING_MODULE_CANCEL_TEST_RUN_RESPONSE', - 'TESTING_MODULE_CRASH_REPORT', - 'TESTING_MODULE_PROGRESS_REPORT', - 'TESTING_MODULE_RUN_REQUEST', 'TOGGLE_WHATS_NEW_NOTIFICATIONS', 'UNHANDLED_ERRORS_WHILE_PLAYING', 'UPDATE_GLOBALS', diff --git a/code/core/src/types/modules/addons.ts b/code/core/src/types/modules/addons.ts index 0fef98cab33..8095c360969 100644 --- a/code/core/src/types/modules/addons.ts +++ b/code/core/src/types/modules/addons.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import type { FC, PropsWithChildren, ReactElement, ReactNode } from 'react'; -import type { TestProviderConfig, TestingModuleProgressReportProgress } from '../../core-events'; import type { RenderData as RouterData } from '../../router/types'; import type { ThemeVars } from '../../theming/types'; import type { API_SidebarOptions } from './api'; @@ -324,7 +323,7 @@ export type Addon_Type = | Addon_BaseType | Addon_PageType | Addon_WrapperType - | Addon_TestProviderType<Addon_TestProviderState>; + | Addon_TestProviderType; export interface Addon_BaseType { /** * The title of the addon. This can be a simple string, but it can also be a @@ -440,44 +439,14 @@ export interface Addon_WrapperType { >; } -export interface Addon_TestProviderType< - Details extends { [key: string]: any } = NonNullable<unknown>, -> { +export interface Addon_TestProviderType { type: Addon_TypesEnum.experimental_TEST_PROVIDER; /** The unique id of the test provider. */ id: string; - name: string; - /** @deprecated Use render instead */ - title?: (state: TestProviderConfig & Addon_TestProviderState<Details>) => ReactNode; - /** @deprecated Use render instead */ - description?: (state: TestProviderConfig & Addon_TestProviderState<Details>) => ReactNode; - render?: (state: TestProviderConfig & Addon_TestProviderState<Details>) => ReactNode; - sidebarContextMenu?: (options: { - context: API_HashEntry; - state: TestProviderConfig & Addon_TestProviderState<Details>; - }) => ReactNode; - stateUpdater?: ( - state: TestProviderConfig & Addon_TestProviderState<Details>, - update: Partial<Addon_TestProviderState<Details>> - ) => void | Partial<TestProviderConfig & Addon_TestProviderState<Details>>; - runnable?: boolean; + render: () => ReactNode; + sidebarContextMenu?: (options: { context: API_HashEntry }) => ReactNode; } -export type Addon_TestProviderState<Details extends { [key: string]: any } = NonNullable<unknown>> = - Pick<Addon_TestProviderType, 'runnable'> & { - progress?: TestingModuleProgressReportProgress; - details: Details; - cancellable: boolean; - cancelling: boolean; - running: boolean; - failed: boolean; - crashed: boolean; - error?: { - name: string; - message?: string; - }; - }; - type Addon_TypeBaseNames = Exclude< Addon_TypesEnum, | Addon_TypesEnum.PREVIEW @@ -488,7 +457,7 @@ type Addon_TypeBaseNames = Exclude< export interface Addon_TypesMapping extends Record<Addon_TypeBaseNames, Addon_BaseType> { [Addon_TypesEnum.PREVIEW]: Addon_WrapperType; [Addon_TypesEnum.experimental_PAGE]: Addon_PageType; - [Addon_TypesEnum.experimental_TEST_PROVIDER]: Addon_TestProviderType<Addon_TestProviderState>; + [Addon_TypesEnum.experimental_TEST_PROVIDER]: Addon_TestProviderType; } export type Addon_Loader<API> = (api: API) => void;