-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
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
Addon-jest: UI Redesign #7424
Merged
Merged
Addon-jest: UI Redesign #7424
Changes from 46 commits
Commits
Show all changes
47 commits
Select commit
Hold shift + click to select a range
c68deb5
removed color file from message class
CodeByAlex eb42c1a
removed colors file from jest addon
CodeByAlex 20d3f8e
removed indicator component from jest addon
CodeByAlex 08fc66f
modified panel and result of the jest testing addon to look and feel …
CodeByAlex 3a93467
removed console.log from message component
CodeByAlex 58e3d6e
changed green and red progress colors to theme colors
CodeByAlex 189f785
changed tab button to accept text color variable
CodeByAlex 616dbbb
added story for tab components to show button text color being set
CodeByAlex bca648b
used textColor in the panel to set tab buttons
CodeByAlex 4847b51
added background color prop to bar props
CodeByAlex 8586060
added background color in tabs component
CodeByAlex 3f0bc57
added background color to panel where tabs component is used
CodeByAlex 660178e
modified jest config so that jest tests could run in the official sto…
CodeByAlex 6a0961d
refactored message component to reduce code complexity - still need t…
CodeByAlex 35dbfbb
Update Message.tsx
CodeByAlex 1cad06a
added test vs tests condition for display value
CodeByAlex 59a78ea
hide pointer and hover state on passing tests
CodeByAlex 6af0198
Merge branch 'feature/jest-redesign' of https://github.com/CodeByAlex…
CodeByAlex b74209c
added trim to stack trace and !== instead of !=
CodeByAlex 2a7a2cf
pulled out message parse functionality from component into separate …
CodeByAlex 071a69c
colorized text in message component of the jest addon
CodeByAlex c55b0da
bumped jest versions in official example
CodeByAlex bd3f0fa
Merge branch 'next' into feature/jest-redesign
CodeByAlex 9f232c5
updated message component to be less specific in the result condition…
CodeByAlex 847d90d
changed variable name i to index
CodeByAlex aaa476b
Ensured backwards compatibility with previous test result formatting …
CodeByAlex b8f709d
used theme colors where possible and fixed commit hook errors
CodeByAlex f969b5f
removed unused code
CodeByAlex c61518a
Merge branch 'next' of https://github.com/storybooks/storybook into f…
CodeByAlex 9eabbbb
removed console log
CodeByAlex 045c024
added spacing
CodeByAlex 47829cc
updated yarn lock
CodeByAlex 5d0c2ec
Fixed flex bar props
CodeByAlex 2accbf0
Fixed ts lint issues
CodeByAlex 3d0790e
fixed ts issues
CodeByAlex b8c8b00
Fixed tabs story
CodeByAlex 5842e84
Merge branch 'next' of https://github.com/storybooks/storybook into f…
CodeByAlex 2954e2a
modified font size and color for the test detail section
CodeByAlex 4df0199
made the progress information mobile friendly
CodeByAlex 1da3506
pulll width out
CodeByAlex 87bba55
Increased width breakpoint
CodeByAlex c197a9b
Removed @ts-ignore from jest panel
CodeByAlex 9c57cfa
converted colors to tokens and removed passing rate
CodeByAlex 0669a80
Merge branch 'feature/jest-redesign' of ssh://github.com/storybookjs/…
CodeByAlex 1948cc2
corrected theme usage
CodeByAlex b1d31db
convert result component to hook
CodeByAlex 214504a
Merge branch 'next' into feature/jest-redesign
ndelangen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,205 +1,164 @@ | ||
/* eslint-disable react/no-array-index-key */ | ||
/* eslint-disable no-param-reassign */ | ||
/* eslint-disable no-control-regex */ | ||
/* tslint:disable:object-literal-sort-keys */ | ||
|
||
import React from 'react'; | ||
import React, { Fragment } from 'react'; | ||
import { styled } from '@storybook/theming'; | ||
import colors from '../colors'; | ||
|
||
const patterns = [/^\x08+/, /^\x1b\[[012]?K/, /^\x1b\[?[\d;]{0,3}/]; | ||
const positiveConsoleRegex = /\[32m(.*?)\[39m/; | ||
const negativeConsoleRegex = /\[31m(.*?)\[39m/; | ||
const positiveType = 'positive'; | ||
const negativeType = 'negative'; | ||
const endToken = '[39m'; | ||
const failStartToken = '[31m'; | ||
const passStartToken = '[32m'; | ||
const stackTraceStartToken = 'at'; | ||
const titleEndToken = ':'; | ||
|
||
const Pre = styled.pre({ | ||
margin: 0, | ||
}); | ||
type MsgElement = string | JSX.Element; | ||
|
||
const Positive = styled.strong({ | ||
color: colors.success, | ||
fontWeight: 500, | ||
}); | ||
const Negative = styled.strong({ | ||
color: colors.error, | ||
fontWeight: 500, | ||
}); | ||
class TestDetail { | ||
description: MsgElement[]; | ||
|
||
interface StackTraceProps { | ||
trace: MsgElement[]; | ||
className?: string; | ||
} | ||
result: MsgElement[]; | ||
|
||
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: '#e2e2e2', | ||
padding: 10, | ||
stackTrace: string; | ||
} | ||
const StackTrace = styled.pre<{}>(({ theme }) => ({ | ||
background: theme.color.lighter, | ||
paddingTop: '4px', | ||
paddingBottom: '4px', | ||
paddingLeft: '6px', | ||
borderRadius: '2px', | ||
overflow: 'auto', | ||
margin: '10px 30px 10px 30px', | ||
whiteSpace: 'pre', | ||
})); | ||
|
||
const Results = styled.div({ | ||
paddingTop: '10px', | ||
marginLeft: '31px', | ||
marginRight: '30px', | ||
}); | ||
|
||
const Main = styled(({ msg, className }) => <section className={className}>{msg}</section>)({ | ||
padding: 10, | ||
borderBottom: '1px solid #e2e2e2', | ||
}); | ||
|
||
interface SubProps { | ||
msg: MsgElement[]; | ||
className?: string; | ||
} | ||
const Description = styled.div<{}>(({ theme }) => ({ | ||
paddingBottom: '10px', | ||
paddingTop: '10px', | ||
borderBottom: theme.appBorderColor, | ||
marginLeft: '31px', | ||
marginRight: '30px', | ||
overflowWrap: 'break-word', | ||
})); | ||
|
||
const StatusColor = styled.strong<{ status: string }>(({ status, theme }) => ({ | ||
color: status === positiveType ? theme.color.positive : theme.color.negative, | ||
fontWeight: 500, | ||
})); | ||
|
||
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') { | ||
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, | ||
const Main = styled(({ msg, className }) => <section className={className}>{msg}</section>)({ | ||
padding: 5, | ||
}); | ||
|
||
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'; | ||
const colorizeText: (msg: string, type: string) => MsgElement[] = (msg: string, type: string) => { | ||
if (type) { | ||
return msg | ||
.split(type === positiveType ? positiveConsoleRegex : negativeConsoleRegex) | ||
.map((i, index) => | ||
index % 2 ? ( | ||
<StatusColor key={`${type}_${i}`} status={type}> | ||
{i} | ||
</StatusColor> | ||
) : ( | ||
i | ||
) | ||
); | ||
} | ||
return [msg]; | ||
}; | ||
|
||
// push item in correct aggregator | ||
if (mode === 'inject') { | ||
grouped.push(item); | ||
} else { | ||
accList.push(item); | ||
} | ||
const getConvertedText: (msg: string) => MsgElement[] = (msg: string) => { | ||
let elementArray: MsgElement[] = []; | ||
|
||
if (!msg) return elementArray; | ||
|
||
// 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) => { | ||
if (injectionPoint === 0 && ei === 0) { | ||
// at index 0, inject before | ||
return eacc.concat(grouper(grouped, grouperIndex)).concat(el); | ||
const splitText = msg | ||
.split(/\[2m/) | ||
.join('') | ||
.split(/\[22m/); | ||
|
||
splitText.forEach(element => { | ||
if (element && element.trim()) { | ||
if ( | ||
element.indexOf(failStartToken) > -1 && | ||
element.indexOf(failStartToken) < element.indexOf(endToken) | ||
) { | ||
elementArray = elementArray.concat(colorizeText(element, negativeType)); | ||
} else if ( | ||
element.indexOf(passStartToken) > -1 && | ||
element.indexOf(passStartToken) < element.indexOf(endToken) | ||
) { | ||
elementArray = elementArray.concat(colorizeText(element, positiveType)); | ||
} else { | ||
elementArray = elementArray.concat(element); | ||
} | ||
if (injectionPoint > 0 && injectionPoint === ei + 1) { | ||
// at index > 0, and next index WOULD BE injectionPoint, inject after | ||
return eacc.concat(el).concat(grouper(grouped, grouperIndex)); | ||
} | ||
}); | ||
return elementArray; | ||
}; | ||
|
||
const getTestDetail: (msg: string) => TestDetail = (msg: string) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a couple unit tests for this? |
||
const lines = msg.split('\n').filter(Boolean); | ||
|
||
const testDetail: TestDetail = new TestDetail(); | ||
testDetail.description = getConvertedText(lines[0]); | ||
testDetail.stackTrace = ''; | ||
testDetail.result = []; | ||
|
||
for (let index = 1; index < lines.length; index += 1) { | ||
const current = lines[index]; | ||
const next = lines[index + 1]; | ||
|
||
if ( | ||
current | ||
.trim() | ||
.toLowerCase() | ||
.indexOf(stackTraceStartToken) === 0 | ||
) { | ||
testDetail.stackTrace += `${current.trim()}\n`; | ||
} else if (current.trim().indexOf(titleEndToken) > -1) { | ||
let title; | ||
let value = null; | ||
if (current.trim().indexOf(titleEndToken) === current.length - 1) { | ||
// there are breaks in the middle of result | ||
title = current.trim(); | ||
value = getConvertedText(next); | ||
index += 1; | ||
} else { | ||
// results come in a single line | ||
title = current.substring(0, current.indexOf(titleEndToken)).trim(); | ||
value = getConvertedText(current.substring(current.indexOf(titleEndToken), current.length)); | ||
} | ||
// do not inject | ||
return eacc.concat(el); | ||
}, []); | ||
testDetail.result = [...testDetail.result, title, ' ', ...value, <br key={index} />]; | ||
} else { | ||
// results come in an unexpected format | ||
testDetail.result = [...testDetail.result, ' ', ...getConvertedText(current)]; | ||
} | ||
} | ||
return acc; | ||
|
||
return testDetail; | ||
}; | ||
|
||
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((acc, item) => acc.concat(item), [] as string[]) | ||
.map((item, li) => | ||
item | ||
.split(/\[32m(.*?)\[39m/) | ||
.map((i, index) => (index % 2 ? <Positive key={`p_${li}_${i}`}>{i}</Positive> : i)) | ||
) | ||
.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), []) | ||
.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( | ||
(acc, it) => | ||
typeof it === 'string' ? acc.concat(it.split(/(at(.|\n)+\d+:\d+\))/)) : acc.concat(it), | ||
[] as MsgElement[] | ||
) | ||
.reduce((acc, item) => acc.concat(item), [] as MsgElement[]) | ||
.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 const Message = (props: any) => { | ||
const { msg } = props; | ||
|
||
const detail: TestDetail = getTestDetail(msg); | ||
return ( | ||
<Fragment> | ||
{detail.description ? <Description>{detail.description}</Description> : null} | ||
{detail.result ? <Results>{detail.result}</Results> : null} | ||
{detail.stackTrace ? <StackTrace>{detail.stackTrace}</StackTrace> : null} | ||
</Fragment> | ||
); | ||
}; | ||
|
||
export default Message; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a couple unit tests for this?