Skip to content
Open
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
"publishConfig": {
"access": "public"
},
"gitHead": "a8e7fd8a655c69780bc20b9749d2699e45beae16",
"gitHead": "a8e7fd8a655c69780bc20b9749d2699e45beae17",
"storybook": {
"displayName": "Jest",
"icon": "https://pbs.twimg.com/profile_images/821713465245102080/mMtKIMax_400x400.jpg",
Expand Down
274 changes: 152 additions & 122 deletions src/components/Panel.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { FC } from 'react';
import React, { Fragment } from 'react';
import React, { Fragment, useMemo } from 'react';

import { Link, Placeholder, ScrollArea, TabsState } from 'storybook/internal/components';
import { Badge, Link, Placeholder, ScrollArea, TabsView } from 'storybook/internal/components';

Check failure on line 4 in src/components/Panel.tsx

View workflow job for this annotation

GitHub Actions / Build

Module '"storybook/internal/components"' has no exported member 'TabsView'.

import { useResizeDetector } from 'react-resize-detector';
import { convert, styled, themes } from 'storybook/theming';

import type { Test } from '../hoc/provideJestResult';
import type { AssertionResult, Test } from '../hoc/provideJestResult';
import { provideTests as provideJestResult } from '../hoc/provideJestResult';
import { Result } from './Result';

Expand All @@ -30,20 +30,15 @@
});

const ProgressWrapper = styled.div({
position: 'relative',
height: 10,
width: 30,
display: 'flex',
top: -2,
});

const SuiteHead = styled.div({
display: 'flex',
alignItems: 'baseline',
position: 'absolute',
zIndex: 2,
right: 20,
marginTop: 15,
alignItems: 'center',
marginInlineEnd: 5,
gap: 15,
});

const UnstyledSuiteTotals: FC<{
Expand Down Expand Up @@ -72,30 +67,31 @@
alignItems: 'center',
color: theme.color.dark,
fontSize: '14px',
marginTop: -5,
'& > *': {
marginRight: 10,
},
flexShrink: 0,
}));

const SuiteProgressPortion = styled.div<{ color?: string; progressPercent: number }>(({ color, progressPercent }) => ({
height: 6,
top: 3,
width: `${progressPercent}%`,
backgroundColor: color,
}));
const SuiteProgressPortion = styled.div<{ color?: string; progressPercent: number }>(
({ color, progressPercent }) => ({
height: 6,
top: 3,
width: `${progressPercent}%`,
backgroundColor: color,
})
);

interface ContentProps {
tests: Test[];
className?: string;
}

