diff --git a/packages/vitest/package.json b/packages/vitest/package.json index 4f792557f902d..5dc1cd5958db1 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -162,6 +162,7 @@ "expect-type": "^1.1.0", "magic-string": "^0.30.12", "pathe": "^1.1.2", + "restore-cursor": "^5.1.0", "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.1", diff --git a/packages/vitest/src/node/reporters/renderers/windowedRenderer.ts b/packages/vitest/src/node/reporters/renderers/windowedRenderer.ts index 017f9a7b14d18..3c7a9dcd23710 100644 --- a/packages/vitest/src/node/reporters/renderers/windowedRenderer.ts +++ b/packages/vitest/src/node/reporters/renderers/windowedRenderer.ts @@ -1,6 +1,7 @@ import type { Writable } from 'node:stream' import type { Vitest } from '../../core' import { stripVTControlCharacters } from 'node:util' +import restoreCursor from 'restore-cursor' const DEFAULT_RENDER_INTERVAL = 16 @@ -32,6 +33,7 @@ export class WindowRenderer { private windowHeight = 0 private finished = false + private cleanups: (() => void)[] = [] constructor(options: Options) { this.options = { @@ -44,8 +46,13 @@ export class WindowRenderer { error: options.logger.errorStream.write.bind(options.logger.errorStream), } - this.interceptStream(process.stdout, 'output') - this.interceptStream(process.stderr, 'error') + this.cleanups.push( + this.interceptStream(process.stdout, 'output'), + this.interceptStream(process.stderr, 'error'), + ) + + restoreCursor() + this.write(HIDE_CURSOR, 'output') this.start() } @@ -57,6 +64,7 @@ export class WindowRenderer { stop() { this.write(SHOW_CURSOR, 'output') + this.cleanups.splice(0).map(fn => fn()) clearInterval(this.renderInterval) } @@ -125,7 +133,6 @@ export class WindowRenderer { this.write(windowContent.join('\n')) this.write(SYNC_END) - this.write(HIDE_CURSOR) this.windowHeight = rowCount + Math.max(0, padding) } @@ -145,6 +152,8 @@ export class WindowRenderer { } private interceptStream(stream: NodeJS.WriteStream, type: StreamType) { + const original = stream.write + // @ts-expect-error -- not sure how 2 overloads should be typed stream.write = (chunk, _, callback) => { if (chunk) { @@ -157,6 +166,10 @@ export class WindowRenderer { } callback?.() } + + return function restore() { + stream.write = original + } } private write(message: string, type: 'output' | 'error' = 'output') { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d38d0b1d93df1..15ee5120ba505 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -880,6 +880,9 @@ importers: pathe: specifier: ^1.1.2 version: 1.1.2 + restore-cursor: + specifier: ^5.1.0 + version: 5.1.0 std-env: specifier: ^3.8.0 version: 3.8.0 @@ -7147,6 +7150,10 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -7441,6 +7448,10 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + oniguruma-to-js@0.4.3: resolution: {integrity: sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==} @@ -7993,6 +8004,10 @@ packages: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + ret@0.2.2: resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} engines: {node: '>=4'} @@ -16367,6 +16382,8 @@ snapshots: mimic-fn@4.0.0: {} + mimic-function@5.0.1: {} + mimic-response@3.1.0: {} mimic-response@4.0.0: {} @@ -16627,6 +16644,10 @@ snapshots: dependencies: mimic-fn: 4.0.0 + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + oniguruma-to-js@0.4.3: dependencies: regex: 4.3.3 @@ -17246,6 +17267,11 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + ret@0.2.2: {} reusify@1.0.4: {}