Skip to content

Commit

Permalink
Merge pull request #18555 from storybookjs/ghengeveld/ap-1859-generic…
Browse files Browse the repository at this point in the history
…-step-function

Interactions: Add `step` function and support multiple levels of nesting
  • Loading branch information
ghengeveld authored Aug 19, 2022
2 parents 65e222d + bfa4326 commit 2dc0cbe
Show file tree
Hide file tree
Showing 42 changed files with 676 additions and 405 deletions.
33 changes: 28 additions & 5 deletions code/addons/interactions/src/Panel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,30 @@ describe('Panel', () => {
{
callId: 'story--id [4] findByText',
status: CallStates.DONE,
ancestors: [],
},
{
callId: 'story--id [5] click',
status: CallStates.DONE,
ancestors: [],
},
{
callId: 'story--id [6] waitFor',
status: CallStates.DONE,
ancestors: [],
},
{
callId: 'story--id [6] waitFor [2] toHaveBeenCalledWith',
parentId: 'story--id [6] waitFor',
status: CallStates.DONE,
ancestors: ['story--id [6] waitFor'],
},
];
const calls = new Map<Call['id'], Call>(
[
{
id: 'story--id [0] action',
storyId: 'story--id',
ancestors: [],
cursor: 0,
path: [],
method: 'action',
Expand All @@ -37,6 +41,7 @@ describe('Panel', () => {
{
id: 'story--id [1] action',
storyId: 'story--id',
ancestors: [],
cursor: 1,
path: [],
method: 'action',
Expand All @@ -47,6 +52,7 @@ describe('Panel', () => {
{
id: 'story--id [2] action',
storyId: 'story--id',
ancestors: [],
cursor: 2,
path: [],
method: 'action',
Expand All @@ -57,6 +63,7 @@ describe('Panel', () => {
{
id: 'story--id [3] within',
storyId: 'story--id',
ancestors: [],
cursor: 3,
path: [],
method: 'within',
Expand All @@ -67,6 +74,7 @@ describe('Panel', () => {
{
id: 'story--id [4] findByText',
storyId: 'story--id',
ancestors: [],
cursor: 4,
path: [{ __callId__: 'story--id [3] within' }],
method: 'findByText',
Expand All @@ -77,6 +85,7 @@ describe('Panel', () => {
{
id: 'story--id [5] click',
storyId: 'story--id',
ancestors: [],
cursor: 5,
path: ['userEvent'],
method: 'click',
Expand All @@ -86,8 +95,8 @@ describe('Panel', () => {
},
{
id: 'story--id [6] waitFor [0] expect',
parentId: 'story--id [6] waitFor',
storyId: 'story--id',
ancestors: ['story--id [6] waitFor'],
cursor: 0,
path: [],
method: 'expect',
Expand All @@ -97,8 +106,8 @@ describe('Panel', () => {
},
{
id: 'story--id [6] waitFor [1] stringMatching',
parentId: 'story--id [6] waitFor',
storyId: 'story--id',
ancestors: ['story--id [6] waitFor'],
cursor: 1,
path: ['expect'],
method: 'stringMatching',
Expand All @@ -108,8 +117,8 @@ describe('Panel', () => {
},
{
id: 'story--id [6] waitFor [2] toHaveBeenCalledWith',
parentId: 'story--id [6] waitFor',
storyId: 'story--id',
ancestors: ['story--id [6] waitFor'],
cursor: 2,
path: [{ __callId__: 'story--id [6] waitFor [0] expect' }],
method: 'toHaveBeenCalledWith',
Expand All @@ -120,6 +129,7 @@ describe('Panel', () => {
{
id: 'story--id [6] waitFor',
storyId: 'story--id',
ancestors: [],
cursor: 6,
path: [],
method: 'waitFor',
Expand All @@ -138,51 +148,64 @@ describe('Panel', () => {
...calls.get('story--id [4] findByText'),
status: CallStates.DONE,
childCallIds: undefined,
isHidden: false,
isCollapsed: false,
toggleCollapsed: expect.any(Function),
},
{
...calls.get('story--id [5] click'),
status: CallStates.DONE,
childCallIds: undefined,
isHidden: false,
isCollapsed: false,
toggleCollapsed: expect.any(Function),
},
{
...calls.get('story--id [6] waitFor'),
status: CallStates.DONE,
childCallIds: ['story--id [6] waitFor [2] toHaveBeenCalledWith'],
isHidden: false,
isCollapsed: false,
toggleCollapsed: expect.any(Function),
},
{
...calls.get('story--id [6] waitFor [2] toHaveBeenCalledWith'),
status: CallStates.DONE,
childCallIds: undefined,
isHidden: false,
isCollapsed: false,
toggleCollapsed: expect.any(Function),
},
]);
});

it('omits calls for which the parent is collapsed', () => {
it('hides calls for which the parent is collapsed', () => {
const withCollapsed = new Set<Call['id']>(['story--id [6] waitFor']);

expect(getInteractions({ log, calls, collapsed: withCollapsed, setCollapsed })).toEqual([
expect.objectContaining({
...calls.get('story--id [4] findByText'),
childCallIds: undefined,
isCollapsed: false,
isHidden: false,
}),
expect.objectContaining({
...calls.get('story--id [5] click'),
childCallIds: undefined,
isCollapsed: false,
isHidden: false,
}),
expect.objectContaining({
...calls.get('story--id [6] waitFor'),
childCallIds: ['story--id [6] waitFor [2] toHaveBeenCalledWith'],
isCollapsed: true,
isHidden: false,
}),
expect.objectContaining({
...calls.get('story--id [6] waitFor [2] toHaveBeenCalledWith'),
childCallIds: undefined,
isCollapsed: false,
isHidden: true,
}),
]);
});
Expand Down
28 changes: 19 additions & 9 deletions code/addons/interactions/src/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { TabIcon, TabStatus } from './components/TabStatus';
interface Interaction extends Call {
status: Call['status'];
childCallIds: Call['id'][];
isHidden: boolean;
isCollapsed: boolean;
toggleCollapsed: () => void;
}
Expand Down Expand Up @@ -43,16 +44,18 @@ export const getInteractions = ({
const callsById = new Map<Call['id'], Call>();
const childCallMap = new Map<Call['id'], Call['id'][]>();
return log
.filter(({ callId, parentId }) => {
if (!parentId) return true;
childCallMap.set(parentId, (childCallMap.get(parentId) || []).concat(callId));
return !collapsed.has(parentId);
.map<Call & { isHidden: boolean }>(({ 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 };
})
.map(({ callId, status }) => ({ ...calls.get(callId), status } as Call))
.map<Interaction>((call) => {
const status =
call.status === CallStates.ERROR &&
callsById.get(call.parentId)?.status === CallStates.ACTIVE
callsById.get(call.ancestors.slice(-1)[0])?.status === CallStates.ACTIVE
? CallStates.ACTIVE
: call.status;
callsById.set(call.id, { ...call, status });
Expand Down Expand Up @@ -84,7 +87,8 @@ export const Panel: React.FC<{ active: boolean }> = (props) => {
const [interactions, setInteractions] = React.useState<Interaction[]>([]);
const [interactionsCount, setInteractionsCount] = React.useState<number>();

// Calls are tracked in a ref so we don't needlessly rerender.
// Log and calls are tracked in a ref so we don't needlessly rerender.
const log = React.useRef<LogItem[]>([]);
const calls = React.useRef<Map<Call['id'], Omit<Call, 'status'>>>(new Map());
const setCall = ({ status, ...call }: Call) => calls.current.set(call.id, call);

Expand All @@ -110,6 +114,7 @@ export const Panel: React.FC<{ active: boolean }> = (props) => {
setInteractions(
getInteractions({ log: payload.logItems, calls: calls.current, collapsed, setCollapsed })
);
log.current = payload.logItems;
},
[STORY_RENDER_PHASE_CHANGED]: (event) => {
setStoryId(event.storyId);
Expand All @@ -124,17 +129,22 @@ export const Panel: React.FC<{ active: boolean }> = (props) => {
setErrored(true);
},
[PLAY_FUNCTION_THREW_EXCEPTION]: (e) => {
console.log('PLAY_FUNCTION_THREW_EXCEPTION');
if (e?.message !== IGNORED_EXCEPTION.message) setCaughtException(e);
else setCaughtException(undefined);
},
},
[collapsed]
);

React.useEffect(() => {
setInteractions(
getInteractions({ log: log.current, calls: calls.current, collapsed, setCollapsed })
);
}, [collapsed]);

React.useEffect(() => {
if (isPlaying || isRerunAnimating) return;
setInteractionsCount(interactions.length);
setInteractionsCount(interactions.filter(({ method }) => method !== 'step').length);
}, [interactions, isPlaying, isRerunAnimating]);

const controls = React.useMemo(
Expand Down

This file was deleted.

Loading

0 comments on commit 2dc0cbe

Please sign in to comment.