From a1da5714701f3814f2866f74784a01b9c1413d24 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 24 May 2023 17:45:30 +0200 Subject: [PATCH] fix(typecheck): show tsc errors not related to test files (#3441) --- packages/vitest/src/node/workspace.ts | 10 +++++++- packages/vitest/src/typecheck/typechecker.ts | 25 +++++++++++++------ .../test/__snapshots__/runner.test.ts.snap | 17 +++++++++++++ test/typescript/test/runner.test.ts | 21 ++++++++++++++++ test/typescript/test/vitest.empty.config.ts | 10 ++++++++ test/typescript/tsconfig.empty.json | 9 +++++++ 6 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 test/typescript/test/vitest.empty.config.ts create mode 100644 test/typescript/tsconfig.empty.json diff --git a/packages/vitest/src/node/workspace.ts b/packages/vitest/src/node/workspace.ts index 0200ddb1493e..1c67ef4ea95f 100644 --- a/packages/vitest/src/node/workspace.ts +++ b/packages/vitest/src/node/workspace.ts @@ -209,11 +209,18 @@ export class WorkspaceProject { this.ctx.state.collectFiles(checker.getTestFiles()) await this.report('onTaskUpdate', checker.getTestPacks()) await this.report('onCollected') + const failedTests = hasFailed(files) + const exitCode = !failedTests && checker.getExitCode() + if (exitCode) { + const error = new Error(checker.getOutput()) + error.stack = '' + this.ctx.state.catchError(error, 'Typecheck Error') + } if (!files.length) { this.ctx.logger.printNoTestFound() } else { - if (hasFailed(files)) + if (failedTests) process.exitCode = 1 await this.report('onFinished', files) } @@ -224,6 +231,7 @@ export class WorkspaceProject { // if there are source errors, we are showing it, and then terminating process if (!files.length) { const exitCode = this.config.passWithNoTests ? (process.exitCode ?? 0) : 1 + await this.close() process.exit(exitCode) } if (this.config.watch) { diff --git a/packages/vitest/src/typecheck/typechecker.ts b/packages/vitest/src/typecheck/typechecker.ts index 2c6a1bab0b44..ce1c4863afc6 100644 --- a/packages/vitest/src/typecheck/typechecker.ts +++ b/packages/vitest/src/typecheck/typechecker.ts @@ -37,10 +37,11 @@ export class Typechecker { sourceErrors: [], } + private _output = '' private _tests: Record | null = {} private tempConfigPath?: string private allowJs?: boolean - private process!: ExecaChildProcess + private process?: ExecaChildProcess constructor(protected ctx: WorkspaceProject, protected files: string[]) { } @@ -221,6 +222,14 @@ export class Typechecker { this.allowJs = typecheck.allowJs || config.allowJs || false } + public getExitCode() { + return this.process?.exitCode != null && this.process.exitCode + } + + public getOutput() { + return this._output + } + public async start() { if (!this.tempConfigPath) throw new Error('tsconfig was not initialized') @@ -233,7 +242,7 @@ export class Typechecker { args.push('--watch') if (typecheck.allowJs) args.push('--allowJs', '--checkJs') - let output = '' + this._output = '' const child = execa(typecheck.checker, args, { cwd: root, stdout: 'pipe', @@ -243,28 +252,28 @@ export class Typechecker { await this._onParseStart?.() let rerunTriggered = false child.stdout?.on('data', (chunk) => { - output += chunk + this._output += chunk if (!watch) return - if (output.includes('File change detected') && !rerunTriggered) { + if (this._output.includes('File change detected') && !rerunTriggered) { this._onWatcherRerun?.() this._result.sourceErrors = [] this._result.files = [] this._tests = null // test structure might've changed rerunTriggered = true } - if (/Found \w+ errors*. Watching for/.test(output)) { + if (/Found \w+ errors*. Watching for/.test(this._output)) { rerunTriggered = false - this.prepareResults(output).then((result) => { + this.prepareResults(this._output).then((result) => { this._result = result this._onParseEnd?.(result) }) - output = '' + this._output = '' } }) if (!watch) { await child - this._result = await this.prepareResults(output) + this._result = await this.prepareResults(this._output) await this._onParseEnd?.(this._result) } } diff --git a/test/typescript/test/__snapshots__/runner.test.ts.snap b/test/typescript/test/__snapshots__/runner.test.ts.snap index 6c56b9333cbf..ff543a322695 100644 --- a/test/typescript/test/__snapshots__/runner.test.ts.snap +++ b/test/typescript/test/__snapshots__/runner.test.ts.snap @@ -87,6 +87,23 @@ TypeCheckError: Expected 1 arguments, but got 0. 5| })" `; +exports[`should fail > typechecks empty "include" but with tests 1`] = ` +"Testing types with tsc and vue-tsc is an experimental feature. +Breaking changes might not follow semver, please pin Vitest's version when using it. + +⎯⎯⎯⎯⎯⎯ Typecheck Error ⎯⎯⎯⎯⎯⎯⎯ +Error: error TS18003: No inputs were found in config file '/tsconfig.vitest-temp.json'. Specified 'include' paths were '[\\"src\\"]' and 'exclude' paths were '[\\"**/dist/**\\",\\"./dist\\"]'. + +" +`; + +exports[`should fail > typechecks with custom tsconfig 1`] = ` +"TypeCheckError: Expected 1 arguments, but got 0. +TypeCheckError: Expected 1 arguments, but got 0. +TypeCheckError: Expected 1 arguments, but got 0. +TypeCheckError: Expected 1 arguments, but got 0." +`; + exports[`should fail > typecheks with custom tsconfig 1`] = ` "TypeCheckError: Expected 1 arguments, but got 0. TypeCheckError: Expected 1 arguments, but got 0. diff --git a/test/typescript/test/runner.test.ts b/test/typescript/test/runner.test.ts index e5a49460c45a..bbbc539f3ca3 100644 --- a/test/typescript/test/runner.test.ts +++ b/test/typescript/test/runner.test.ts @@ -74,4 +74,25 @@ describe('should fail', async () => { expect(stderr).not.toContain('js.test-d.js') expect(stderr).not.toContain('test.test-d.ts') }, 30_000) + + it('typechecks empty "include" but with tests', async () => { + const { stderr } = await runVitestCli( + { + cwd: root, + env: { + ...process.env, + CI: 'true', + NO_COLOR: 'true', + }, + }, + 'typecheck', + '--run', + '--dir', + resolve(__dirname, '..', './failing'), + '--config', + resolve(__dirname, './vitest.empty.config.ts'), + ) + + expect(stderr.replace(resolve(__dirname, '..'), '')).toMatchSnapshot() + }, 30_000) }) diff --git a/test/typescript/test/vitest.empty.config.ts b/test/typescript/test/vitest.empty.config.ts new file mode 100644 index 000000000000..96d9349649a0 --- /dev/null +++ b/test/typescript/test/vitest.empty.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + typecheck: { + include: ['**/fail.test-d.ts'], + tsconfig: '../tsconfig.empty.json', + }, + }, +}) diff --git a/test/typescript/tsconfig.empty.json b/test/typescript/tsconfig.empty.json new file mode 100644 index 000000000000..111d96877228 --- /dev/null +++ b/test/typescript/tsconfig.empty.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "exclude": [ + "**/dist/**" + ], + "include": [ + "src" + ] +}