Skip to content

Commit 273affe

Browse files
committed
refactor: proposal for the public Vitest API
1 parent 9f78a91 commit 273affe

File tree

5 files changed

+112
-26
lines changed

5 files changed

+112
-26
lines changed

packages/browser/src/node/pool.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as nodeos from 'node:os'
22
import crypto from 'node:crypto'
33
import { relative } from 'pathe'
4-
import type { BrowserProvider, ProcessPool, Vitest, WorkspaceProject, WorkspaceSpec } from 'vitest/node'
4+
import type { BrowserProvider, ProcessPool, TestProject, TestSpecification, Vitest } from 'vitest/node'
55
import { createDebugger } from 'vitest/node'
66

77
const debug = createDebugger('vitest:browser:pool')
@@ -12,15 +12,15 @@ export function createBrowserPool(ctx: Vitest): ProcessPool {
1212
const waitForTests = async (
1313
method: 'run' | 'collect',
1414
contextId: string,
15-
project: WorkspaceProject,
15+
project: TestProject,
1616
files: string[],
1717
) => {
1818
const context = project.browser!.state.createAsyncContext(method, contextId, files)
1919
return await context
2020
}
2121

22-
const executeTests = async (method: 'run' | 'collect', project: WorkspaceProject, files: string[]) => {
23-
ctx.state.clearFiles(project, files)
22+
const executeTests = async (method: 'run' | 'collect', project: TestProject, files: string[]) => {
23+
ctx.state.clearFiles(project.workspaceProject, files)
2424
const browser = project.browser!
2525

2626
const threadsCount = getThreadsCount(project)
@@ -33,7 +33,7 @@ export function createBrowserPool(ctx: Vitest): ProcessPool {
3333

3434
if (!origin) {
3535
throw new Error(
36-
`Can't find browser origin URL for project "${project.getName()}" when running tests for files "${files.join('", "')}"`,
36+
`Can't find browser origin URL for project "${project.name}" when running tests for files "${files.join('", "')}"`,
3737
)
3838
}
3939

@@ -50,7 +50,7 @@ export function createBrowserPool(ctx: Vitest): ProcessPool {
5050

5151
debug?.(
5252
`[%s] Running %s tests in %s chunks (%s threads)`,
53-
project.getName() || 'core',
53+
project.name || 'core',
5454
files.length,
5555
chunks.length,
5656
threadsCount,
@@ -92,12 +92,12 @@ export function createBrowserPool(ctx: Vitest): ProcessPool {
9292
await Promise.all(promises)
9393
}
9494

95-
const runWorkspaceTests = async (method: 'run' | 'collect', specs: WorkspaceSpec[]) => {
96-
const groupedFiles = new Map<WorkspaceProject, string[]>()
97-
for (const [project, file] of specs) {
98-
const files = groupedFiles.get(project) || []
99-
files.push(file)
100-
groupedFiles.set(project, files)
95+
const runWorkspaceTests = async (method: 'run' | 'collect', specs: TestSpecification[]) => {
96+
const groupedFiles = new Map<TestProject, string[]>()
97+
for (const spec of specs) {
98+
const files = groupedFiles.get(spec.project) || []
99+
files.push(spec.moduleId)
100+
groupedFiles.set(spec.project, files)
101101
}
102102

103103
let isCancelled = false
@@ -120,7 +120,7 @@ export function createBrowserPool(ctx: Vitest): ProcessPool {
120120
? nodeos.availableParallelism()
121121
: nodeos.cpus().length
122122

123-
function getThreadsCount(project: WorkspaceProject) {
123+
function getThreadsCount(project: TestProject) {
124124
const config = project.config.browser
125125
if (!config.headless || !project.browser!.provider.supportsParallelism) {
126126
return 1
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { ProvidedContext } from '../types/general'
2+
import type { WorkspaceProject } from './workspace'
3+
4+
export class VitestContext {
5+
constructor(private workspaceProject: WorkspaceProject) {
6+
this.workspaceProject = workspaceProject
7+
}
8+
9+
/**
10+
* Provide a custom serializable context to the project. This context will be available for tests once they run.
11+
*/
12+
public provide<T extends keyof ProvidedContext & string>(
13+
key: T,
14+
value: ProvidedContext[T],
15+
): void {
16+
this.workspaceProject.provide(key, value)
17+
}
18+
19+
/**
20+
* Get a custom serializable context provided to the project.
21+
*/
22+
public get<T extends keyof ProvidedContext & string>(name: T): ProvidedContext[T] {
23+
return this.workspaceProject.getProvidedContext()[name]
24+
}
25+
}

packages/vitest/src/node/core.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ export class Vitest {
668668
process.exitCode = 1
669669
}
670670
})()
671-
.finally(async () => {
671+
.finally(() => {
672672
this.runningPromise = undefined
673673

674674
// all subsequent runs will treat this as a fresh run
@@ -1054,7 +1054,9 @@ export class Vitest {
10541054
else if (runningServers > 1) {
10551055
console.warn(`Tests closed successfully but something prevents ${runningServers} Vite servers from exiting`)
10561056
}
1057-
else { console.warn('Tests closed successfully but something prevents the main process from exiting') }
1057+
else {
1058+
console.warn('Tests closed successfully but something prevents the main process from exiting')
1059+
}
10581060

10591061
console.warn('You can try to identify the cause by enabling "hanging-process" reporter. See https://vitest.dev/config/#reporters')
10601062
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/* eslint-disable ts/method-signature-style */
2+
import type { ModuleNode, ViteDevServer } from 'vite'
3+
import { SnapshotManager } from '@vitest/snapshot/manager'
4+
import type { TestProject } from './reported-workspace-project'
5+
import type { TestModule } from './reporters/reported-tasks'
6+
import type { TestSpecification } from './spec'
7+
import type { ResolvedConfig } from './types/config'
8+
import type { VitestContext } from './context'
9+
10+
export interface Vitest {
11+
readonly config: ResolvedConfig
12+
readonly projects: TestProject[]
13+
readonly vite: ViteDevServer
14+
readonly moduleGraph: VitestModuleGraph
15+
readonly runner: VitestRunner
16+
readonly context: VitestContext
17+
readonly snapshot: VitestSnapshot
18+
19+
onClose(cb: () => void): void
20+
21+
close(): Promise<void>
22+
exit(): Promise<void>
23+
}
24+
25+
interface VitestModuleGraph {
26+
getTestModuleIds(moduleNames?: string[]): string[]
27+
getViteModuleNodeById(
28+
transformMode: 'web' | 'ssr' | 'browser',
29+
moduleId: string,
30+
): ModuleNode | undefined
31+
getViteModuleNodesById(moduleId: string): ModuleNode[]
32+
}
33+
34+
interface VitestRunner {
35+
// Vitest starts a standalone runner, will react on watch changes, it doesn't run tests
36+
start(): Promise<void>
37+
38+
// Vitest will still start in a standalone mode if `watch` is `true`
39+
run(): Promise<TestModule[]>
40+
runModules(moduleNames: string[]): Promise<TestModule[]>
41+
runTests(filters: TestSpecification[]): Promise<TestModule[]>
42+
}
43+
44+
class VitestSnapshot extends SnapshotManager {
45+
async update(_files?: string[]): Promise<void> {
46+
// TODO
47+
}
48+
}

packages/vitest/src/node/reported-workspace-project.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import type { ProvidedContext } from '../types/general'
22
import type { ResolvedConfig, ResolvedProjectConfig, SerializedConfig } from './types/config'
33
import type { WorkspaceProject } from './workspace'
44
import type { Vitest } from './core'
5+
import type { BrowserServer } from './types/browser'
6+
import { type WorkspaceSpec, getFilePoolName } from './pool'
7+
import { VitestContext } from './context'
58

69
export class TestProject {
710
/**
@@ -29,43 +32,51 @@ export class TestProject {
2932
*/
3033
public readonly name: string
3134

35+
/**
36+
* Context manager.
37+
*/
38+
public readonly context: VitestContext
39+
3240
constructor(workspaceProject: WorkspaceProject) {
3341
this.workspaceProject = workspaceProject
3442
this.vitest = workspaceProject.ctx
3543
this.globalConfig = workspaceProject.ctx.config
3644
this.config = workspaceProject.config
3745
this.name = workspaceProject.getName()
46+
this.context = new VitestContext(workspaceProject)
3847
}
3948

4049
/**
4150
* Serialized project configuration. This is the config that tests receive.
4251
*/
43-
public get serializedConfig() {
52+
public get serializedConfig(): SerializedConfig {
4453
return this.workspaceProject.getSerializableConfig()
4554
}
4655

4756
/**
48-
* Custom context provided to the project.
57+
* Browser server if the project has browser enabled.
4958
*/
50-
public context(): ProvidedContext {
51-
return this.workspaceProject.getProvidedContext()
59+
public get browser(): BrowserServer | null {
60+
return this.workspaceProject.browser || null
5261
}
5362

5463
/**
55-
* Provide a custom serializable context to the project. This context will be available for tests once they run.
64+
* Create test specification describing a test module.
65+
* @param moduleId File path to the module or an ID that Vite understands (like a virtual module).
66+
* @param pool The pool to run the test in. If not provided, a pool will be selected based on the project configuration.
5667
*/
57-
public provide<T extends keyof ProvidedContext & string>(
58-
key: T,
59-
value: ProvidedContext[T],
60-
): void {
61-
this.workspaceProject.provide(key, value)
68+
public createSpecification(moduleId: string, pool?: string): WorkspaceSpec {
69+
return this.workspaceProject.createSpec(
70+
moduleId,
71+
pool || getFilePoolName(this.workspaceProject, moduleId),
72+
)
6273
}
6374

6475
public toJSON(): SerializedTestProject {
6576
return {
6677
name: this.name,
6778
serializedConfig: this.serializedConfig,
68-
context: this.context(),
79+
context: this.workspaceProject.getProvidedContext(),
6980
}
7081
}
7182
}

0 commit comments

Comments
 (0)