Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] next from storybookjs:next #107

Merged
merged 9 commits into from
Dec 24, 2024
2 changes: 2 additions & 0 deletions code/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { StorybookConfig } from '../frameworks/react-vite';

const componentsPath = join(__dirname, '../core/src/components');
const managerApiPath = join(__dirname, '../core/src/manager-api');
const imageContextPath = join(__dirname, '..//frameworks/nextjs/src/image-context.ts');

const config: StorybookConfig = {
stories: [
Expand Down Expand Up @@ -146,6 +147,7 @@ const config: StorybookConfig = {
'storybook/internal/components': componentsPath,
'@storybook/manager-api': managerApiPath,
'storybook/internal/manager-api': managerApiPath,
'sb-original/image-context': imageContextPath,
}
: {}),
},
Expand Down
7 changes: 5 additions & 2 deletions code/addons/test/src/components/Interaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const MethodCallWrapper = styled.div(() => ({

const RowContainer = styled('div', {
shouldForwardProp: (prop) => !['call', 'pausedAt'].includes(prop.toString()),
})<{ call: Call; pausedAt: Call['id'] }>(
})<{ call: Call; pausedAt: Call['id'] | undefined }>(
({ theme, call }) => ({
position: 'relative',
display: 'flex',
Expand Down Expand Up @@ -117,6 +117,9 @@ const RowMessage = styled('div')(({ theme }) => ({

export const Exception = ({ exception }: { exception: Call['exception'] }) => {
const filter = useAnsiToHtmlFilter();
if (!exception) {
return null;
}
if (isJestError(exception)) {
return <MatcherResult {...exception} />;
}
Expand Down Expand Up @@ -187,7 +190,7 @@ export const Interaction = ({
</MethodCallWrapper>
</RowLabel>
<RowActions>
{childCallIds?.length > 0 && (
{(childCallIds?.length ?? 0) > 0 && (
<WithTooltip
hasChrome={false}
tooltip={<Note note={`${isCollapsed ? 'Show' : 'Hide'} interactions`} />}
Expand Down
4 changes: 2 additions & 2 deletions code/addons/test/src/components/MatcherResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ export const MatcherResult = ({
{lines.flatMap((line: string, index: number) => {
if (line.startsWith('expect(')) {
const received = getParams(line, 7);
const remainderIndex = received && 7 + received.length;
const remainderIndex = received ? 7 + received.length : 0;
const matcher = received && line.slice(remainderIndex).match(/\.(to|last|nth)[A-Z]\w+\(/);
if (matcher) {
const expectedIndex = remainderIndex + matcher.index + matcher[0].length;
const expectedIndex = remainderIndex + (matcher.index ?? 0) + matcher[0].length;
const expected = getParams(line, expectedIndex);
if (expected) {
return [
Expand Down
6 changes: 3 additions & 3 deletions code/addons/test/src/components/MethodCall.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export const Node = ({
case Object.prototype.hasOwnProperty.call(value, '__class__'):
return <ClassNode {...props} {...value.__class__} />;
case Object.prototype.hasOwnProperty.call(value, '__callId__'):
return <MethodCall call={callsById.get(value.__callId__)} callsById={callsById} />;
return <MethodCall call={callsById?.get(value.__callId__)} callsById={callsById} />;
/* eslint-enable no-underscore-dangle */

case Object.prototype.toString.call(value) === '[object Object]':
Expand Down Expand Up @@ -418,7 +418,7 @@ export const MethodCall = ({
callsById,
}: {
call?: Call;
callsById: Map<Call['id'], Call>;
callsById?: Map<Call['id'], Call>;
}) => {
// Call might be undefined during initial render, can be safely ignored.
if (!call) {
Expand All @@ -434,7 +434,7 @@ export const MethodCall = ({
const callId = (elem as CallRef).__callId__;
return [
callId ? (
<MethodCall key={`elem${index}`} call={callsById.get(callId)} callsById={callsById} />
<MethodCall key={`elem${index}`} call={callsById?.get(callId)} callsById={callsById} />
) : (
<span key={`elem${index}`}>{elem as any}</span>
),
Expand Down
25 changes: 11 additions & 14 deletions code/addons/test/src/components/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,6 @@ import type { API_StatusValue } from '@storybook/types';
import { ADDON_ID, STORYBOOK_ADDON_TEST_CHANNEL, TEST_PROVIDER_ID } from '../constants';
import { InteractionsPanel } from './InteractionsPanel';

interface Interaction extends Call {
status: Call['status'];
childCallIds: Call['id'][];
isHidden: boolean;
isCollapsed: boolean;
toggleCollapsed: () => void;
}

const INITIAL_CONTROL_STATES = {
start: false,
back: false,
Expand Down Expand Up @@ -60,19 +52,20 @@ export const getInteractions = ({
const childCallMap = new Map<Call['id'], Call['id'][]>();

return log
.map<Call & { isHidden: boolean }>(({ callId, ancestors, status }) => {
.map(({ callId, ancestors, status }) => {
let isHidden = false;
ancestors.forEach((ancestor) => {
if (collapsed.has(ancestor)) {
isHidden = true;
}
childCallMap.set(ancestor, (childCallMap.get(ancestor) || []).concat(callId));
});
return { ...calls.get(callId), status, isHidden };
return { ...calls.get(callId)!, status, isHidden };
})
.map<Interaction>((call) => {
.map((call) => {
const status =
call.status === CallStates.ERROR &&
call.ancestors &&
callsById.get(call.ancestors.slice(-1)[0])?.status === CallStates.ACTIVE
? CallStates.ACTIVE
: call.status;
Expand Down Expand Up @@ -131,7 +124,7 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId
const calls = useRef<Map<Call['id'], Omit<Call, 'status'>>>(new Map());
const setCall = ({ status, ...call }: Call) => calls.current.set(call.id, call);

const endRef = useRef();
const endRef = useRef<HTMLDivElement>();
useEffect(() => {
let observer: IntersectionObserver;
if (global.IntersectionObserver) {
Expand All @@ -151,6 +144,7 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId
{
[EVENTS.CALL]: setCall,
[EVENTS.SYNC]: (payload) => {
// @ts-expect-error TODO
set((s) => {
const list = getInteractions({
log: payload.logItems,
Expand Down Expand Up @@ -214,6 +208,7 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId
);

useEffect(() => {
// @ts-expect-error TODO
set((s) => {
const list = getInteractions({
log: log.current,
Expand Down Expand Up @@ -250,16 +245,17 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId
const hasException =
!!caughtException ||
!!unhandledErrors ||
// @ts-expect-error TODO
interactions.some((v) => v.status === CallStates.ERROR);

const storyStatus = storyStatuses[storyId]?.[TEST_PROVIDER_ID];
const storyTestStatus = storyStatus?.status;

const browserTestStatus = useMemo<CallStates | null>(() => {
const browserTestStatus = useMemo<CallStates | undefined>(() => {
if (!isPlaying && (interactions.length > 0 || hasException)) {
return hasException ? CallStates.ERROR : CallStates.DONE;
}
return isPlaying ? CallStates.ACTIVE : null;
return isPlaying ? CallStates.ACTIVE : undefined;
}, [isPlaying, interactions, hasException]);

const { testRunId } = storyStatus?.data || {};
Expand Down Expand Up @@ -315,6 +311,7 @@ export const Panel = memo<{ storyId: string }>(function PanelMemoized({ storyId
unhandledErrors={unhandledErrors}
isPlaying={isPlaying}
pausedAt={pausedAt}
// @ts-expect-error TODO
endRef={endRef}
onScrollToEnd={scrollTarget && scrollToTarget}
/>
Expand Down
2 changes: 1 addition & 1 deletion code/addons/test/src/components/RelativeTime.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';

export const RelativeTime = ({ timestamp }: { timestamp?: number }) => {
const [timeAgo, setTimeAgo] = useState(null);
const [timeAgo, setTimeAgo] = useState<number | null>(null);

useEffect(() => {
if (timestamp) {
Expand Down
4 changes: 2 additions & 2 deletions code/addons/test/src/components/StatusBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const StyledBadge = styled.div<StatusBadgeProps>(({ theme, status }) => {
[CallStates.ERROR]: theme.color.negative,
[CallStates.ACTIVE]: theme.color.warning,
[CallStates.WAITING]: theme.color.warning,
}[status];
}[status!];
return {
padding: '4px 6px 4px 8px;',
borderRadius: '4px',
Expand All @@ -36,7 +36,7 @@ export const StatusBadge: React.FC<StatusBadgeProps> = ({ status }) => {
[CallStates.ERROR]: 'Fail',
[CallStates.ACTIVE]: 'Runs',
[CallStates.WAITING]: 'Runs',
}[status];
}[status!];
return (
<StyledBadge aria-label="Status of the test run" status={status}>
{badgeText}
Expand Down
2 changes: 1 addition & 1 deletion code/addons/test/src/components/Subnav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const RerunButton = styled(StyledIconButton)<
>(({ theme, animating, disabled }) => ({
opacity: disabled ? 0.5 : 1,
svg: {
animation: animating && `${theme.animation.rotate360} 200ms ease-out`,
animation: animating ? `${theme.animation.rotate360} 200ms ease-out` : undefined,
},
}));

Expand Down
7 changes: 3 additions & 4 deletions code/addons/test/src/components/TestDiscrepancyMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import React, { useEffect } from 'react';
import React from 'react';

import { Link } from 'storybook/internal/components';
import { useStorybookApi } from 'storybook/internal/manager-api';
import { styled } from 'storybook/internal/theming';
import type { StoryId } from 'storybook/internal/types';

import { CallStates } from '@storybook/instrumenter';

import { DOCUMENTATION_DISCREPANCY_LINK, STORYBOOK_ADDON_TEST_CHANNEL } from '../constants';
import { DOCUMENTATION_DISCREPANCY_LINK } from '../constants';

const Wrapper = styled.div(({ theme: { color, typography, background } }) => ({
textAlign: 'start',
Expand All @@ -32,7 +31,7 @@ const Wrapper = styled.div(({ theme: { color, typography, background } }) => ({
}));

interface TestDiscrepancyMessageProps {
browserTestStatus: CallStates;
browserTestStatus?: CallStates;
}

export const TestDiscrepancyMessage = ({ browserTestStatus }: TestDiscrepancyMessageProps) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const baseState: TestProviderState<Details, Config> = {
cancellable: true,
cancelling: false,
crashed: false,
error: null,
error: undefined,
failed: false,
running: false,
watching: false,
Expand Down
14 changes: 9 additions & 5 deletions code/addons/test/src/components/TestProviderRender.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export const TestProviderRender: FC<
: undefined;

const a11ySkippedAmount =
state.running || !state?.details.config?.a11y || !state.config.a11y
state.running || !state?.details.config?.a11y || !state.config?.a11y
? null
: a11yResults?.filter((result) => !result).length;

Expand Down Expand Up @@ -304,9 +304,11 @@ export const TestProviderRender: FC<
const firstNotPassed = results.find(
(r) => r.status === 'failed' || r.status === 'warning'
);
openPanel(firstNotPassed.storyId, PANEL_ID);
if (firstNotPassed) {
openPanel(firstNotPassed.storyId, PANEL_ID);
}
}
: null
: undefined
}
icon={
state.crashed ? (
Expand Down Expand Up @@ -359,9 +361,11 @@ export const TestProviderRender: FC<
(report) => report.status === 'failed' || report.status === 'warning'
)
);
openPanel(firstNotPassed.storyId, A11y_ADDON_PANEL_ID);
if (firstNotPassed) {
openPanel(firstNotPassed.storyId, A11y_ADDON_PANEL_ID);
}
}
: null
: undefined
}
icon={<TestStatusIcon status={a11yStatus} aria-label={`status: ${a11yStatus}`} />}
right={isStoryEntry ? null : a11yNotPassedAmount || null}
Expand Down
9 changes: 7 additions & 2 deletions code/addons/test/src/manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ addons.register(ADDON_ID, (api) => {
runnable: true,
watchable: true,
name: 'Component tests',
// @ts-expect-error: TODO: Fix types
render: (state) => {
const [isModalOpen, setModalOpen] = useState(false);
return (
Expand All @@ -55,6 +56,7 @@ addons.register(ADDON_ID, (api) => {
);
},

// @ts-expect-error: TODO: Fix types
sidebarContextMenu: ({ context, state }) => {
if (context.type === 'docs') {
return null;
Expand All @@ -72,6 +74,7 @@ addons.register(ADDON_ID, (api) => {
);
},

// @ts-expect-error: TODO: Fix types
stateUpdater: (state, update) => {
const updated = {
...state,
Expand All @@ -89,6 +92,7 @@ addons.register(ADDON_ID, (api) => {
await api.experimental_updateStatus(
TEST_PROVIDER_ID,
Object.fromEntries(
// @ts-expect-error: TODO: Fix types
update.details.testResults.flatMap((testResult) =>
testResult.results
.filter(({ storyId }) => storyId)
Expand All @@ -113,6 +117,7 @@ addons.register(ADDON_ID, (api) => {
await api.experimental_updateStatus(
'storybook/addon-a11y/test-provider',
Object.fromEntries(
// @ts-expect-error: TODO: Fix types
update.details.testResults.flatMap((testResult) =>
testResult.results
.filter(({ storyId }) => storyId)
Expand Down Expand Up @@ -143,7 +148,7 @@ addons.register(ADDON_ID, (api) => {

return updated;
},
} as Addon_TestProviderType<Details, Config>);
} satisfies Omit<Addon_TestProviderType<Details, Config>, 'id'>);
}

const filter = ({ state }: Combo) => {
Expand All @@ -158,7 +163,7 @@ addons.register(ADDON_ID, (api) => {
match: ({ viewMode }) => viewMode === 'story',
render: ({ active }) => {
return (
<AddonPanel active={active}>
<AddonPanel active={!!active}>
<Consumer filter={filter}>{({ storyId }) => <Panel storyId={storyId} />}</Consumer>
</AddonPanel>
);
Expand Down
4 changes: 2 additions & 2 deletions code/addons/test/src/node/boot-test-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const MAX_START_TIME = 30000;
const vitestModulePath = join(__dirname, 'node', 'vitest.mjs');

// Events that were triggered before Vitest was ready are queued up and resent once it's ready
const eventQueue: { type: string; args: any[] }[] = [];
const eventQueue: { type: string; args?: any[] }[] = [];

let child: null | ChildProcess;
let ready = false;
Expand Down Expand Up @@ -87,7 +87,7 @@ const bootTestRunner = async (channel: Channel) => {
if (result.type === 'ready') {
// Resend events that triggered (during) the boot sequence, now that Vitest is ready
while (eventQueue.length) {
const { type, args } = eventQueue.shift();
const { type, args } = eventQueue.shift()!;
child?.send({ type, args, from: 'server' });
}

Expand Down
4 changes: 2 additions & 2 deletions code/addons/test/src/node/reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ export class StorybookReporter implements Reporter {
(t) => t.status === 'failed' && t.results.length === 0
);

const reducedTestSuiteFailures = new Set<string>();
const reducedTestSuiteFailures = new Set<string | undefined>();

testSuiteFailures.forEach((t) => {
reducedTestSuiteFailures.add(t.message);
Expand All @@ -240,7 +240,7 @@ export class StorybookReporter implements Reporter {
message: Array.from(reducedTestSuiteFailures).reduce(
(acc, curr) => `${acc}\n${curr}`,
''
),
)!,
}
: {
name: `${unhandledErrors.length} unhandled error${unhandledErrors?.length > 1 ? 's' : ''}`,
Expand Down
3 changes: 2 additions & 1 deletion code/addons/test/src/node/test-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ const vitest = vi.hoisted(() => ({
configOverride: {
actualTestNamePattern: undefined,
get testNamePattern() {
return this.actualTestNamePattern;
return this.actualTestNamePattern!;
},
set testNamePattern(value: string) {
setTestNamePattern(value);
// @ts-expect-error Ignore for testing
this.actualTestNamePattern = value;
},
},
Expand Down
Loading
Loading