Skip to content

Commit cbed8f0

Browse files
authored
feat: show a squiggly line where the error was thrown (#613)
1 parent 1b8d477 commit cbed8f0

File tree

8 files changed

+60
-1
lines changed

8 files changed

+60
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ These options are resolved relative to the [workspace file](https://code.visuals
9696
- `vitest.debugOutFiles`: If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with `!` the files are excluded. If not specified, the generated code is expected in the same directory as its source. Default: `["${workspaceFolder}/**/*.(m|c|)js", "!**/node_modules/**"]`
9797
- `vitest.maximumConfigs`: The maximum amount of configs that Vitest extension can load. If exceeded, the extension will show a warning suggesting to use a workspace config file. Default: `3`
9898
- `vitest.logLevel`: How verbose should the logger be in the "Output" channel. Default: `info`
99+
- `vitest.applyDiagnostic`: Show a squiggly line where the error was thrown. This also enables the error count in the File Tab. Default: `true`
99100
- `vitest.experimentalStaticAstCollect`: uses AST parses to collect tests instead of running files and collecting them at runtime. Default: `true`
100101

101102
### Commands

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,11 @@
234234
"markdownDescription": "Additional arguments to pass to the Vitest CLI. Note that some arguments will be ignored: `watch`, `reporter`, `api`, and `ui`. Example: `--mode=staging`.",
235235
"type": "string",
236236
"scope": "resource"
237+
},
238+
"vitest.applyDiagnostic": {
239+
"description": "Show a squiggly line where the error was thrown. This also enables the error count in the File Tab.",
240+
"type": "boolean",
241+
"default": true
237242
}
238243
}
239244
}

src/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) {
6464
const cliArguments = get<string | undefined>('cliArguments')
6565

6666
const debugOutFiles = get<string[]>('debugOutFiles', [])
67+
const applyDiagnostic = get<boolean>('applyDiagnostic', true)
6768

6869
return {
6970
env: get<null | Record<string, string>>('nodeEnv', null),
@@ -73,6 +74,7 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) {
7374
terminalShellArgs,
7475
terminalShellPath,
7576
shellType,
77+
applyDiagnostic,
7678
cliArguments,
7779
nodeExecArgs,
7880
experimentalStaticAstCollect,

src/debug.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ import type { WsConnectionMetadata } from './api/ws'
1515
import { waitForWsConnection } from './api/ws'
1616
import type { ExtensionWorkerProcess } from './api/types'
1717
import { findNode } from './utils'
18+
import type { ExtensionDiagnostic } from './diagnostic'
1819

1920
export async function debugTests(
2021
controller: vscode.TestController,
2122
tree: TestTree,
2223
pkg: VitestPackage,
24+
diagnostic: ExtensionDiagnostic | undefined,
2325

2426
request: vscode.TestRunRequest,
2527
token: vscode.CancellationToken,
@@ -118,6 +120,7 @@ export async function debugTests(
118120
controller,
119121
tree,
120122
api,
123+
diagnostic,
121124
)
122125
disposables.push(api, runner)
123126

src/diagnostic.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import * as vscode from 'vscode'
2+
3+
export class ExtensionDiagnostic {
4+
private diagnostic = vscode.languages.createDiagnosticCollection('Vitest')
5+
6+
addDiagnostic(testFile: vscode.Uri, errors: vscode.TestMessage[]) {
7+
const diagnostics: vscode.Diagnostic[] = [...this.diagnostic.get(testFile) || []]
8+
errors.forEach((error) => {
9+
const range = error.location?.range
10+
if (!range) {
11+
return
12+
}
13+
const diagnostic = new vscode.Diagnostic(
14+
range,
15+
error.message.toString(),
16+
vscode.DiagnosticSeverity.Error,
17+
)
18+
diagnostics.push(diagnostic)
19+
})
20+
this.diagnostic.set(testFile, diagnostics)
21+
}
22+
23+
deleteDiagnostic(testFile: vscode.Uri) {
24+
this.diagnostic.delete(testFile)
25+
}
26+
27+
clearDiagnostic() {
28+
this.diagnostic.clear()
29+
}
30+
}

src/extension.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { TagsManager } from './tagsManager'
1616
import { coverageContext } from './coverage'
1717
import { ExtensionTerminalProcess } from './api/terminal'
1818
import { debugTests } from './debug'
19+
import { ExtensionDiagnostic } from './diagnostic'
1920

2021
export async function activate(context: vscode.ExtensionContext) {
2122
const extension = new VitestExtension()
@@ -38,6 +39,7 @@ class VitestExtension {
3839
private runners: TestRunner[] = []
3940

4041
private disposables: vscode.Disposable[] = []
42+
private diagnostic: ExtensionDiagnostic | undefined
4143

4244
/** @internal */
4345
_debugDisposable: vscode.Disposable | undefined
@@ -141,6 +143,7 @@ class VitestExtension {
141143
this.testController,
142144
this.testTree,
143145
api,
146+
this.diagnostic,
144147
)
145148
this.runners.push(runner)
146149

@@ -182,6 +185,7 @@ class VitestExtension {
182185
this.testController,
183186
this.testTree,
184187
api.package,
188+
this.diagnostic,
185189

186190
request,
187191
token,
@@ -245,6 +249,10 @@ class VitestExtension {
245249
}
246250

247251
async activate() {
252+
this.diagnostic = getConfig().applyDiagnostic
253+
? new ExtensionDiagnostic()
254+
: undefined
255+
248256
this.loadingTestItem.busy = true
249257
this.testController.items.replace([this.loadingTestItem])
250258

src/runner.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { showVitestError } from './utils'
1313
import { coverageContext, readCoverageReport } from './coverage'
1414
import { normalizeDriveLetter } from './worker/utils'
1515
import type { SerializedTestSpecification } from './api/rpc'
16+
import type { ExtensionDiagnostic } from './diagnostic'
1617

1718
export class TestRunner extends vscode.Disposable {
1819
private continuousRequests = new Set<vscode.TestRunRequest>()
@@ -32,6 +33,7 @@ export class TestRunner extends vscode.Disposable {
3233
private readonly controller: vscode.TestController,
3334
private readonly tree: TestTree,
3435
private readonly api: VitestFolderAPI,
36+
private readonly diagnostic: ExtensionDiagnostic | undefined,
3537
) {
3638
super(() => {
3739
log.verbose?.('Disposing test runner')
@@ -56,6 +58,10 @@ export class TestRunner extends vscode.Disposable {
5658
log.verbose?.('Not starting the runner because tests are being collected for', ...files.map(f => this.relative(f)))
5759
}
5860
else {
61+
files.forEach((file) => {
62+
const uri = vscode.Uri.file(file)
63+
this.diagnostic?.deleteDiagnostic(uri)
64+
})
5965
log.verbose?.('Starting a test run because', ...files.map(f => this.relative(f)), 'triggered a watch rerun event')
6066
this.startTestRun(files)
6167
}
@@ -121,6 +127,7 @@ export class TestRunner extends vscode.Disposable {
121127

122128
api.onFinished(async (files = [], unhandledError, collecting) => {
123129
const testRun = this.testRun
130+
124131
if (!testRun) {
125132
log.verbose?.('No test run to finish for', files.map(f => this.relative(f.filepath)).join(', '))
126133
if (!files.length) {
@@ -504,6 +511,9 @@ export class TestRunner extends vscode.Disposable {
504511
log.verbose?.(`Test failed, but no errors found for "${test.label}"`)
505512
return
506513
}
514+
if (test.uri) {
515+
this.diagnostic?.addDiagnostic(test.uri, errors)
516+
}
507517
log.verbose?.(`Marking "${test.label}" as failed with ${errors.length} errors`)
508518
testRun.failed(test, errors, result.duration)
509519
break

test-e2e/tester.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class TesterTree {
4141

4242
getFileItem(file: string) {
4343
const name = basename(file)
44-
return new TesterTestItem(name, this.page.locator(`[aria-label*="${name} "]`), this.page)
44+
return new TesterTestItem(name, this.page.locator(`[aria-label*="${name} ("]`), this.page)
4545
}
4646

4747
async expand(path: string) {

0 commit comments

Comments
 (0)