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

Interactions: Fix waitFor behavior while debugging #18460

Merged
merged 19 commits into from
Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions addons/interactions/src/Panel.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ComponentStoryObj, ComponentMeta } from '@storybook/react';
import { CallStates } from '@storybook/instrumenter';
import { styled } from '@storybook/theming';

import { getCall } from './mocks';
import { getCalls, getInteractions } from './mocks';
import { AddonPanelPure } from './Panel';
import SubnavStories from './components/Subnav/Subnav.stories';

Expand All @@ -20,6 +20,8 @@ const StyledWrapper = styled.div(({ theme }) => ({
overflow: 'auto',
}));

const interactions = getInteractions(CallStates.DONE);

export default {
title: 'Addons/Interactions/Panel',
component: AddonPanelPure,
Expand All @@ -34,10 +36,10 @@ export default {
layout: 'fullscreen',
},
args: {
calls: new Map(),
calls: new Map(getCalls(CallStates.DONE).map((call) => [call.id, call])),
controls: SubnavStories.args.controls,
controlStates: SubnavStories.args.controlStates,
interactions: [getCall(CallStates.DONE)],
interactions,
fileName: 'addon-interactions.stories.tsx',
hasException: false,
isPlaying: false,
Expand All @@ -52,14 +54,14 @@ type Story = ComponentStoryObj<typeof AddonPanelPure>;

export const Passing: Story = {
args: {
interactions: [getCall(CallStates.DONE)],
interactions: getInteractions(CallStates.DONE),
},
};

export const Paused: Story = {
args: {
isPlaying: true,
interactions: [getCall(CallStates.WAITING)],
interactions: getInteractions(CallStates.WAITING),
controlStates: {
debugger: true,
start: false,
Expand All @@ -68,20 +70,21 @@ export const Paused: Story = {
next: true,
end: true,
},
pausedAt: interactions[interactions.length - 1].id,
},
};

export const Playing: Story = {
args: {
isPlaying: true,
interactions: [getCall(CallStates.ACTIVE)],
interactions: getInteractions(CallStates.ACTIVE),
},
};

export const Failed: Story = {
args: {
hasException: true,
interactions: [getCall(CallStates.ERROR)],
interactions: getInteractions(CallStates.ERROR),
},
};

Expand Down
27 changes: 18 additions & 9 deletions addons/interactions/src/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ interface InteractionsPanelProps {
fileName?: string;
hasException?: boolean;
isPlaying?: boolean;
pausedAt?: Call['id'];
calls: Map<string, any>;
endRef?: React.Ref<HTMLDivElement>;
onScrollToEnd?: () => void;
Expand Down Expand Up @@ -66,6 +67,7 @@ export const AddonPanelPure: React.FC<InteractionsPanelProps> = React.memo(
fileName,
hasException,
isPlaying,
pausedAt,
onScrollToEnd,
endRef,
isRerunAnimating,
Expand All @@ -87,15 +89,18 @@ export const AddonPanelPure: React.FC<InteractionsPanelProps> = React.memo(
setIsRerunAnimating={setIsRerunAnimating}
/>
)}
{interactions.map((call) => (
<Interaction
key={call.id}
call={call}
callsById={calls}
controls={controls}
controlStates={controlStates}
/>
))}
<div>
{interactions.map((call) => (
<Interaction
key={call.id}
call={call}
callsById={calls}
controls={controls}
controlStates={controlStates}
pausedAt={pausedAt}
/>
))}
</div>
<div ref={endRef} />
{!isPlaying && interactions.length === 0 && (
<Placeholder>
Expand All @@ -116,6 +121,7 @@ export const AddonPanelPure: React.FC<InteractionsPanelProps> = React.memo(
export const Panel: React.FC<AddonPanelProps> = (props) => {
const [storyId, setStoryId] = React.useState<StoryId>();
const [controlStates, setControlStates] = React.useState<ControlStates>(INITIAL_CONTROL_STATES);
const [pausedAt, setPausedAt] = React.useState<Call['id']>();
const [isPlaying, setPlaying] = React.useState(false);
const [isRerunAnimating, setIsRerunAnimating] = React.useState(false);
const [scrollTarget, setScrollTarget] = React.useState<HTMLElement>();
Expand Down Expand Up @@ -146,10 +152,12 @@ export const Panel: React.FC<AddonPanelProps> = (props) => {
[EVENTS.SYNC]: (payload) => {
setControlStates(payload.controlStates);
setLog(payload.logItems);
setPausedAt(payload.pausedAt);
},
[STORY_RENDER_PHASE_CHANGED]: (event) => {
setStoryId(event.storyId);
setPlaying(event.newPhase === 'playing');
setPausedAt(undefined);
},
},
[]
Expand Down Expand Up @@ -191,6 +199,7 @@ export const Panel: React.FC<AddonPanelProps> = (props) => {
fileName={fileName}
hasException={hasException}
isPlaying={isPlaying}
pausedAt={pausedAt}
endRef={endRef}
onScrollToEnd={scrollTarget && scrollToTarget}
isRerunAnimating={isRerunAnimating}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export const StandardEmailFailed: CSF3Story = {
await userEvent.click(canvas.getByRole('button', { name: /create account/i }));

await canvas.findByText('Please enter a correctly formatted email address');
expect(args.onSubmit).not.toHaveBeenCalled();
await expect(args.onSubmit).not.toHaveBeenCalled();
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ComponentStoryObj, ComponentMeta } from '@storybook/react';
import { expect } from '@storybook/jest';
import { CallStates } from '@storybook/instrumenter';
import { userEvent, within } from '@storybook/testing-library';
import { getCall } from '../../mocks';
import { getCalls } from '../../mocks';

import { Interaction } from './Interaction';
import SubnavStories from '../Subnav/Subnav.stories';
Expand All @@ -13,33 +13,39 @@ export default {
title: 'Addons/Interactions/Interaction',
component: Interaction,
args: {
callsById: new Map(),
callsById: new Map(getCalls(CallStates.DONE).map((call) => [call.id, call])),
controls: SubnavStories.args.controls,
controlStates: SubnavStories.args.controlStates,
},
} as ComponentMeta<typeof Interaction>;

export const Active: Story = {
args: {
call: getCall(CallStates.ACTIVE),
call: getCalls(CallStates.ACTIVE).slice(-1)[0],
},
};

export const Waiting: Story = {
args: {
call: getCall(CallStates.WAITING),
call: getCalls(CallStates.WAITING).slice(-1)[0],
},
};

export const Failed: Story = {
args: {
call: getCall(CallStates.ERROR),
call: getCalls(CallStates.ERROR).slice(-1)[0],
},
};

export const Done: Story = {
args: {
call: getCall(CallStates.DONE),
call: getCalls(CallStates.DONE).slice(-1)[0],
},
};

export const WithParent: Story = {
args: {
call: { ...getCalls(CallStates.DONE).slice(-1)[0], parentId: 'parent-id' },
},
};

Expand Down
94 changes: 69 additions & 25 deletions addons/interactions/src/components/Interaction/Interaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,45 @@ const MethodCallWrapper = styled.div(() => ({
inlineSize: 'calc( 100% - 40px )',
}));

const RowContainer = styled('div', { shouldForwardProp: (prop) => !['call'].includes(prop) })<{
call: Call;
}>(({ theme, call }) => ({
display: 'flex',
flexDirection: 'column',
borderBottom: `1px solid ${theme.appBorderColor}`,
fontFamily: typography.fonts.base,
fontSize: 13,
...(call.status === CallStates.ERROR && {
backgroundColor:
theme.base === 'dark' ? transparentize(0.93, theme.color.negative) : theme.background.warning,
const RowContainer = styled('div', {
shouldForwardProp: (prop) => !['call', 'pausedAt'].includes(prop),
})<{ call: Call; pausedAt: Call['id'] }>(
({ theme, call }) => ({
position: 'relative',
display: 'flex',
flexDirection: 'column',
borderBottom: `1px solid ${theme.appBorderColor}`,
fontFamily: typography.fonts.base,
fontSize: 13,
...(call.status === CallStates.ERROR && {
backgroundColor:
theme.base === 'dark'
? transparentize(0.93, theme.color.negative)
: theme.background.warning,
}),
paddingLeft: call.parentId ? 20 : 0,
}),
}));
({ theme, call, pausedAt }) =>
pausedAt === call.id && {
'&::before': {
content: '""',
position: 'absolute',
top: -5,
zIndex: 1,
borderTop: '4.5px solid transparent',
borderLeft: `7px solid ${theme.color.warning}`,
borderBottom: '4.5px solid transparent',
},
'&::after': {
content: '""',
position: 'absolute',
top: -1,
zIndex: 1,
width: '100%',
borderTop: `1.5px solid ${theme.color.warning}`,
},
}
);

const RowLabel = styled('button', { shouldForwardProp: (prop) => !['call'].includes(prop) })<
React.ButtonHTMLAttributes<HTMLButtonElement> & { call: Call }
Expand Down Expand Up @@ -55,30 +81,52 @@ const RowLabel = styled('button', { shouldForwardProp: (prop) => !['call'].inclu
},
}));

const RowMessage = styled('pre')({
margin: 0,
padding: '8px 10px 8px 30px',
const RowMessage = styled('div')(({ theme }) => ({
padding: '8px 10px 8px 36px',
fontSize: typography.size.s1,
});
pre: {
margin: 0,
padding: 0,
},
p: {
color: theme.color.dark,
},
}));

const Exception = ({ exception }: { exception: Call['exception'] }) => {
if (exception.message.startsWith('expect(')) {
return <MatcherResult {...exception} />;
}
const paragraphs = exception.message.split('\n\n');
const more = paragraphs.length > 1;
return (
<RowMessage>
<pre>{paragraphs[0]}</pre>
{more && <p>See the full stack trace in the browser console.</p>}
ghengeveld marked this conversation as resolved.
Show resolved Hide resolved
</RowMessage>
);
};

export const Interaction = ({
call,
callsById,
controls,
controlStates,
pausedAt,
}: {
call: Call;
callsById: Map<Call['id'], Call>;
controls: Controls;
controlStates: ControlStates;
pausedAt?: Call['id'];
}) => {
const [isHovered, setIsHovered] = React.useState(false);
return (
<RowContainer call={call}>
<RowContainer call={call} pausedAt={pausedAt}>
<RowLabel
call={call}
onClick={() => controls.goto(call.id)}
disabled={!controlStates.goto}
disabled={!controlStates.goto || !call.interceptable || !!call.parentId}
onMouseEnter={() => controlStates.goto && setIsHovered(true)}
onMouseLeave={() => controlStates.goto && setIsHovered(false)}
>
Expand All @@ -87,13 +135,9 @@ export const Interaction = ({
<MethodCall call={call} callsById={callsById} />
</MethodCallWrapper>
</RowLabel>
{call.status === CallStates.ERROR &&
call.exception &&
(call.exception.message.startsWith('expect(') ? (
<MatcherResult {...call.exception} />
) : (
<RowMessage>{call.exception.message}</RowMessage>
))}
{call.status === CallStates.ERROR && call.exception?.callId === call.id && (
<Exception exception={call.exception} />
)}
</RowContainer>
);
};
2 changes: 1 addition & 1 deletion addons/interactions/src/components/MatcherResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const MatcherResult = ({ message }: { message: string }) => {
<pre
style={{
margin: 0,
padding: '8px 10px 8px 30px',
padding: '8px 10px 8px 36px',
fontSize: typography.size.s1,
}}
>
Expand Down
Loading