Skip to content

Commit

Permalink
feat: support inline diff options and support printBasicPrototype (
Browse files Browse the repository at this point in the history
…#6740)

Co-authored-by: Vladimir <sleuths.slews0s@icloud.com>
Co-authored-by: Michał Grzegorzewski <4864089+spamshaker@users.noreply.github.com>
3 people authored Nov 13, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 32f23b9 commit 391860f
Showing 16 changed files with 295 additions and 20 deletions.
58 changes: 48 additions & 10 deletions docs/config/index.md
Original file line number Diff line number Diff line change
@@ -2284,22 +2284,32 @@ export default defineConfig({
### diff

- **Type:** `string`
- **CLI:** `--diff=<value>`
- **CLI:** `--diff=<path>`

Path to a diff config that will be used to generate diff interface. Useful if you want to customize diff display.
`DiffOptions` object or a path to a module which exports `DiffOptions`. Useful if you want to customize diff display.

For example, as a config object:

:::code-group
```ts [vitest.diff.ts]
import type { DiffOptions } from 'vitest'
import c from 'tinyrainbow'
```ts [vitest.config.js]
import { defineConfig } from 'vitest/config'
import c from 'picocolors'

export default {
aIndicator: c.bold('--'),
bIndicator: c.bold('++'),
omitAnnotationLines: true,
} satisfies DiffOptions
export default defineConfig({
test: {
diff: {
aIndicator: c.bold('--'),
bIndicator: c.bold('++'),
omitAnnotationLines: true,
}
}
})
```
:::

Or as a module:

:::code-group
```ts [vitest.config.js]
import { defineConfig } from 'vitest/config'

@@ -2309,12 +2319,32 @@ export default defineConfig({
}
})
```

```ts [vitest.diff.ts]
import type { DiffOptions } from 'vitest'
import c from 'picocolors'

export default {
aIndicator: c.bold('--'),
bIndicator: c.bold('++'),
omitAnnotationLines: true,
} satisfies DiffOptions
```
:::

#### diff.expand

- **Type**: `boolean`
- **Default**: `true`
- **CLI:** `--diff.expand=false`

Expand all common lines.

#### diff.truncateThreshold

- **Type**: `number`
- **Default**: `0`
- **CLI:** `--diff.truncateThreshold=<path>`

The maximum length of diff result to be displayed. Diffs above this threshold will be truncated.
Truncation won't take effect with default value 0.
@@ -2323,6 +2353,7 @@ Truncation won't take effect with default value 0.

- **Type**: `string`
- **Default**: `'... Diff result is truncated'`
- **CLI:** `--diff.truncateAnnotation=<annotation>`

Annotation that is output at the end of diff result if it's truncated.

@@ -2333,6 +2364,13 @@ Annotation that is output at the end of diff result if it's truncated.

Color of truncate annotation, default is output with no color.

#### diff.printBasicPrototype

- **Type**: `boolean`
- **Default**: `true`

Print basic prototype `Object` and `Array` in diff output

### fakeTimers

- **Type:** `FakeTimerInstallOpts`
2 changes: 1 addition & 1 deletion packages/browser/src/node/plugin.ts
Original file line number Diff line number Diff line change
@@ -230,7 +230,7 @@ export default (browserServer: BrowserServer, base = '/'): Plugin[] => {
'msw/browser',
]

if (project.config.diff) {
if (typeof project.config.diff === 'string') {
entries.push(project.config.diff)
}

5 changes: 3 additions & 2 deletions packages/utils/src/diff/index.ts
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ import { getType } from './getType'
import { normalizeDiffOptions } from './normalizeDiffOptions'
import { diffStringsRaw, diffStringsUnified } from './printDiffs'

export type { DiffOptions, DiffOptionsColor } from './types'
export type { DiffOptions, DiffOptionsColor, SerializedDiffOptions } from './types'

export { diffLinesRaw, diffLinesUnified, diffLinesUnified2 }
export { diffStringsRaw, diffStringsUnified }
@@ -180,11 +180,12 @@ function getFormatOptions(
formatOptions: PrettyFormatOptions,
options?: DiffOptions,
): PrettyFormatOptions {
const { compareKeys } = normalizeDiffOptions(options)
const { compareKeys, printBasicPrototype } = normalizeDiffOptions(options)

return {
...formatOptions,
compareKeys,
printBasicPrototype,
}
}

1 change: 1 addition & 0 deletions packages/utils/src/diff/normalizeDiffOptions.ts
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@ function getDefaultOptions(): DiffOptionsNormalized {
includeChangeCounts: false,
omitAnnotationLines: false,
patchColor: c.yellow,
printBasicPrototype: true,
truncateThreshold: DIFF_TRUNCATE_THRESHOLD_DEFAULT,
truncateAnnotation: '... Diff result is truncated',
truncateAnnotationColor: noColor,
18 changes: 18 additions & 0 deletions packages/utils/src/diff/types.ts
Original file line number Diff line number Diff line change
@@ -26,12 +26,29 @@ export interface DiffOptions {
includeChangeCounts?: boolean
omitAnnotationLines?: boolean
patchColor?: DiffOptionsColor
printBasicPrototype?: boolean
compareKeys?: CompareKeys
truncateThreshold?: number
truncateAnnotation?: string
truncateAnnotationColor?: DiffOptionsColor
}

export interface SerializedDiffOptions {
aAnnotation?: string
aIndicator?: string
bAnnotation?: string
bIndicator?: string
commonIndicator?: string
contextLines?: number
emptyFirstOrLastLinePlaceholder?: string
expand?: boolean
includeChangeCounts?: boolean
omitAnnotationLines?: boolean
printBasicPrototype?: boolean
truncateThreshold?: number
truncateAnnotation?: string
}

export interface DiffOptionsNormalized {
aAnnotation: string
aColor: DiffOptionsColor
@@ -51,6 +68,7 @@ export interface DiffOptionsNormalized {
includeChangeCounts: boolean
omitAnnotationLines: boolean
patchColor: DiffOptionsColor
printBasicPrototype: boolean
truncateThreshold: number
truncateAnnotation: string
truncateAnnotationColor: DiffOptionsColor
53 changes: 51 additions & 2 deletions packages/vitest/src/node/cli/cli-config.ts
Original file line number Diff line number Diff line change
@@ -599,9 +599,58 @@ export const cliOptionsConfig: VitestCLIOptions = {
},
diff: {
description:
'Path to a diff config that will be used to generate diff interface',
'DiffOptions object or a path to a module which exports DiffOptions object',
argument: '<path>',
normalize: true,
subcommands: {
aAnnotation: {
description: 'Annotation for expected lines (default: `Expected`)',
argument: '<annotation>',
},
aIndicator: {
description: 'Indicator for expected lines (default: `-`)',
argument: '<indicator>',
},
bAnnotation: {
description: 'Annotation for received lines (default: `Received`)',
argument: '<annotation>',
},
bIndicator: {
description: 'Indicator for received lines (default: `+`)',
argument: '<indicator>',
},
commonIndicator: {
description: 'Indicator for common lines (default: ` `)',
argument: '<indicator>',
},
contextLines: {
description: 'Number of lines of context to show around each change (default: `5`)',
argument: '<lines>',
},
emptyFirstOrLastLinePlaceholder: {
description: 'Placeholder for an empty first or last line (default: `""`)',
argument: '<placeholder>',
},
expand: {
description: 'Expand all common lines (default: `true`)',
},
includeChangeCounts: {
description: 'Include comparison counts in diff output (default: `false`)',
},
omitAnnotationLines: {
description: 'Omit annotation lines from the output (default: `false`)',
},
printBasicPrototype: {
description: 'Print basic prototype Object and Array (default: `true`)',
},
truncateThreshold: {
description: 'Number of lines to show before and after each change (default: `0`)',
argument: '<threshold>',
},
truncateAnnotation: {
description: 'Annotation for truncated lines (default: `... Diff result is truncated`)',
argument: '<annotation>',
},
},
},
exclude: {
description: 'Additional file globs to be excluded from test',
2 changes: 1 addition & 1 deletion packages/vitest/src/node/config/resolveConfig.ts
Original file line number Diff line number Diff line change
@@ -578,7 +578,7 @@ export function resolveConfig(
}
}

if (resolved.diff) {
if (typeof resolved.diff === 'string') {
resolved.diff = resolvePath(resolved.diff, resolved.root)
resolved.forceRerunTriggers.push(resolved.diff)
}
1 change: 1 addition & 0 deletions packages/vitest/src/node/config/serializeConfig.ts
Original file line number Diff line number Diff line change
@@ -37,6 +37,7 @@ export function serializeConfig(
pool: config.pool,
expect: config.expect,
snapshotSerializers: config.snapshotSerializers,
// TODO: non serializable function?
diff: config.diff,
retry: config.retry,
disableConsoleIntercept: config.disableConsoleIntercept,
5 changes: 3 additions & 2 deletions packages/vitest/src/node/types/config.ts
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import type { FakeTimerInstallOpts } from '@sinonjs/fake-timers'
import type { PrettyFormatOptions } from '@vitest/pretty-format'
import type { SequenceHooks, SequenceSetupFiles } from '@vitest/runner'
import type { SnapshotStateOptions } from '@vitest/snapshot'
import type { SerializedDiffOptions } from '@vitest/utils/diff'
import type { AliasOptions, ConfigEnv, DepOptimizationConfig, ServerOptions, UserConfig as ViteUserConfig } from 'vite'
import type { ViteNodeServerOptions } from 'vite-node'
import type { ChaiConfig } from '../../integrations/chai/config'
@@ -563,7 +564,7 @@ export interface InlineConfig {
/**
* Path to a module which has a default export of diff config.
*/
diff?: string
diff?: string | SerializedDiffOptions

/**
* Paths to snapshot serializer modules.
@@ -979,7 +980,7 @@ export interface ResolvedConfig
mode: VitestRunMode

base?: string
diff?: string
diff?: string | SerializedDiffOptions
bail?: number

setupFiles: string[]
3 changes: 2 additions & 1 deletion packages/vitest/src/runtime/config.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ import type { PrettyFormatOptions } from '@vitest/pretty-format'
import type { SequenceHooks, SequenceSetupFiles } from '@vitest/runner'
import type { SnapshotUpdateState } from '@vitest/snapshot'
import type { SnapshotEnvironment } from '@vitest/snapshot/environment'
import type { SerializedDiffOptions } from '@vitest/utils/diff'

/**
* Config that tests have access to.
@@ -98,7 +99,7 @@ export interface SerializedConfig {
showDiff?: boolean
truncateThreshold?: number
} | undefined
diff: string | undefined
diff: string | SerializedDiffOptions | undefined
retry: number
includeTaskLocation: boolean | undefined
inspect: boolean | string | undefined
3 changes: 3 additions & 0 deletions packages/vitest/src/runtime/setup-common.ts
Original file line number Diff line number Diff line change
@@ -47,6 +47,9 @@ export async function loadDiffConfig(
config: SerializedConfig,
executor: VitestExecutor,
) {
if (typeof config.diff === 'object') {
return config.diff
}
if (typeof config.diff !== 'string') {
return
}
20 changes: 20 additions & 0 deletions test/config/fixtures/diff/basic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { expect, test } from 'vitest'

test('large diff', () => {
const x = [...Array(30)].map((_, i) => i);
const y = [...x];
y[0] = 1000;
y[15] = 2000;
y[29] = 3000;
expect(x).toEqual(y)
})

test("printBasicPrototype", () => {
expect({
obj: { k: "foo" },
arr: [1, 2]
}).toEqual({
obj: { k: "bar" },
arr: [1, 3]
});
})
10 changes: 10 additions & 0 deletions test/config/fixtures/diff/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {defineConfig} from 'vitest/config'

export default defineConfig({
test: {
diff: {
// expand: false,
// printBasicPrototype: false,
}
}
})
111 changes: 111 additions & 0 deletions test/config/test/__snapshots__/diff.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`inline diff options: { expand: false, printBasicPrototype: false } 1`] = `
[
"- Expected
+ Received
@@ -1,7 +1,7 @@
[
- 1000,
+ 0,
1,
2,
3,
4,
5,
@@ -12,11 +12,11 @@
10,
11,
12,
13,
14,
- 2000,
+ 15,
16,
17,
18,
19,
20,
@@ -26,7 +26,7 @@
24,
25,
26,
27,
28,
- 3000,
+ 29,
]",
"- Expected
+ Received
{
"arr": [
1,
- 3,
+ 2,
],
"obj": {
- "k": "bar",
+ "k": "foo",
},
}",
]
`;

exports[`inline diff options: undefined 1`] = `
[
"- Expected
+ Received
Array [
- 1000,
+ 0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
- 2000,
+ 15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
- 3000,
+ 29,
]",
"- Expected
+ Received
Object {
"arr": Array [
1,
- 3,
+ 2,
],
"obj": Object {
- "k": "bar",
+ "k": "foo",
},
}",
]
`;
19 changes: 19 additions & 0 deletions test/config/test/diff.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { stripVTControlCharacters } from 'node:util'
import { expect, test } from 'vitest'
import { runVitest } from '../../test-utils'

test.for([
[undefined],
[{ expand: false, printBasicPrototype: false }],
])(`inline diff options: %o`, async ([options]) => {
const { ctx } = await runVitest({
root: './fixtures/diff',
diff: options,
})
const errors = ctx!.state.getFiles().flatMap(f =>
f.tasks.flatMap(t => t.result?.errors ?? []),
)
expect(
errors.map(e => e.diff && stripVTControlCharacters(e.diff)),
).matchSnapshot()
})
4 changes: 3 additions & 1 deletion test/config/test/failures.test.ts
Original file line number Diff line number Diff line change
@@ -247,7 +247,9 @@ test('coverage.autoUpdate cannot update thresholds when configuration file doesn
})

test('boolean flag 100 should not crash CLI', async () => {
const { stderr } = await runVitestCli('--coverage.enabled', '--coverage.thresholds.100')
let { stderr } = await runVitestCli('--coverage.enabled', '--coverage.thresholds.100')
// non-zero coverage shows up, which is non-deterministic, so strip it.
stderr = stderr.replace(/\([0-9.]+%\) does/g, '(0%) does')

expect(stderr).toMatch('ERROR: Coverage for lines (0%) does not meet global threshold (100%)')
expect(stderr).toMatch('ERROR: Coverage for functions (0%) does not meet global threshold (100%)')

0 comments on commit 391860f

Please sign in to comment.