Skip to content

Commit

Permalink
feat(reporters): add first 5/7 test run life cycles
Browse files Browse the repository at this point in the history
  • Loading branch information
AriPerkkio committed Dec 29, 2024
1 parent 6f0da08 commit 64abbda
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 10 deletions.
5 changes: 4 additions & 1 deletion packages/vitest/src/node/reporters/reported-tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,9 @@ abstract class SuiteImplementation extends ReportedTaskImplementation {
if (mode === 'skip' || mode === 'todo' || state === 'skip' || state === 'todo') {
return 'skipped'
}
if (state === 'queued') {
return 'queued'
}
if (state == null || state === 'run' || state === 'only') {
return 'pending'
}
Expand Down Expand Up @@ -518,7 +521,7 @@ function buildOptions(
}
}

export type TestSuiteState = 'skipped' | 'pending' | 'failed' | 'passed'
export type TestSuiteState = 'skipped' | 'pending' | 'queued' | 'failed' | 'passed'

export type TestResult =
| TestResultPassed
Expand Down
2 changes: 2 additions & 0 deletions packages/vitest/src/node/reporters/task-parser.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// TODO: Remove once Reporter API implements these life cycles

import type { File, Task, TaskResultPack, Test } from '@vitest/runner'
import type { Vitest } from '../core'
import { getTests } from '@vitest/runner/utils'
Expand Down
6 changes: 5 additions & 1 deletion packages/vitest/src/node/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,11 @@ export class StateManager {
}
}

getReportedEntity(task: Task) {
getReportedEntity(task?: Task) {
if (!task) {
return undefined
}

return this.reportedTasksMap.get(task)
}

Expand Down
105 changes: 100 additions & 5 deletions packages/vitest/src/node/test-run.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,126 @@
import type { TaskResultPack } from '@vitest/runner'
import type { Vitest } from './core'
import type { TestModule } from './reporters/reported-tasks'
import type { TestCase, TestModule } from './reporters/reported-tasks'
import type { TestSpecification } from './spec'

export class TestRun {
private tests = emptyCounters()
private suites = emptyCounters()

// Internal state to prevent reporting duplicates and guaranteeing correct order
private queuedTestModules = new Set<TestModule['id']>()
private runningTestModules = new Set<TestModule['id']>()
private finishedTestModules = new Set<TestModule['id']>()

private runningTestCases = new Set<TestCase['id']>()
private finishedTestCases = new Set<TestCase['id']>()

constructor(private vitest: Vitest) {}

async start(specifications: TestSpecification[]) {
this.tests = emptyCounters()
this.suites = emptyCounters()
this.suites.total = specifications.length

this.queuedTestModules.clear()
this.runningTestModules.clear()
this.finishedTestModules.clear()
this.runningTestCases.clear()
this.finishedTestCases.clear()

await this.vitest.report('onTestRunStart', specifications)
}

enqueued(module: TestModule) {
enqueued(_module: TestModule) {
// TODO
}

collected(modules: TestModule[]) {
collected(_modules: TestModule[]) {
// TODO
}

updated(update: TaskResultPack[]) {
// TODO
async updated(update: TaskResultPack[]) {
const queuedTestModules: TestModule[] = []
const runningTestModules: TestModule[] = []
const finishedTestModules: TestModule[] = []

const runningTestCases: TestCase[] = []
const finishedTestCases: TestCase[] = []

for (const [id] of update) {
const entity = this.vitest.state.getReportedEntity(this.vitest.state.idMap.get(id))

if (!entity) {
continue
}

if (entity.type === 'module') {
const state = entity.state()

if (state === 'queued' && !this.queuedTestModules.has(entity.id)) {
this.queuedTestModules.add(entity.id)
queuedTestModules.push(entity)
}

if (state === 'pending' && !this.runningTestModules.has(entity.id)) {
this.runningTestModules.add(entity.id)
runningTestModules.push(entity)

// If queue phase was fast, it's possible that it was never reported
if (!this.queuedTestModules.has(entity.id)) {
this.queuedTestModules.add(entity.id)
queuedTestModules.push(entity)
}

// TODO: Check if skipped TestCases of finished module were reported.
// If not, get TestCases here and push them to TestCase arrays+sets.
}

if (state !== 'pending' && state !== 'queued' && !this.finishedTestModules.has(entity.id)) {
this.finishedTestModules.add(entity.id)
finishedTestModules.push(entity)

if (!this.queuedTestModules.has(entity.id)) {
this.queuedTestModules.add(entity.id)
queuedTestModules.push(entity)
}

// If module run was fast, it's possible that it was never reported to be running
if (!this.runningTestModules.has(entity.id)) {
this.runningTestModules.add(entity.id)
runningTestModules.push(entity)
}
}
}

if (entity.type === 'test') {
const state = entity.result().state

if (state === 'pending' && !this.runningTestCases.has(entity.id)) {
this.runningTestCases.add(entity.id)
runningTestCases.push(entity)
}

if (state !== 'pending' && !this.finishedTestCases.has(entity.id)) {
this.finishedTestCases.add(entity.id)
finishedTestCases.push(entity)

// If test finished quickly, it's possible that it was never reported as running
if (!this.runningTestCases.has(entity.id)) {
this.runningTestCases.add(entity.id)
runningTestCases.push(entity)
}
}
}
}

// Order of reporting is important here
await Promise.all(finishedTestCases.map(testCase => this.vitest.report('onTestCaseFinished', testCase)))
await Promise.all(finishedTestModules.map(module => this.vitest.report('onTestModuleFinished', module)))

await Promise.all(queuedTestModules.map(module => this.vitest.report('onTestModuleQueued', module)))
await Promise.all(runningTestModules.map(module => this.vitest.report('onTestModulePrepare', module)))
await Promise.all(runningTestCases.map(testCase => this.vitest.report('onTestCasePrepare', testCase)))
}

async end() {
Expand Down
11 changes: 8 additions & 3 deletions packages/vitest/src/node/types/reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ import type { SerializedError } from '@vitest/utils'
import type { SerializedTestSpecification } from '../../runtime/types/utils'
import type { Awaitable, UserConsoleLog } from '../../types/general'
import type { Vitest } from '../core'
import type { TestModule } from '../reporters/reported-tasks'
import type { TestCase, TestModule } from '../reporters/reported-tasks'
import type { TestSpecification } from '../spec'
import type { TestModule } from '../reporters/reported-tasks'

export interface Reporter {
onInit?: (ctx: Vitest) => void
onPathsCollected?: (paths?: string[]) => Awaitable<void>
onSpecsCollected?: (specs?: SerializedTestSpecification[]) => Awaitable<void>
onTestModuleQueued?: (file: TestModule) => Awaitable<void>
onCollected?: (files?: File[]) => Awaitable<void>
onFinished?: (
files: File[],
Expand All @@ -33,4 +31,11 @@ export interface Reporter {
errors: SerializedError[],
reason: 'passed' | 'interrupted' | 'failed'
) => Awaitable<void>
onTestModuleQueued?: (testModule: TestModule) => Awaitable<void>
onTestModulePrepare?: (testModule: TestModule) => Awaitable<void>
onTestModuleFinished?: (testModule: TestModule) => Awaitable<void>

// TODO: This was planned as onTestFinished, but maybe we could use TestCase in name directly?
onTestCasePrepare?: (testCase: TestCase) => Awaitable<void>
onTestCaseFinished?: (testCase: TestCase) => Awaitable<void>
}

0 comments on commit 64abbda

Please sign in to comment.