diff --git a/docs/config/index.md b/docs/config/index.md index c23bc8e47a06..581498e0eb6c 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -364,6 +364,7 @@ Custom reporters for output. Reporters can be [a Reporter instance](https://gith - `'junit'` - JUnit XML reporter (you can configure `testsuites` tag name with `VITEST_JUNIT_SUITE_NAME` environmental variable) - `'json'` - give a simple JSON summary - `'html'` - outputs HTML report based on [`@vitest/ui`](/guide/ui) + - `'hanging-process'` - displays a list of hanging processes, if Vitest cannot exit process safely. This might be a heavy operation, enable it only if Vitest consistently cannot exit process - path of a custom reporter (e.g. `'./path/to/reporter.ts'`, `'@scope/reporter'`) ### outputTruncateLength diff --git a/packages/vitest/package.json b/packages/vitest/package.json index b906f7c67faf..47856a593163 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -122,7 +122,8 @@ "tinypool": "^0.3.0", "tinyspy": "^1.0.2", "vite": "^3.0.0 || ^4.0.0", - "vite-node": "workspace:*" + "vite-node": "workspace:*", + "why-is-node-running": "^2.2.2" }, "devDependencies": { "@antfu/install-pkg": "^0.1.1", diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index b5392e6ab78f..eeff77a45e11 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -553,8 +553,10 @@ export class Vitest { */ async exit(force = false) { setTimeout(() => { - console.warn(`close timed out after ${this.config.teardownTimeout}ms`) - process.exit() + this.report('onProcessTimeout').then(() => { + console.warn(`close timed out after ${this.config.teardownTimeout}ms`) + process.exit() + }) }, this.config.teardownTimeout).unref() await this.close() diff --git a/packages/vitest/src/node/reporters/hanging-process.ts b/packages/vitest/src/node/reporters/hanging-process.ts new file mode 100644 index 000000000000..41c7b4938429 --- /dev/null +++ b/packages/vitest/src/node/reporters/hanging-process.ts @@ -0,0 +1,15 @@ +import { createRequire } from 'module' +import type { Reporter } from '../../types' + +export class HangingProcessReporter implements Reporter { + whyRunning: (() => void) | undefined + + onInit(): void { + const _require = createRequire(import.meta.url) + this.whyRunning = _require('why-is-node-running') + } + + onProcessTimeout() { + this.whyRunning?.() + } +} diff --git a/packages/vitest/src/node/reporters/index.ts b/packages/vitest/src/node/reporters/index.ts index 664f2afcad58..91e4020aba84 100644 --- a/packages/vitest/src/node/reporters/index.ts +++ b/packages/vitest/src/node/reporters/index.ts @@ -5,6 +5,7 @@ import { VerboseReporter } from './verbose' import { TapReporter } from './tap' import { JUnitReporter } from './junit' import { TapFlatReporter } from './tap-flat' +import { HangingProcessReporter } from './hanging-process' export { DefaultReporter } @@ -16,6 +17,7 @@ export const ReportersMap = { 'tap': TapReporter, 'tap-flat': TapFlatReporter, 'junit': JUnitReporter, + 'hanging-process': HangingProcessReporter, } export type BuiltinReporters = keyof typeof ReportersMap diff --git a/packages/vitest/src/types/reporter.ts b/packages/vitest/src/types/reporter.ts index 9eb6b2b1292a..07e65804c450 100644 --- a/packages/vitest/src/types/reporter.ts +++ b/packages/vitest/src/types/reporter.ts @@ -16,6 +16,8 @@ export interface Reporter { onServerRestart?: (reason?: string) => Awaitable onUserConsoleLog?: (log: UserConsoleLog) => Awaitable + + onProcessTimeout?: () => Awaitable } export type { Vitest } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5302ae48c39..f2067b7174dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -875,6 +875,7 @@ importers: typescript: ^4.9.4 vite: ^4.0.0 vite-node: workspace:* + why-is-node-running: ^2.2.2 ws: ^8.12.0 dependencies: '@types/chai': 4.3.4 @@ -894,6 +895,7 @@ importers: tinyspy: 1.0.2 vite: 4.0.0_@types+node@18.7.13 vite-node: link:../vite-node + why-is-node-running: 2.2.2 devDependencies: '@antfu/install-pkg': 0.1.1 '@edge-runtime/vm': 2.0.2 @@ -18766,6 +18768,10 @@ packages: object-inspect: 1.12.2 dev: true + /siginfo/2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: false + /sigmund/1.0.1: resolution: {integrity: sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==} dev: true @@ -19093,6 +19099,10 @@ packages: escape-string-regexp: 2.0.0 dev: true + /stackback/0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: false + /stackframe/1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} dev: true @@ -21421,6 +21431,15 @@ packages: dependencies: isexe: 2.0.0 + /why-is-node-running/2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: false + /wide-align/1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} dependencies: