From 6f97b55ddc446e725d259b7790eb21e39be77d6c Mon Sep 17 00:00:00 2001 From: Vladimir Date: Thu, 26 Mar 2026 10:41:26 +0100 Subject: [PATCH] feat: disable colors if agent is detected (#9851) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ari Perkkiƶ --- packages/vitest/src/node/cli/cac.ts | 7 +++- packages/vitest/src/node/cli/cli-config.ts | 3 +- .../vitest/src/node/config/serializeConfig.ts | 2 ++ packages/vitest/src/runtime/config.ts | 1 + packages/vitest/src/runtime/workers/init.ts | 5 +++ pnpm-lock.yaml | 32 +++++++++---------- pnpm-workspace.yaml | 2 +- test/config/test/console-color.test.ts | 16 +++++++++- 8 files changed, 47 insertions(+), 21 deletions(-) diff --git a/packages/vitest/src/node/cli/cac.ts b/packages/vitest/src/node/cli/cac.ts index a16e044d2..9c9fdb2c3 100644 --- a/packages/vitest/src/node/cli/cac.ts +++ b/packages/vitest/src/node/cli/cac.ts @@ -5,8 +5,9 @@ import type { CLIOption, CLIOptions as CLIOptionsConfig } from './cli-config' import { toArray } from '@vitest/utils/helpers' import cac from 'cac' import { normalize } from 'pathe' -import c from 'tinyrainbow' +import c, { disableDefaultColors } from 'tinyrainbow' import { version } from '../../../package.json' with { type: 'json' } +import { isAgent } from '../../utils/env' import { benchCliOptionsConfig, cliOptionsConfig, collectCliOptionsConfig } from './cli-config' import { setupTabCompletions } from './completions' @@ -74,6 +75,10 @@ function addCliOptions(cli: CAC | Command, options: CLIOptionsConfig) { } export function createCLI(options: CliParseOptions = {}): CAC { + if (isAgent) { + disableDefaultColors() + } + const cli = cac('vitest') cli.version(version) diff --git a/packages/vitest/src/node/cli/cli-config.ts b/packages/vitest/src/node/cli/cli-config.ts index f1cb5cc3e..852372f76 100644 --- a/packages/vitest/src/node/cli/cli-config.ts +++ b/packages/vitest/src/node/cli/cli-config.ts @@ -1,7 +1,6 @@ import type { ApiConfig } from '../types/config' import type { CliOptions } from './cli-api' import { defaultBrowserPort, defaultPort } from '../../constants' -import { ReportersMap } from '../reporters' type NestedOption>> = V extends | never @@ -141,7 +140,7 @@ export const cliOptionsConfig: VitestCLIOptions = { }, reporters: { alias: 'reporter', - description: `Specify reporters (${Object.keys(ReportersMap).join(', ')})`, + description: `Specify reporters (default, agent, blob, verbose, dot, json, tap, tap-flat, junit, tree, hanging-process, github-actions)`, argument: '', subcommands: null, // don't support custom objects array: true, diff --git a/packages/vitest/src/node/config/serializeConfig.ts b/packages/vitest/src/node/config/serializeConfig.ts index 7be23a3cb..ca0115c32 100644 --- a/packages/vitest/src/node/config/serializeConfig.ts +++ b/packages/vitest/src/node/config/serializeConfig.ts @@ -1,6 +1,7 @@ import type { TestProject } from '../project' import type { ApiConfig, SerializedConfig } from '../types/config' import { configDefaults } from '../../defaults' +import { isAgent } from '../../utils/env' export function serializeConfig(project: TestProject): SerializedConfig { const { config, globalConfig } = project @@ -146,5 +147,6 @@ export function serializeConfig(project: TestProject): SerializedConfig { config.slowTestThreshold ?? globalConfig.slowTestThreshold ?? configDefaults.slowTestThreshold, + isAgent, } } diff --git a/packages/vitest/src/runtime/config.ts b/packages/vitest/src/runtime/config.ts index a50b5c0aa..f2e2cbbf8 100644 --- a/packages/vitest/src/runtime/config.ts +++ b/packages/vitest/src/runtime/config.ts @@ -146,6 +146,7 @@ export interface SerializedConfig { tagsFilter: string[] | undefined strictTags: boolean slowTestThreshold: number | undefined + isAgent: boolean } export interface SerializedCoverageConfig { diff --git a/packages/vitest/src/runtime/workers/init.ts b/packages/vitest/src/runtime/workers/init.ts index b225fda7f..431d8da79 100644 --- a/packages/vitest/src/runtime/workers/init.ts +++ b/packages/vitest/src/runtime/workers/init.ts @@ -3,6 +3,7 @@ import type { WorkerRequest, WorkerResponse } from '../../node/pools/types' import type { WorkerSetupContext } from '../../types/worker' import type { VitestWorker } from './types' import { serializeError } from '@vitest/utils/error' +import { disableDefaultColors } from 'tinyrainbow' import { Traces } from '../../utils/traces' import * as listeners from '../listeners' import { createRuntimeRpc } from '../rpc' @@ -49,6 +50,10 @@ export function init(worker: Options): void { process.env.VITEST_WORKER_ID = String(message.workerId) reportMemory = message.options.reportMemory + if (message.context.config.isAgent) { + disableDefaultColors() + } + traces ??= await new Traces({ enabled: message.traces.enabled, sdkPath: message.traces.sdkPath, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9b5344470..da59eedd0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -133,8 +133,8 @@ catalogs: specifier: ^0.3.2 version: 0.3.2 tinyrainbow: - specifier: ^3.0.3 - version: 3.0.3 + specifier: ^3.1.0 + version: 3.1.0 tinyspy: specifier: ^4.0.4 version: 4.0.4 @@ -537,7 +537,7 @@ importers: version: 3.0.2 tinyrainbow: specifier: 'catalog:' - version: 3.0.3 + version: 3.1.0 ws: specifier: 'catalog:' version: 8.19.0 @@ -586,7 +586,7 @@ importers: version: link:../mocker tinyrainbow: specifier: 'catalog:' - version: 3.0.3 + version: 3.1.0 devDependencies: playwright: specifier: ^1.58.2 @@ -658,7 +658,7 @@ importers: version: 2.1.1 tinyrainbow: specifier: 'catalog:' - version: 3.0.3 + version: 3.1.0 devDependencies: '@types/istanbul-lib-coverage': specifier: 'catalog:' @@ -719,7 +719,7 @@ importers: version: 4.0.0-rc.1 tinyrainbow: specifier: 'catalog:' - version: 3.0.3 + version: 3.1.0 devDependencies: '@types/istanbul-lib-coverage': specifier: 'catalog:' @@ -762,7 +762,7 @@ importers: version: 6.2.2 tinyrainbow: specifier: 'catalog:' - version: 3.0.3 + version: 3.1.0 devDependencies: '@vitest/runner': specifier: workspace:* @@ -809,7 +809,7 @@ importers: dependencies: tinyrainbow: specifier: 'catalog:' - version: 3.0.3 + version: 3.1.0 devDependencies: '@types/react-is': specifier: ^19.2.0 @@ -876,7 +876,7 @@ importers: version: 0.2.15 tinyrainbow: specifier: 'catalog:' - version: 3.0.3 + version: 3.1.0 vitest: specifier: workspace:* version: link:../vitest @@ -988,7 +988,7 @@ importers: version: 2.0.0 tinyrainbow: specifier: 'catalog:' - version: 3.0.3 + version: 3.1.0 devDependencies: '@jridgewell/trace-mapping': specifier: 'catalog:' @@ -1073,7 +1073,7 @@ importers: version: 0.2.15 tinyrainbow: specifier: 'catalog:' - version: 3.0.3 + version: 3.1.0 vite: specifier: 7.1.5 version: 7.1.5(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) @@ -1529,7 +1529,7 @@ importers: version: 0.3.0 tinyrainbow: specifier: 'catalog:' - version: 3.0.3 + version: 3.1.0 tinyspy: specifier: ^4.0.4 version: 4.0.4 @@ -1634,7 +1634,7 @@ importers: version: 1.0.2 tinyrainbow: specifier: 'catalog:' - version: 3.0.3 + version: 3.1.0 vite: specifier: 7.1.5 version: 7.1.5(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) @@ -9872,8 +9872,8 @@ packages: picocolors: optional: true - tinyrainbow@3.0.3: - resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} tinyspy@4.0.4: @@ -19285,7 +19285,7 @@ snapshots: optionalDependencies: picocolors: 1.1.1 - tinyrainbow@3.0.3: {} + tinyrainbow@3.1.0: {} tinyspy@4.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 88066971c..c18207096 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -85,7 +85,7 @@ catalog: tinyexec: ^1.0.2 tinyglobby: ^0.2.15 tinyhighlight: ^0.3.2 - tinyrainbow: ^3.0.3 + tinyrainbow: ^3.1.0 tinyspy: ^4.0.4 typescript: ^5.9.3 unocss: ^66.6.6 diff --git a/test/config/test/console-color.test.ts b/test/config/test/console-color.test.ts index b552908a1..29a8b54c3 100644 --- a/test/config/test/console-color.test.ts +++ b/test/config/test/console-color.test.ts @@ -1,5 +1,5 @@ import { expect, test } from 'vitest' -import { runVitest } from '../../test-utils' +import { runVitest, runVitestCli } from '../../test-utils' test('with color', async () => { const { stdout } = await runVitest({ @@ -30,6 +30,20 @@ test('without color', async () => { expect(stdout).not.toContain('\x1B[33mtrue\x1B[39m\n') }) +test('agent', async () => { + // Agent check is done on module import, so new process is needed + const { stdout } = await runVitestCli({ + preserveAnsi: true, + nodeOptions: { env: { AI_AGENT: 'copilot' } }, + }, '--root', 'fixtures/console-color', '--reporter', 'default') + + expect.soft(stdout).toContain('true\n') + expect.soft(stdout).not.toContain('\x1B[33mtrue\x1B[39m\n') + + expect.soft(stdout).toContain(' RUN') + expect.soft(stdout).not.toContain('\x1B[46m RUN') +}) + test.skipIf(process.platform === 'win32')('without color, forks pool in non-TTY parent', async () => { const { stdout } = await runVitest({ root: 'fixtures/console-color', -- 2.51.2