Skip to content

Commit 886e0c4

Browse files
committed
feat: Implement AgentReporter as a separate reporter.
1 parent ea67b09 commit 886e0c4

15 files changed

Lines changed: 85 additions & 90 deletions

File tree

packages/vitest/src/node/cli/cac.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,6 @@ function normalizeCliOptions(cliFilters: string[], argv: CliOptions): CliOptions
300300

301301
async function start(mode: VitestRunMode, cliFilters: string[], options: CliOptions): Promise<void> {
302302
try {
303-
const { detectAgent } = await import('../../utils/env')
304-
if (detectAgent()) {
305-
options.env = { AI_AGENT: '1', ...options.env }
306-
}
307303
const { startVitest } = await import('./cli-api')
308304
const ctx = await startVitest(mode, cliFilters.map(normalize), normalizeCliOptions(cliFilters, options))
309305
if (!ctx.shouldKeepServer()) {

packages/vitest/src/node/config/resolveConfig.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
defaultPort,
2424
} from '../../constants'
2525
import { benchmarkConfigDefaults, configDefaults } from '../../defaults'
26-
import { isCI, stdProvider } from '../../utils/env'
26+
import { isAgent, isCI, stdProvider } from '../../utils/env'
2727
import { getWorkersCountByPercentage } from '../../utils/workers'
2828
import { BaseSequencer } from '../sequencers/BaseSequencer'
2929
import { RandomSequencer } from '../sequencers/RandomSequencer'
@@ -729,7 +729,7 @@ export function resolveConfig(
729729
}
730730

731731
if (!resolved.reporters.length) {
732-
resolved.reporters.push(['default', {}])
732+
resolved.reporters.push([isAgent ? 'agent' : 'default', {}])
733733

734734
// also enable github-actions reporter as a default
735735
if (process.env.GITHUB_ACTIONS === 'true') {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import type { Task } from '@vitest/runner'
2+
import type { UserConsoleLog } from '../../types/general'
3+
import type { TestSpecification } from '../test-specification'
4+
import type { DefaultReporterOptions } from './default'
5+
import type { TestCase, TestModule, TestModuleState, TestResult } from './reported-tasks'
6+
import c from 'tinyrainbow'
7+
import { DefaultReporter } from './default'
8+
import { separator, taskFail } from './renderers/utils'
9+
10+
export class AgentReporter extends DefaultReporter {
11+
renderSucceed = false
12+
13+
constructor(options: DefaultReporterOptions = {}) {
14+
super({ ...options, summary: false })
15+
}
16+
17+
onTestRunStart(specifications: ReadonlyArray<TestSpecification>): void {
18+
this.renderSucceed = false
19+
super.onTestRunStart(specifications)
20+
}
21+
22+
protected logFailedTask(task: Task): void {
23+
for (const log of task.logs || []) {
24+
this.onUserConsoleLog(log, 'failed')
25+
}
26+
}
27+
28+
protected printTestModule(testModule: TestModule): void {
29+
if (testModule.state() !== 'failed') {
30+
return
31+
}
32+
super.printTestModule(testModule)
33+
}
34+
35+
protected printTestCase(_moduleState: TestModuleState, test: TestCase): void {
36+
const testResult = test.result()
37+
if (testResult.state === 'failed') {
38+
const padding = this.getTestIndentation(test.task)
39+
const suffix = this.getTestCaseSuffix(test)
40+
this.log(c.red(` ${padding}${taskFail} ${this.getTestName(test.task, separator)}`) + suffix)
41+
}
42+
}
43+
44+
shouldLog(log: UserConsoleLog, taskState?: TestResult['state']): boolean {
45+
if (taskState !== 'failed') {
46+
return false
47+
}
48+
return super.shouldLog(log, taskState)
49+
}
50+
}

packages/vitest/src/node/reporters/base.ts

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ const BADGE_PADDING = ' '
3737

3838
export interface BaseOptions {
3939
isTTY?: boolean
40-
isAgent?: boolean
4140
}
4241

4342
export abstract class BaseReporter implements Reporter {
@@ -50,14 +49,12 @@ export abstract class BaseReporter implements Reporter {
5049
renderSucceed = false
5150

5251
protected verbose = false
53-
protected isAgent = false
5452

5553
private _filesInWatchMode = new Map<string, number>()
5654
private _timeStart = formatTimeString(new Date())
5755

5856
constructor(options: BaseOptions = {}) {
5957
this.isTTY = options.isTTY ?? isTTY
60-
this.isAgent = options.isAgent ?? false
6158
}
6259

6360
onInit(ctx: Vitest): void {
@@ -120,8 +117,8 @@ export abstract class BaseReporter implements Reporter {
120117
this.printTestModule(testModule)
121118
}
122119

123-
private logFailedTask(task: Task) {
124-
if (this.ctx.config.silent === 'passed-only' || this.isAgent) {
120+
protected logFailedTask(task: Task): void {
121+
if (this.ctx.config.silent === 'passed-only') {
125122
for (const log of task.logs || []) {
126123
this.onUserConsoleLog(log, 'failed')
127124
}
@@ -134,10 +131,6 @@ export abstract class BaseReporter implements Reporter {
134131
return
135132
}
136133

137-
if (this.isAgent && moduleState !== 'failed') {
138-
return
139-
}
140-
141134
let testsCount = 0
142135
let failedCount = 0
143136
let skippedCount = 0
@@ -207,10 +200,6 @@ export abstract class BaseReporter implements Reporter {
207200
this.log(c.red(` ${padding}${taskFail} ${this.getTestName(test.task, separator)}`) + suffix)
208201
}
209202

210-
else if (this.isAgent) {
211-
// In agent mode, only print failed tests
212-
}
213-
214203
// also print slow tests
215204
else if (duration > this.ctx.config.slowTestThreshold) {
216205
this.log(` ${padding}${c.yellow(c.dim(F_CHECK))} ${this.getTestName(test.task, separator)} ${suffix}`)
@@ -511,10 +500,6 @@ export abstract class BaseReporter implements Reporter {
511500
return false
512501
}
513502

514-
if (this.isAgent && taskState !== 'failed') {
515-
return false
516-
}
517-
518503
if (this.ctx.config.onConsoleLog) {
519504
const task = log.taskId ? this.ctx.state.idMap.get(log.taskId) : undefined
520505
const entity = task && this.ctx.state.getReportedEntity(task)

packages/vitest/src/node/reporters/default.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export class DefaultReporter extends BaseReporter {
2222
...options,
2323
}
2424

25-
if (!this.isTTY || this.isAgent) {
25+
if (!this.isTTY) {
2626
this.options.summary = false
2727
}
2828

@@ -32,10 +32,7 @@ export class DefaultReporter extends BaseReporter {
3232
}
3333

3434
onTestRunStart(specifications: ReadonlyArray<TestSpecification>): void {
35-
if (this.isAgent) {
36-
this.renderSucceed = false
37-
}
38-
else if (this.isTTY) {
35+
if (this.isTTY) {
3936
if (this.renderSucceed === undefined) {
4037
this.renderSucceed = !!this.renderSucceed
4138
}

packages/vitest/src/node/reporters/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { GithubActionsReporterOptions } from './github-actions'
66
import type { HTMLOptions } from './html'
77
import type { JsonOptions } from './json'
88
import type { JUnitOptions } from './junit'
9+
import { AgentReporter } from './agent'
910
import { BlobReporter } from './blob'
1011
import { DefaultReporter } from './default'
1112
import { DotReporter } from './dot'
@@ -19,6 +20,7 @@ import { TreeReporter } from './tree'
1920
import { VerboseReporter } from './verbose'
2021

2122
export {
23+
AgentReporter,
2224
DefaultReporter,
2325
DotReporter,
2426
GithubActionsReporter,
@@ -46,6 +48,7 @@ export type {
4648

4749
export const ReportersMap = {
4850
'default': DefaultReporter as typeof DefaultReporter,
51+
'agent': AgentReporter as typeof AgentReporter,
4952
'blob': BlobReporter as typeof BlobReporter,
5053
'verbose': VerboseReporter as typeof VerboseReporter,
5154
'dot': DotReporter as typeof DotReporter,
@@ -62,6 +65,7 @@ export type BuiltinReporters = keyof typeof ReportersMap
6265

6366
export interface BuiltinReporterOptions {
6467
'default': DefaultReporterOptions
68+
'agent': DefaultReporterOptions
6569
'verbose': DefaultReporterOptions
6670
'dot': BaseOptions
6771
'tree': BaseOptions

packages/vitest/src/node/reporters/utils.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,34 +37,30 @@ function createReporters(
3737
ctx: Vitest,
3838
): Promise<Array<Reporter | DefaultReporter | BlobReporter | DotReporter | JsonReporter | TapReporter | JUnitReporter | HangingProcessReporter | GithubActionsReporter>> {
3939
const runner = ctx.runner
40-
const isAgent = !!ctx.config.env?.AI_AGENT
4140
const promisedReporters = reporterReferences.map(
4241
async (referenceOrInstance) => {
4342
if (Array.isArray(referenceOrInstance)) {
4443
const [reporterName, reporterOptions] = referenceOrInstance
45-
const options = isAgent && reporterName in ReportersMap && (reporterOptions as any)?.isAgent == null
46-
? { ...reporterOptions, isAgent: true }
47-
: reporterOptions
4844

4945
if (reporterName === 'html') {
5046
await ctx.packageInstaller.ensureInstalled('@vitest/ui', ctx.config.root, ctx.version)
5147
const CustomReporter = await loadCustomReporterModule(
5248
'@vitest/ui/reporter',
5349
runner,
5450
)
55-
return new CustomReporter(options)
51+
return new CustomReporter(reporterOptions)
5652
}
5753
else if (reporterName in ReportersMap) {
5854
const BuiltinReporter
5955
= ReportersMap[reporterName as BuiltinReporters]
60-
return new BuiltinReporter(options)
56+
return new BuiltinReporter(reporterOptions)
6157
}
6258
else {
6359
const CustomReporter = await loadCustomReporterModule(
6460
reporterName,
6561
runner,
6662
)
67-
return new CustomReporter(options)
63+
return new CustomReporter(reporterOptions)
6864
}
6965
}
7066

packages/vitest/src/public/node.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export { VmThreadsPoolWorker } from '../node/pools/workers/vmThreadsWorker'
4343
export type { SerializedTestProject, TestProject } from '../node/project'
4444

4545
export {
46+
AgentReporter,
4647
BenchmarkReporter,
4748
BenchmarkReportsMap,
4849
DefaultReporter,

packages/vitest/src/public/reporters.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export {
2+
AgentReporter,
23
BenchmarkReporter,
34
BenchmarkReportsMap,
45
DefaultReporter,

packages/vitest/src/utils/env.ts

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,4 @@ export const isDeno: boolean
1212
export const isWindows: boolean = (isNode || isDeno) && process.platform === 'win32'
1313
export const isBrowser: boolean = typeof window !== 'undefined'
1414
export const isTTY: boolean = ((isNode || isDeno) && process.stdout?.isTTY && !isCI)
15-
export function detectAgent(): boolean {
16-
if (!(isNode || isDeno)) {
17-
return false
18-
}
19-
const env = process.env
20-
if (env.AI_AGENT === '0' || env.AI_AGENT === 'false') {
21-
return false
22-
}
23-
return !!(
24-
env.AI_AGENT
25-
|| env.CURSOR_TRACE_ID
26-
|| env.CURSOR_AGENT
27-
|| env.GEMINI_CLI
28-
|| env.CODEX_SANDBOX
29-
|| env.AUGMENT_AGENT
30-
|| env.OPENCODE_CLIENT
31-
|| env.CLAUDECODE
32-
|| env.CLAUDE_CODE
33-
|| env.REPL_ID
34-
)
35-
}
36-
export { isCI, provider as stdProvider } from 'std-env'
15+
export { isAgent, isCI, provider as stdProvider } from 'std-env'

0 commit comments

Comments
 (0)