Skip to content

Commit 8595c0e

Browse files
scarf005sheremet-va
andauthoredDec 5, 2022
feat(ui): show diff in report panel (fix #2406) (#2423)
* feat(ui): show diff on report panel * refactor: add better type guard * refactor: move diff to utils * chore: move utils to ui package Co-authored-by: Vladimir Sheremet <sleuths.slews0s@icloud.com>
1 parent a430365 commit 8595c0e

File tree

8 files changed

+119
-4
lines changed

8 files changed

+119
-4
lines changed
 

‎packages/ui/client/components/views/ViewReport.vue

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script setup lang="ts">
2+
import { unifiedDiff } from '../../composables/diff'
23
import { openInEditor, shouldOpenInEditor } from '../../composables/error'
3-
import type { File, ParsedStack, Suite, Task } from '#types'
4+
import type { ErrorWithDiff, File, ParsedStack, Suite, Task } from '#types'
45
import { config } from '~/composables/client'
56
import { isDark } from '~/composables/dark'
67
import { createAnsiToHtmlFilter } from '~/composables/error'
@@ -94,6 +95,18 @@ function line(stack: ParsedStack) {
9495
function column(stack: ParsedStack) {
9596
return stack.sourcePos?.column ?? stack.column
9697
}
98+
99+
interface Diff { error: NonNullable<Pick<ErrorWithDiff, 'expected' | 'actual'>> }
100+
type ResultWithDiff = Task['result'] & Diff
101+
function isDiffShowable(result?: Task['result']): result is ResultWithDiff {
102+
return result && result?.error?.expected && result?.error?.actual
103+
}
104+
105+
function diff(result: ResultWithDiff): string {
106+
return unifiedDiff(result.error.expected, result.error.actual, {
107+
outputTruncateLength: 80,
108+
})
109+
}
97110
</script>
98111

99112
<template>
@@ -125,6 +138,9 @@ function column(stack: ParsedStack) {
125138
@click.passive="openInEditor(stack.file, line(stack), column(stack))"
126139
/>
127140
</div>
141+
<pre v-if="isDiffShowable(task.result)">
142+
{{ `\n${diff(task.result)}` }}
143+
</pre>
128144
</div>
129145
</div>
130146
</div>
+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import * as diff from 'diff'
2+
3+
export interface DiffOptions {
4+
outputTruncateLength?: number
5+
outputDiffLines?: number
6+
showLegend?: boolean
7+
}
8+
9+
function formatLine(line: string, maxWidth: number) {
10+
return line.slice(0, maxWidth) + (line.length > maxWidth ? '…' : '')
11+
}
12+
13+
export function unifiedDiff(actual: string, expected: string, options: DiffOptions = {}) {
14+
if (actual === expected)
15+
return ''
16+
17+
const { outputTruncateLength = 80, outputDiffLines, showLegend = true } = options
18+
19+
const indent = ' '
20+
const diffLimit = outputDiffLines || 15
21+
22+
const counts = {
23+
'+': 0,
24+
'-': 0,
25+
}
26+
let previousState: '-' | '+' | null = null
27+
let previousCount = 0
28+
function preprocess(line: string) {
29+
if (!line || line.match(/\\ No newline/))
30+
return
31+
32+
const char = line[0] as '+' | '-'
33+
if ('-+'.includes(char)) {
34+
if (previousState !== char) {
35+
previousState = char
36+
previousCount = 0
37+
}
38+
previousCount++
39+
counts[char]++
40+
if (previousCount === diffLimit)
41+
return `${char} ...`
42+
else if (previousCount > diffLimit)
43+
return
44+
}
45+
return line
46+
}
47+
48+
const msg = diff.createPatch('string', expected, actual)
49+
const lines = msg.split('\n').slice(5).map(preprocess).filter(Boolean) as string[]
50+
const isCompact = counts['+'] === 1 && counts['-'] === 1 && lines.length === 2
51+
52+
let formatted = lines.map((line: string) => {
53+
line = line.replace(/\\"/g, '"')
54+
if (line[0] === '-') {
55+
line = formatLine(line.slice(1), outputTruncateLength)
56+
if (isCompact)
57+
return line
58+
return `- ${formatLine(line, outputTruncateLength)}`
59+
}
60+
if (line[0] === '+') {
61+
line = formatLine(line.slice(1), outputTruncateLength)
62+
if (isCompact)
63+
return line
64+
return `+ ${formatLine(line, outputTruncateLength)}`
65+
}
66+
if (line.match(/@@/))
67+
return '--'
68+
return ` ${line}`
69+
})
70+
71+
if (showLegend) {
72+
// Compact mode
73+
if (isCompact) {
74+
formatted = [
75+
`- Expected ${formatted[0]}`,
76+
`+ Received ${formatted[1]}`,
77+
]
78+
}
79+
else {
80+
if (formatted[0].includes('"'))
81+
formatted[0] = formatted[0].replace('"', '')
82+
83+
const last = formatted.length - 1
84+
if (formatted[last].endsWith('"'))
85+
formatted[last] = formatted[last].slice(0, formatted[last].length - 1)
86+
87+
formatted.unshift(
88+
`- Expected - ${counts['-']}`,
89+
`+ Received + ${counts['+']}`,
90+
'',
91+
)
92+
}
93+
}
94+
95+
return formatted.map(i => indent + i).join('\n')
96+
}

‎packages/ui/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"codemirror-theme-vars": "^0.1.1",
5656
"cypress": "^11.0.1",
5757
"d3-graph-controller": "^2.3.22",
58+
"diff": "^5.1.0",
5859
"flatted": "^3.2.7",
5960
"floating-vue": "^2.0.0-y.0",
6061
"picocolors": "^1.0.0",

‎packages/vitest/src/integrations/chai/jest-expect.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { isMockFunction } from '../spy'
55
import { addSerializer } from '../snapshot/port/plugins'
66
import type { Constructable, Test } from '../../types'
77
import { assertTypes } from '../../utils'
8-
import { unifiedDiff } from '../../node/diff'
8+
import { unifiedDiff } from '../../utils/diff'
99
import type { ChaiPlugin, MatcherState } from '../../types/chai'
1010
import { arrayBufferEquality, generateToBeMessage, iterableEquality, equals as jestEquals, sparseArrayEquality, subsetEquality, typeEquality } from './jest-utils'
1111
import type { AsymmetricMatcher } from './jest-asymmetric-matchers'

‎packages/vitest/src/integrations/chai/jest-matcher-utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import c from 'picocolors'
55
import type { PrettyFormatOptions } from 'pretty-format'
66
import { format as prettyFormat, plugins as prettyFormatPlugins } from 'pretty-format'
7-
import { unifiedDiff } from '../../node/diff'
7+
import { unifiedDiff } from '../../utils/diff'
88
import type { DiffOptions, MatcherHintOptions } from '../../types/matcher-utils'
99

1010
export const EXPECTED_COLOR = c.green

‎packages/vitest/src/node/error.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import { lineSplitRE, parseStacktrace, posToNumber } from '../utils/source-map'
88
import { F_POINTER } from '../utils/figures'
99
import { stringify } from '../integrations/chai/jest-matcher-utils'
1010
import { TypeCheckError } from '../typecheck/typechecker'
11+
import { type DiffOptions, unifiedDiff } from '../utils/diff'
1112
import type { Vitest } from './core'
12-
import { type DiffOptions, unifiedDiff } from './diff'
1313
import { divider } from './reporters/renderers/utils'
1414
import type { Logger } from './logger'
1515

File renamed without changes.

‎pnpm-lock.yaml

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
Please sign in to comment.