Merge pull request #30962 from storybookjs/jeppe/remove-deprecated-test-provider-api

Core: Remove deprecated parts of test provider API
This commit is contained in:
Jeppe Reinhold 2025-03-31 15:32:39 +02:00 committed by GitHub
commit 5fdb23a03f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 83 additions and 607 deletions

View File

@ -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'

View File

@ -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 <SidebarContextMenu context={context} api={api} />;
},
} satisfies Omit<Addon_TestProviderType, 'id'>);
});
}
});

View File

@ -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<Details extends { [key: string]: any } = NonNullable<unknown>> =
Addon_TestProviderState<Details>;
export type TestProviders = Record<TestProviderId, TestProviderConfig & TestProviderState>;
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;
};

View File

@ -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';

View File

@ -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;
};

View File

@ -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<TestProviderState>): void;
clearTestProviderState(id: TestProviderId): void;
runTestProvider(id: TestProviderId, options?: RunOptions): () => void;
cancelTestProvider(id: TestProviderId): void;
};
export const init: ModuleFn<SubAPI, SubState> = ({ 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 };
};

View File

@ -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<ManagerProviderProps, State> {
addons,
layout,
notifications,
testProviders,
settings,
shortcuts,
stories,

View File

@ -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<typeof Too
links,
...rest
}) => {
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<typeof Too
};
export function generateTestProviderLinks(
testProviders: TestProviders,
registeredTestProviders: Addon_Collection<Addon_TestProviderType>,
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;

View File

@ -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 (
<Container>
<Info>
<TitleWrapper crashed={state.crashed} id="testing-module-title">
<Title {...state} />
</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>
);
};

View File

@ -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'),

View File

@ -35,7 +35,6 @@ const managerContext: any = {
autodocs: 'tag',
docsMode: false,
},
testProviders: {},
},
api: {
emit: fn().mockName('api::emit'),

View File

@ -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,
},
},
},

View File

@ -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) => {

View File

@ -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,
},
};

View File

@ -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>
);
})}

View File

@ -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>
),
},
};

View File

@ -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',

View File

@ -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;