const getTestsByTypeMap = (result: any) => {
const testsByType: Map<string, any> = new Map();
result.assertionResults.forEach((assertion: any) => {
const getTestsByTypeMap = (result: Test['result']) => {
const testsByType: Map<string, AssertionResult[]> = new Map();
result.assertionResults.forEach((assertion) => {
const existingTestsForType = testsByType.get(assertion.status);

testsByType.set(
assertion.status,
testsByType.get(assertion.status) ? testsByType.get(assertion.status).concat(assertion) : [assertion]
existingTestsForType ? existingTestsForType.concat(assertion) : [assertion]
);
});
return testsByType;
Expand All @@ -117,111 +113,133 @@
}
};

const TabItemWrapper = styled.div({
display: 'flex',
alignItems: 'center',
gap: 6,
});

const TabItem: FC<{ count: number; title: string }> = ({ count, title }) => (
<TabItemWrapper>
{title}
<Badge compact status="neutral">
{count}
</Badge>
</TabItemWrapper>
);

const TabPanel: FC<{
emptyMessage: string;
tests: AssertionResult[];
}> = ({ emptyMessage, tests }) => (
<List>
{tests.length ? (
tests?.map((res: AssertionResult) => (
<Item key={res.fullName || res.title}>
<Result {...res} />
</Item>
))
) : (
<Placeholder>{emptyMessage}</Placeholder>
)}
</List>
);

const TestPanel: FC<{ test: Test }> = ({ test }) => {
const { ref, width } = useResizeDetector();
const { result } = test;
if (!result || !result.assertionResults) {
return <Placeholder>This story has tests configured, but no file was found</Placeholder>;
}

const testsByType: Map<string, any> = getTestsByTypeMap(result);
const entries: any = testsByType.entries();
const testsByType: Map<string, AssertionResult[]> = useMemo(
() => getTestsByTypeMap(result),
[result]
);
const tabs = useMemo(
() => [
{
id: 'failing-tests',
title: (
<TabItem count={testsByType.get(StatusTypes.FAILED_TYPE)?.length ?? 0} title="Failing" />
),
children: () => (
<TabPanel
emptyMessage="This story has no failing tests."
tests={testsByType.get(StatusTypes.FAILED_TYPE) ?? []}
/>
),
},
{
id: 'passing-tests',
title: (
<TabItem count={testsByType.get(StatusTypes.PASSED_TYPE)?.length ?? 0} title="Passing" />
),
children: () => (
<TabPanel
emptyMessage="This story has no passing tests."
tests={testsByType.get(StatusTypes.PASSED_TYPE) ?? []}
/>
),
},
{
id: 'pending-tests',
title: (
<TabItem count={testsByType.get(StatusTypes.PENDING_TYPE)?.length ?? 0} title="Pending" />
),
children: () => (
<TabPanel
emptyMessage="This story has no pending tests."
tests={testsByType.get(StatusTypes.PENDING_TYPE) ?? []}
/>
),
},
{
id: 'todo-tests',
title: (
<TabItem count={testsByType.get(StatusTypes.TODO_TYPE)?.length ?? 0} title="To Do" />
),
children: () => (
<TabPanel
emptyMessage="This story has no tests to do."
tests={testsByType.get(StatusTypes.TODO_TYPE) ?? []}
/>
),
},
],
[testsByType]
);

const entries = testsByType.entries();
const sortedTestsByCount = [...entries].sort((a, b) => a[1].length - b[1].length);

return (
<section ref={ref}>
<SuiteHead>
<SuiteTotals {...{ result, width: width ?? 0 }} />
{width != null && width > 240 ? (
<ProgressWrapper>
{sortedTestsByCount.map((entry: any) => {
return (
<SuiteProgressPortion
key={`progress-portion-${entry[0]}`}
color={getColorByType(entry[0])}
progressPercent={entry[1] ? (entry[1].length / result.assertionResults.length) * 100 : 0}
/>
);
})}
</ProgressWrapper>
) : null}
</SuiteHead>
<TabsState initial="failing-tests" backgroundColor={convert(themes.light).background.hoverable}>
<div
id="failing-tests"
title={`${
testsByType.get(StatusTypes.FAILED_TYPE) ? testsByType.get(StatusTypes.FAILED_TYPE).length : 0
} Failed`}
color={getColorByType(StatusTypes.FAILED_TYPE)}
>
<List>
{testsByType.get(StatusTypes.FAILED_TYPE) ? (
testsByType.get(StatusTypes.FAILED_TYPE).map((res: any) => (
<Item key={res.fullName || res.title}>
<Result {...res} />
</Item>
))
) : (
<Placeholder key={`no-tests-${StatusTypes.FAILED_TYPE}`}>This story has no failing tests.</Placeholder>
)}
</List>
</div>
<div
id="passing-tests"
title={`${
testsByType.get(StatusTypes.PASSED_TYPE) ? testsByType.get(StatusTypes.PASSED_TYPE).length : 0
} Passed`}
color={getColorByType(StatusTypes.PASSED_TYPE)}
>
<List>
{testsByType.get(StatusTypes.PASSED_TYPE) ? (
testsByType.get(StatusTypes.PASSED_TYPE).map((res: any) => (
<Item key={res.fullName || res.title}>
<Result {...res} />
</Item>
))
) : (
<Placeholder key={`no-tests-${StatusTypes.PASSED_TYPE}`}>This story has no passing tests.</Placeholder>
)}
</List>
</div>
<div
id="pending-tests"
title={`${
testsByType.get(StatusTypes.PENDING_TYPE) ? testsByType.get(StatusTypes.PENDING_TYPE).length : 0
} Pending`}
color={getColorByType(StatusTypes.PENDING_TYPE)}
>
<List>
{testsByType.get(StatusTypes.PENDING_TYPE) ? (
testsByType.get(StatusTypes.PENDING_TYPE).map((res: any) => (
<Item key={res.fullName || res.title}>
<Result {...res} />
</Item>
))
) : (
<Placeholder key={`no-tests-${StatusTypes.PENDING_TYPE}`}>This story has no pending tests.</Placeholder>
)}
</List>
</div>
<div
id="todo-tests"
title={`${testsByType.get(StatusTypes.TODO_TYPE) ? testsByType.get(StatusTypes.TODO_TYPE).length : 0} Todo`}
color={getColorByType(StatusTypes.TODO_TYPE)}
>
<List>
{testsByType.get(StatusTypes.TODO_TYPE) ? (
testsByType.get(StatusTypes.TODO_TYPE).map((res: any) => (
<Item key={res.fullName || res.title}>
<Result {...res} />
</Item>
))
) : (
<Placeholder key={`no-tests-${StatusTypes.TODO_TYPE}`}>This story has no tests todo.</Placeholder>
)}
</List>
</div>
</TabsState>
<TabsView
defaultSelected="failing-tests"
backgroundColor={convert(themes.light).background.hoverable}
tabs={tabs}
tools={
<SuiteHead>
<SuiteTotals {...{ result, width: width ?? 0 }} />
{width != null && width > 240 ? (
<ProgressWrapper>
{sortedTestsByCount.map((entry) => {
return (
<SuiteProgressPortion
key={`progress-portion-${entry[0]}`}
color={getColorByType(entry[0])}
progressPercent={
entry[1] ? (entry[1].length / result.assertionResults.length) * 100 : 0
}
/>
);
})}
</ProgressWrapper>
) : null}
</SuiteHead>
}
/>
</section>
);
};
Expand All @@ -240,20 +258,32 @@
tests?: Test[];
}

const TallPlaceholder = styled(Placeholder)({
height: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
});

const Panel = ({ tests }: PanelProps) => (
<ScrollArea vertical>
{tests ? (
<Content tests={tests} />
) : (
<Placeholder>
<TallPlaceholder>
<Fragment>No tests found</Fragment>
<Fragment>
Learn how to&nbsp;
<Link href="https://github.com/storybookjs/storybook/tree/master/addons/jest" target="_blank" withArrow>
<Link
href="https://github.com/storybookjs/storybook/tree/master/addons/jest"
target="_blank"
withArrow
>
add Jest test results to your story
</Link>
</Fragment>
</Placeholder>
</TallPlaceholder>
)}
</ScrollArea>
);
Expand Down
2 changes: 1 addition & 1 deletion src/hoc/provideJestResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { API } from 'storybook/manager-api';
import { ADD_TESTS } from '../shared';

// TODO: import type from @types/jest
interface AssertionResult {
export interface AssertionResult {
status: string;
fullName: string;
title: string;
Expand Down
Loading