|
| 1 | +import * as React from 'react'; |
| 2 | +import { Box, Color, Text } from 'ink'; |
| 3 | +import { Config } from '@jest/types'; |
| 4 | +import { AssertionResult, Suite, TestResult } from '@jest/test-result'; |
| 5 | +import { VerboseReporter } from '@jest/reporters'; |
| 6 | +import { specialChars } from 'jest-util'; |
| 7 | + |
| 8 | +const { ICONS } = specialChars; |
| 9 | + |
| 10 | +const Status: React.FC<{ status: AssertionResult['status'] }> = ({ |
| 11 | + status, |
| 12 | +}) => { |
| 13 | + if (status === 'failed') { |
| 14 | + return <Color red>{ICONS.failed}</Color>; |
| 15 | + } |
| 16 | + if (status === 'pending') { |
| 17 | + return <Color yellow>{ICONS.pending}</Color>; |
| 18 | + } |
| 19 | + if (status === 'todo') { |
| 20 | + return <Color magenta>{ICONS.todo}</Color>; |
| 21 | + } |
| 22 | + return <Color green>{ICONS.success}</Color>; |
| 23 | +}; |
| 24 | + |
| 25 | +const TestLine: React.FC<{ test: AssertionResult; indentation: number }> = ({ |
| 26 | + test, |
| 27 | + indentation, |
| 28 | +}) => ( |
| 29 | + <Box paddingLeft={indentation}> |
| 30 | + <Box paddingRight={1}> |
| 31 | + <Status status={test.status} /> |
| 32 | + </Box> |
| 33 | + <Color dim>{test.title}</Color> |
| 34 | + {test.duration ? ( |
| 35 | + <Box paddingLeft={1}> |
| 36 | + <Color dim>({test.duration.toFixed(0)}ms)</Color> |
| 37 | + </Box> |
| 38 | + ) : null} |
| 39 | + </Box> |
| 40 | +); |
| 41 | +const NotExpandedTestLine: React.FC<{ |
| 42 | + test: AssertionResult; |
| 43 | + indentation: number; |
| 44 | +}> = ({ test, indentation }) => ( |
| 45 | + <Box paddingLeft={indentation}> |
| 46 | + <Box paddingRight={1}> |
| 47 | + <Status status={test.status} /> |
| 48 | + </Box> |
| 49 | + <Color dim> |
| 50 | + {test.status === 'pending' ? 'skipped' : test.status} {test.title} |
| 51 | + </Color> |
| 52 | + </Box> |
| 53 | +); |
| 54 | + |
| 55 | +const TestsLog: React.FC<{ |
| 56 | + tests: Suite['tests']; |
| 57 | + indendation: number; |
| 58 | + expand: boolean; |
| 59 | +}> = ({ tests, expand, indendation }) => { |
| 60 | + if (expand) { |
| 61 | + return ( |
| 62 | + <> |
| 63 | + {tests.map((test, i) => ( |
| 64 | + <TestLine key={i} test={test} indentation={indendation} /> |
| 65 | + ))} |
| 66 | + </> |
| 67 | + ); |
| 68 | + } |
| 69 | + |
| 70 | + const summedTests = tests.reduce<{ |
| 71 | + pending: Suite['tests']; |
| 72 | + todo: Suite['tests']; |
| 73 | + render: Suite['tests']; |
| 74 | + }>( |
| 75 | + (result, test) => { |
| 76 | + if (test.status === 'pending') { |
| 77 | + result.pending.push(test); |
| 78 | + } else if (test.status === 'todo') { |
| 79 | + result.todo.push(test); |
| 80 | + } else { |
| 81 | + result.render.push(test); |
| 82 | + } |
| 83 | + |
| 84 | + return result; |
| 85 | + }, |
| 86 | + { pending: [], todo: [], render: [] }, |
| 87 | + ); |
| 88 | + |
| 89 | + return ( |
| 90 | + <Box flexDirection="column"> |
| 91 | + {summedTests.render.map((test, i) => ( |
| 92 | + <TestLine key={i} test={test} indentation={indendation} /> |
| 93 | + ))} |
| 94 | + {summedTests.pending.map((test, i) => ( |
| 95 | + <NotExpandedTestLine |
| 96 | + key={`pending${i}`} |
| 97 | + test={test} |
| 98 | + indentation={indendation} |
| 99 | + /> |
| 100 | + ))} |
| 101 | + {summedTests.todo.map((test, i) => ( |
| 102 | + <NotExpandedTestLine |
| 103 | + key={`pending${i}`} |
| 104 | + test={test} |
| 105 | + indentation={indendation} |
| 106 | + /> |
| 107 | + ))} |
| 108 | + </Box> |
| 109 | + ); |
| 110 | +}; |
| 111 | + |
| 112 | +const SuiteLog: React.FC<{ |
| 113 | + indendation: number; |
| 114 | + suite: Suite; |
| 115 | + globalConfig: Config.GlobalConfig; |
| 116 | +}> = ({ globalConfig, indendation, suite }) => { |
| 117 | + const newIndentation = indendation + 1; |
| 118 | + |
| 119 | + return ( |
| 120 | + <Box paddingLeft={indendation} flexDirection="column"> |
| 121 | + {suite.title ? <Text>{suite.title}</Text> : null} |
| 122 | + |
| 123 | + <TestsLog |
| 124 | + tests={suite.tests} |
| 125 | + indendation={newIndentation} |
| 126 | + expand={globalConfig.expand} |
| 127 | + /> |
| 128 | + {suite.suites.map((inner, i) => ( |
| 129 | + <SuiteLog |
| 130 | + key={i} |
| 131 | + globalConfig={globalConfig} |
| 132 | + indendation={newIndentation} |
| 133 | + suite={inner} |
| 134 | + /> |
| 135 | + ))} |
| 136 | + </Box> |
| 137 | + ); |
| 138 | +}; |
| 139 | + |
| 140 | +export const VerboseTestList: React.FC<{ |
| 141 | + testResult: TestResult; |
| 142 | + globalConfig: Config.GlobalConfig; |
| 143 | +}> = ({ testResult, globalConfig }) => { |
| 144 | + if (!globalConfig.verbose) { |
| 145 | + return null; |
| 146 | + } |
| 147 | + |
| 148 | + const groupedTests = VerboseReporter.groupTestsBySuites( |
| 149 | + testResult.testResults, |
| 150 | + ); |
| 151 | + |
| 152 | + return ( |
| 153 | + <SuiteLog |
| 154 | + suite={groupedTests} |
| 155 | + indendation={0} |
| 156 | + globalConfig={globalConfig} |
| 157 | + /> |
| 158 | + ); |
| 159 | +}; |
0 commit comments