From 003c0bef48dcd13f660162578eb914fe1482c801 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 15 Jan 2025 14:43:53 +0100 Subject: [PATCH] fix(watch): don't indicate exit when no matching files (#7246) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ari Perkkiƶ --- packages/vitest/src/node/core.ts | 14 ++- packages/vitest/src/node/logger.ts | 43 +++++---- packages/vitest/src/node/project.ts | 2 +- packages/vitest/src/node/reporters/base.ts | 7 +- packages/vitest/src/node/reporters/default.ts | 18 ++-- packages/vitest/src/node/reporters/json.ts | 2 +- .../reporters/renderers/windowedRenderer.ts | 2 +- packages/vitest/src/node/reporters/summary.ts | 13 ++- packages/vitest/src/node/test-run.ts | 5 ++ .../vitest.config.correct.ts | 2 +- ...vitest.config.custom-transformIndexHtml.ts | 2 +- ...itest.config.default-transformIndexHtml.ts | 2 +- .../vitest.config.error-hook.ts | 2 +- .../vitest.config.non-existing.ts | 2 +- test/core/vite.config.ts | 1 + test/reporters/tests/json.test.ts | 87 +++++++++++++++++++ test/reporters/tests/test-run.test.ts | 10 +-- 17 files changed, 150 insertions(+), 64 deletions(-) diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index 92497c41d5f6..5b77a4412cb7 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -34,6 +34,7 @@ import { VitestPackageInstaller } from './packageInstaller' import { createPool } from './pool' import { TestProject } from './project' import { BlobReporter, readBlobs } from './reporters/blob' +import { HangingProcessReporter } from './reporters/hanging-process' import { createBenchmarkReporters, createReporters } from './reporters/utils' import { VitestSpecifications } from './specifications' import { StateManager } from './state' @@ -564,8 +565,6 @@ export class Vitest { // Report coverage for uncovered files await this.reportCoverage(coverage, true) - this.logger.printNoTestFound(filters) - if (throwAnError) { throw new FilesNotFoundError(this.mode) } @@ -671,11 +670,6 @@ export class Vitest { } private async runFiles(specs: TestSpecification[], allTestsRun: boolean): Promise { - const filepaths = specs.map(spec => spec.moduleId) - this.state.collectPaths(filepaths) - - await this.report('onPathsCollected', filepaths) - await this.report('onSpecsCollected', specs.map(spec => spec.toJSON())) await this._testRun.start(specs) // previous run @@ -1140,7 +1134,7 @@ export class Vitest { this.state.getProcessTimeoutCauses().forEach(cause => console.warn(cause)) if (!this.pool) { - const runningServers = [this.vite, ...this.resolvedProjects.map(p => p.vite)].filter(Boolean).length + const runningServers = [this._vite, ...this.resolvedProjects.map(p => p._vite)].filter(Boolean).length if (runningServers === 1) { console.warn('Tests closed successfully but something prevents Vite server from exiting') @@ -1152,7 +1146,9 @@ export class Vitest { console.warn('Tests closed successfully but something prevents the main process from exiting') } - console.warn('You can try to identify the cause by enabling "hanging-process" reporter. See https://vitest.dev/config/#reporters') + if (!this.reporters.some(r => r instanceof HangingProcessReporter)) { + console.warn('You can try to identify the cause by enabling "hanging-process" reporter. See https://vitest.dev/config/#reporters') + } } process.exit() diff --git a/packages/vitest/src/node/logger.ts b/packages/vitest/src/node/logger.ts index ba0ff9efaaf2..a00a5bad42de 100644 --- a/packages/vitest/src/node/logger.ts +++ b/packages/vitest/src/node/logger.ts @@ -128,9 +128,29 @@ export class Logger { printNoTestFound(filters?: string[]) { const config = this.ctx.config + + if (config.watch && (config.changed || config.related?.length)) { + this.log(`No affected ${config.mode} files found\n`) + } + else if (config.watch) { + this.log( + c.red(`No ${config.mode} files found. You can change the file name pattern by pressing "p"\n`), + ) + } + else { + if (config.passWithNoTests) { + this.log(`No ${config.mode} files found, exiting with code 0\n`) + } + else { + this.error( + c.red(`No ${config.mode} files found, exiting with code 1\n`), + ) + } + } + const comma = c.dim(', ') if (filters?.length) { - this.console.error(c.dim('filter: ') + c.yellow(filters.join(comma))) + this.console.error(c.dim('filter: ') + c.yellow(filters.join(comma))) } const projectsFilter = toArray(config.project) if (projectsFilter.length) { @@ -140,9 +160,9 @@ export class Logger { } this.ctx.projects.forEach((project) => { const config = project.config - const output = (project.isRootProject() || !project.name) ? '' : `[${project.name}]` - if (output) { - this.console.error(c.bgCyan(`${output} Config`)) + const printConfig = !project.isRootProject() && project.name + if (printConfig) { + this.console.error(`\n${formatProjectName(project.name)}\n`) } if (config.include) { this.console.error( @@ -165,20 +185,7 @@ export class Logger { ) } }) - - if (config.watch && (config.changed || config.related?.length)) { - this.log(`No affected ${config.mode} files found\n`) - } - else { - if (config.passWithNoTests) { - this.log(`No ${config.mode} files found, exiting with code 0\n`) - } - else { - this.error( - c.red(`\nNo ${config.mode} files found, exiting with code 1`), - ) - } - } + this.console.error() } printBanner() { diff --git a/packages/vitest/src/node/project.ts b/packages/vitest/src/node/project.ts index 081404e40cdb..ce7858fa9cc4 100644 --- a/packages/vitest/src/node/project.ts +++ b/packages/vitest/src/node/project.ts @@ -65,6 +65,7 @@ export class TestProject { /** @internal */ vitenode!: ViteNodeServer /** @internal */ typechecker?: Typechecker /** @internal */ _config?: ResolvedConfig + /** @internal */ _vite?: ViteDevServer private runner!: ViteNodeRunner @@ -75,7 +76,6 @@ export class TestProject { private _globalSetups?: GlobalSetupFile[] private _provided: ProvidedContext = {} as any - private _vite?: ViteDevServer constructor( /** @deprecated */ diff --git a/packages/vitest/src/node/reporters/base.ts b/packages/vitest/src/node/reporters/base.ts index 6ee4ef1347fe..531d2e36cd66 100644 --- a/packages/vitest/src/node/reporters/base.ts +++ b/packages/vitest/src/node/reporters/base.ts @@ -58,7 +58,12 @@ export abstract class BaseReporter implements Reporter { onFinished(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) { this.end = performance.now() - this.reportSummary(files, errors) + if (!files.length && !errors.length) { + this.ctx.logger.printNoTestFound(this.ctx.filenamePattern) + } + else { + this.reportSummary(files, errors) + } } onTaskUpdate(packs: TaskResultPack[]) { diff --git a/packages/vitest/src/node/reporters/default.ts b/packages/vitest/src/node/reporters/default.ts index 8dcc60af80f3..9e929152e9b7 100644 --- a/packages/vitest/src/node/reporters/default.ts +++ b/packages/vitest/src/node/reporters/default.ts @@ -1,5 +1,5 @@ -import type { File } from '@vitest/runner' import type { Vitest } from '../core' +import type { TestSpecification } from '../spec' import type { BaseOptions } from './base' import type { ReportedHookContext, TestCase, TestModule } from './reported-tasks' import { BaseReporter } from './base' @@ -29,6 +29,10 @@ export class DefaultReporter extends BaseReporter { } } + onTestRunStart(specifications: ReadonlyArray) { + this.summary?.onTestRunStart(specifications) + } + onTestModuleQueued(file: TestModule) { this.summary?.onTestModuleQueued(file) } @@ -72,17 +76,9 @@ export class DefaultReporter extends BaseReporter { this.renderSucceed = paths.length <= 1 } } - - this.summary?.onPathsCollected(paths) - } - - onWatcherRerun(files: string[], trigger?: string) { - this.summary?.onWatcherRerun() - super.onWatcherRerun(files, trigger) } - onFinished(files?: File[], errors?: unknown[]) { - this.summary?.onFinished() - super.onFinished(files, errors) + onTestRunEnd() { + this.summary?.onTestRunEnd() } } diff --git a/packages/vitest/src/node/reporters/json.ts b/packages/vitest/src/node/reporters/json.ts index f77f354a82f2..02f38fc19cab 100644 --- a/packages/vitest/src/node/reporters/json.ts +++ b/packages/vitest/src/node/reporters/json.ts @@ -110,7 +110,7 @@ export class JsonReporter implements Reporter { const numTodoTests = tests.filter(t => t.mode === 'todo').length const testResults: Array = [] - const success = numFailedTestSuites === 0 && numFailedTests === 0 + const success = !!(files.length > 0 || this.ctx.config.passWithNoTests) && numFailedTestSuites === 0 && numFailedTests === 0 for (const file of files) { const tests = getTests([file]) diff --git a/packages/vitest/src/node/reporters/renderers/windowedRenderer.ts b/packages/vitest/src/node/reporters/renderers/windowedRenderer.ts index 742d4a53279b..72d042d23afc 100644 --- a/packages/vitest/src/node/reporters/renderers/windowedRenderer.ts +++ b/packages/vitest/src/node/reporters/renderers/windowedRenderer.ts @@ -59,7 +59,7 @@ export class WindowRenderer { start() { this.finished = false - this.renderInterval = setInterval(() => this.flushBuffer(), this.options.interval) + this.renderInterval = setInterval(() => this.flushBuffer(), this.options.interval).unref() } stop() { diff --git a/packages/vitest/src/node/reporters/summary.ts b/packages/vitest/src/node/reporters/summary.ts index d3a423caf4f2..38c002f43ec4 100644 --- a/packages/vitest/src/node/reporters/summary.ts +++ b/packages/vitest/src/node/reporters/summary.ts @@ -1,4 +1,5 @@ import type { Vitest } from '../core' +import type { TestSpecification } from '../spec' import type { Reporter } from '../types/reporter' import type { ReportedHookContext, TestCase, TestModule } from './reported-tasks' import c from 'tinyrainbow' @@ -75,19 +76,13 @@ export class SummaryReporter implements Reporter { getWindow: () => this.createSummary(), }) - this.startTimers() - this.ctx.onClose(() => { clearInterval(this.durationInterval) this.renderer.stop() }) } - onPathsCollected(paths?: string[]) { - this.modules.total = (paths || []).length - } - - onWatcherRerun() { + onTestRunStart(specifications: ReadonlyArray) { this.runningModules.clear() this.finishedModules.clear() this.modules = emptyCounters() @@ -95,9 +90,11 @@ export class SummaryReporter implements Reporter { this.startTimers() this.renderer.start() + + this.modules.total = specifications.length } - onFinished() { + onTestRunEnd() { this.runningModules.clear() this.finishedModules.clear() this.renderer.finish() diff --git a/packages/vitest/src/node/test-run.ts b/packages/vitest/src/node/test-run.ts index 13ac1ae15c7d..6a05e1a80338 100644 --- a/packages/vitest/src/node/test-run.ts +++ b/packages/vitest/src/node/test-run.ts @@ -12,6 +12,11 @@ export class TestRun { constructor(private vitest: Vitest) {} async start(specifications: TestSpecification[]) { + const filepaths = specifications.map(spec => spec.moduleId) + this.vitest.state.collectPaths(filepaths) + + await this.vitest.report('onPathsCollected', Array.from(new Set(filepaths))) + await this.vitest.report('onSpecsCollected', specifications.map(spec => spec.toJSON())) await this.vitest.report('onTestRunStart', [...specifications]) } diff --git a/test/config/fixtures/browser-custom-html/vitest.config.correct.ts b/test/config/fixtures/browser-custom-html/vitest.config.correct.ts index cd2dd2daf2a8..bef458c6a486 100644 --- a/test/config/fixtures/browser-custom-html/vitest.config.correct.ts +++ b/test/config/fixtures/browser-custom-html/vitest.config.correct.ts @@ -4,7 +4,7 @@ export default defineConfig({ test: { include: ['browser-basic.test.ts'], browser: { - name: 'chromium', + instances: [{ browser: 'chromium' }], enabled: true, headless: true, provider: 'playwright', diff --git a/test/config/fixtures/browser-custom-html/vitest.config.custom-transformIndexHtml.ts b/test/config/fixtures/browser-custom-html/vitest.config.custom-transformIndexHtml.ts index 276d5b3526d9..ccb683258034 100644 --- a/test/config/fixtures/browser-custom-html/vitest.config.custom-transformIndexHtml.ts +++ b/test/config/fixtures/browser-custom-html/vitest.config.custom-transformIndexHtml.ts @@ -30,7 +30,7 @@ export default defineConfig({ test: { include: ['./browser-custom.test.ts'], browser: { - name: 'chromium', + instances: [{ browser: 'chromium' }], enabled: true, headless: true, provider: 'playwright', diff --git a/test/config/fixtures/browser-custom-html/vitest.config.default-transformIndexHtml.ts b/test/config/fixtures/browser-custom-html/vitest.config.default-transformIndexHtml.ts index 07597992f7e4..d47640dffc75 100644 --- a/test/config/fixtures/browser-custom-html/vitest.config.default-transformIndexHtml.ts +++ b/test/config/fixtures/browser-custom-html/vitest.config.default-transformIndexHtml.ts @@ -30,7 +30,7 @@ export default defineConfig({ test: { include: ['./browser-custom.test.ts'], browser: { - name: 'chromium', + instances: [{ browser: 'chromium' }], enabled: true, headless: true, provider: 'playwright', diff --git a/test/config/fixtures/browser-custom-html/vitest.config.error-hook.ts b/test/config/fixtures/browser-custom-html/vitest.config.error-hook.ts index d89eac4068d2..9fbd6a9786b2 100644 --- a/test/config/fixtures/browser-custom-html/vitest.config.error-hook.ts +++ b/test/config/fixtures/browser-custom-html/vitest.config.error-hook.ts @@ -12,7 +12,7 @@ export default defineConfig({ test: { include: ['./browser-basic.test.ts'], browser: { - name: 'chromium', + instances: [{ browser: 'chromium' }], enabled: true, headless: true, provider: 'playwright', diff --git a/test/config/fixtures/browser-custom-html/vitest.config.non-existing.ts b/test/config/fixtures/browser-custom-html/vitest.config.non-existing.ts index 04f863e3a7cb..cdedec59b440 100644 --- a/test/config/fixtures/browser-custom-html/vitest.config.non-existing.ts +++ b/test/config/fixtures/browser-custom-html/vitest.config.non-existing.ts @@ -3,7 +3,7 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { browser: { - name: 'chromium', + instances: [{ browser: 'chromium' }], enabled: true, headless: true, provider: 'playwright', diff --git a/test/core/vite.config.ts b/test/core/vite.config.ts index cd0bf996e990..27dc7e604fc9 100644 --- a/test/core/vite.config.ts +++ b/test/core/vite.config.ts @@ -63,6 +63,7 @@ export default defineConfig({ setupFiles: [ './test/setup.ts', ], + reporters: [['default', { summary: true }], 'hanging-process'], testNamePattern: '^((?!does not include test that).)*$', coverage: { provider: 'istanbul', diff --git a/test/reporters/tests/json.test.ts b/test/reporters/tests/json.test.ts index 70bd4f104b92..509800118967 100644 --- a/test/reporters/tests/json.test.ts +++ b/test/reporters/tests/json.test.ts @@ -38,6 +38,93 @@ describe('json reporter', async () => { expect(result).toMatchSnapshot() }, 40000) + it('generates empty json with success: false', async () => { + const { stdout } = await runVitest({ + reporters: 'json', + root, + includeTaskLocation: true, + }, ['json-non-existing-files']) + + const json = JSON.parse(stdout) + json.startTime = 0 + expect(json).toMatchInlineSnapshot(` + { + "numFailedTestSuites": 0, + "numFailedTests": 0, + "numPassedTestSuites": 0, + "numPassedTests": 0, + "numPendingTestSuites": 0, + "numPendingTests": 0, + "numTodoTests": 0, + "numTotalTestSuites": 0, + "numTotalTests": 0, + "snapshot": { + "added": 0, + "didUpdate": false, + "failure": false, + "filesAdded": 0, + "filesRemoved": 0, + "filesRemovedList": [], + "filesUnmatched": 0, + "filesUpdated": 0, + "matched": 0, + "total": 0, + "unchecked": 0, + "uncheckedKeysByFile": [], + "unmatched": 0, + "updated": 0, + }, + "startTime": 0, + "success": false, + "testResults": [], + } + `) + }) + + it('generates empty json with success: true', async () => { + const { stdout } = await runVitest({ + reporters: 'json', + root, + includeTaskLocation: true, + passWithNoTests: true, + }, ['json-non-existing-files']) + + const json = JSON.parse(stdout) + json.startTime = 0 + expect(json).toMatchInlineSnapshot(` + { + "numFailedTestSuites": 0, + "numFailedTests": 0, + "numPassedTestSuites": 0, + "numPassedTests": 0, + "numPendingTestSuites": 0, + "numPendingTests": 0, + "numTodoTests": 0, + "numTotalTestSuites": 0, + "numTotalTests": 0, + "snapshot": { + "added": 0, + "didUpdate": false, + "failure": false, + "filesAdded": 0, + "filesRemoved": 0, + "filesRemovedList": [], + "filesUnmatched": 0, + "filesUpdated": 0, + "matched": 0, + "total": 0, + "unchecked": 0, + "uncheckedKeysByFile": [], + "unmatched": 0, + "updated": 0, + }, + "startTime": 0, + "success": true, + "testResults": [], + } + `) + }) + it.each([ ['passed', 'all-passing-or-skipped'], ['passed', 'all-skipped'], diff --git a/test/reporters/tests/test-run.test.ts b/test/reporters/tests/test-run.test.ts index 192f20307c98..a2304a732cbe 100644 --- a/test/reporters/tests/test-run.test.ts +++ b/test/reporters/tests/test-run.test.ts @@ -999,15 +999,7 @@ async function run( const { stdout, stderr } = await runInlineTests(structure, config) - if (reporterOptions?.printTestRunEvents && reporterOptions?.failed) { - if (config.passWithNoTests) { - expect(stdout).toContain('No test files found, exiting with code 0') - } - else { - expect(stderr).toContain('No test files found, exiting with code 1') - } - } - else if (!reporterOptions?.printTestRunEvents) { + if (!reporterOptions?.printTestRunEvents && !reporterOptions?.failed) { expect(stdout).toBe('') expect(stderr).toBe('') }