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

Migrate @storybook/addon-jest to TS #6403

Merged
merged 13 commits into from
Apr 8, 2019
4 changes: 2 additions & 2 deletions addons/jest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@
"license": "MIT",
"author": "Renaud Tertrais <[email protected]> (https://github.com/renaudtertrais)",
"main": "dist/index.js",
"jsnext:main": "src/index.js",
"types": "dist/index.d.ts",
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.1.0-alpha.21",
"@storybook/api": "^5.1.0-alpha.21",
"@storybook/components": "5.1.0-alpha.21",
"@storybook/core-events": "5.1.0-alpha.21",
"@storybook/theming": "5.1.0-alpha.21",
"core-js": "^2.6.5",
"global": "^4.3.2",
"prop-types": "^15.7.2",
"react": "^16.8.4",
"upath": "^1.1.0",
"util-deprecate": "^1.0.2"
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import PropTypes from 'prop-types';
import React from 'react';

import { styled } from '@storybook/theming';

const Indicator = styled.div(
interface IndicatorProps {
color: string;
size: number;
children?: React.ReactNode;
right?: boolean;
overrides?: any;
styles?: any;
sairus2k marked this conversation as resolved.
Show resolved Hide resolved
}

const Indicator = styled.div<IndicatorProps>(
({ color, size }) => ({
boxSizing: 'border-box',
padding: `0 ${size / 2}px`,
Expand All @@ -25,11 +34,4 @@ Indicator.defaultProps = {
children: '',
};

Indicator.propTypes = {
color: PropTypes.string.isRequired,
size: PropTypes.number.isRequired,
children: PropTypes.node,
right: PropTypes.bool,
};

export default Indicator;
208 changes: 208 additions & 0 deletions addons/jest/src/components/Message.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/* tslint:disable:object-literal-sort-keys */

import React from 'react';
import { styled } from '@storybook/theming';
import colors from '../colors';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these colours should come from the theme if possible

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I don't find similar colors in lib/theming/src/base.ts


const patterns = [/^\x08+/, /^\x1b\[[012]?K/, /^\x1b\[?[\d;]{0,3}/];

const Pre = styled.pre({
margin: 0,
});

const Positive = styled.strong({
color: colors.success,
fontWeight: 500,
});
const Negative = styled.strong({
color: colors.error,
fontWeight: 500,
});

interface StackTraceProps {
trace: MsgElement[];
className?: string;
}

const StackTrace = styled(({ trace, className }: StackTraceProps) => (
<details className={className}>
<summary>Callstack</summary>
{trace
.join('')
.trim()
.split(/\n/)
.map((traceLine, traceLineIndex) => (
<div key={traceLineIndex}>{traceLine.trim()}</div>
))}
</details>
))({
background: 'silver',
padding: 10,
overflow: 'auto',
});

const Main = styled(({ msg, className }) => <section className={className}>{msg}</section>)({
padding: 10,
borderBottom: '1px solid silver',
});

interface SubProps {
msg: MsgElement[];
className?: string;
}

const Sub = styled(({ msg, className }: SubProps) => (
<section className={className}>
{msg
.filter(item => typeof item !== 'string' || item.trim() !== '')
.map((item, index, list) => {
if (typeof item === 'string') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems odd you filter for anything that isn't a string, then do something for only strings. seems like this code could be clearer

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It filters for not empty strings, then removes line breaks for those strings

if (index === 0 && index === list.length - 1) {
return item.trim();
}
if (index === 0) {
return item.replace(/^[\s\n]*/, '');
}
if (index === list.length - 1) {
return item.replace(/[\s\n]*$/, '');
}
}
return item;
})}
</section>
))({
padding: 10,
});

interface SubgroupOptions {
startTrigger: (e: MsgElement) => boolean;
endTrigger: (e: MsgElement) => boolean;
grouper: (list: MsgElement[], key: number) => JSX.Element;
accList?: MsgElement[];
grouped?: MsgElement[];
grouperIndex?: number;
mode?: 'inject' | 'stop';
injectionPoint?: number;
}

const createSubgroup = ({
startTrigger,
endTrigger,
grouper,
accList = [],
grouped = [],
grouperIndex = 0,
mode,
injectionPoint,
}: SubgroupOptions) => (acc: MsgElement[], item: MsgElement, i: number, list: MsgElement[]) => {
grouperIndex += 1;

// start or stop extraction
if (startTrigger(item)) {
mode = 'inject';
injectionPoint = i;
}
if (endTrigger(item)) {
mode = 'stop';
}

// push item in correct aggregator
if (mode === 'inject') {
grouped.push(item);
} else {
accList.push(item);
}

// on last iteration inject at detected injection point, and group
if (i === list.length - 1) {
// Provide a "safety net" when Jest returns a partially recognized "group"
// (recognized by acc.startTrigger but acc.endTrigger was never found) and
// it's the only group in output for a test result. In that case, accList
// will be empty, so return whatever was found, even if it will be unstyled
// and prevent next createSubgroup calls from throwing due to empty lists.
accList.push(null);

return accList.reduce<MsgElement[]>((eacc, el, ei) => {
switch (true) {
sairus2k marked this conversation as resolved.
Show resolved Hide resolved
case injectionPoint === 0 && ei === 0: {
// at index 0, inject before
return eacc.concat(grouper(grouped, grouperIndex)).concat(el);
}
case injectionPoint > 0 && injectionPoint === ei + 1: {
// at index > 0, and next index WOULD BE injectionPoint, inject after
return eacc.concat(el).concat(grouper(grouped, grouperIndex));
}
default: {
// do not inject
return eacc.concat(el);
}
}
}, []);
}
return acc;
};

interface MessageProps {
msg: string;
}

type MsgElement = string | JSX.Element;

const Message = ({ msg }: MessageProps) => {
const data = patterns
.reduce((acc, regex) => acc.replace(regex, ''), msg)
.split(/\[2m/)
.join('')
.split(/\[22m/)
.reduce<string[]>((acc, item) => acc.concat(item), [])
ndelangen marked this conversation as resolved.
Show resolved Hide resolved
.map((item, li) =>
typeof item === 'string'
sairus2k marked this conversation as resolved.
Show resolved Hide resolved
? item
.split(/\[32m(.*?)\[39m/)
.map((i, index) => (index % 2 ? <Positive key={`p_${li}_${i}`}>{i}</Positive> : i))
: item
)
.reduce((acc, item) => acc.concat(item))
.map((item, li) =>
typeof item === 'string'
? item
.split(/\[31m(.*?)\[39m/)
.map((i, index) => (index % 2 ? <Negative key={`n_${li}_${i}`}>{i}</Negative> : i))
: item
)
.reduce<MsgElement[]>((acc, item) => acc.concat(item), [])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.reduce<MsgElement[]>((acc, item) => acc.concat(item), [])
.reduce((acc, item) => acc.concat(item), [] as MsgElement[])

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case TS throws error Property 'concat' does not exist on type Element

.reduce(
createSubgroup({
startTrigger: e => typeof e === 'string' && e.indexOf('Error: ') === 0,
endTrigger: e => typeof e === 'string' && Boolean(e.match('Expected ')),
grouper: (list, key) => <Main key={key} msg={list} />,
}),
[]
)
.reduce<MsgElement[]>(
sairus2k marked this conversation as resolved.
Show resolved Hide resolved
(acc, it) =>
typeof it === 'string' ? acc.concat(it.split(/(at(.|\n)+\d+:\d+\))/)) : acc.concat(it),
[]
)
.reduce<MsgElement[]>((acc, item) => acc.concat(item), [])
sairus2k marked this conversation as resolved.
Show resolved Hide resolved
.reduce(
createSubgroup({
startTrigger: e => typeof e === 'string' && e.indexOf('Expected ') !== -1,
endTrigger: e => typeof e === 'string' && Boolean(e.match(/^at/)),
grouper: (list, key) => <Sub key={key} msg={list} />,
}),
[]
)
.reduce(
createSubgroup({
startTrigger: e => typeof e === 'string' && Boolean(e.match(/at(.|\n)+\d+:\d+\)/)),
endTrigger: () => false,
grouper: (list, key) => <StackTrace key={key} trace={list} />,
}),
[]
);

return <Pre>{data}</Pre>;
};

export default Message;
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import { styled } from '@storybook/theming';
import { ScrollArea } from '@storybook/components';

import Indicator from './Indicator';
import Result, { FailedResult } from './Result';
import provideJestResult from '../hoc/provideJestResult';
import provideJestResult, { Test } from '../hoc/provideJestResult';
import colors from '../colors';

const List = styled.ul({
Expand Down Expand Up @@ -95,7 +94,12 @@ const SuiteTitle = styled.div({
alignItems: 'center',
});

const Content = styled(({ tests, className }) => (
interface ContentProps {
tests: Test[];
className?: string;
}

const Content = styled(({ tests, className }: ContentProps) => (
<div className={className}>
{tests.map(({ name, result }) => {
if (!result) {
Expand Down Expand Up @@ -142,7 +146,11 @@ const Content = styled(({ tests, className }) => (
flex: '1 1 0%',
});

const Panel = ({ tests }) => (
interface PanelProps {
tests: null | Test[];
}

const Panel = ({ tests }: PanelProps) => (
<ScrollArea vertical>
{tests ? <Content tests={tests} /> : <NoTests>This story has no tests configured</NoTests>}
</ScrollArea>
Expand All @@ -152,12 +160,4 @@ Panel.defaultProps = {
tests: null,
};

Panel.propTypes = {
tests: PropTypes.arrayOf(
PropTypes.shape({
result: PropTypes.object,
})
),
};

export default provideJestResult(Panel);
47 changes: 0 additions & 47 deletions addons/jest/src/components/PanelTitle.js

This file was deleted.

Loading