Skip to content

Commit

Permalink
feat(api): make spec into a class instead of a tuple (#6355)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va authored Aug 19, 2024
1 parent e03683c commit 874a121
Show file tree
Hide file tree
Showing 12 changed files with 134 additions and 50 deletions.
4 changes: 0 additions & 4 deletions packages/browser/src/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ export async function createBrowserServer(
) {
const server = new BrowserServer(project, '/')

const root = project.config.root

await project.ctx.packageInstaller.ensureInstalled('@vitest/browser', root)

const configPath = typeof configFile === 'string' ? configFile : false

const vite = await createServer({
Expand Down
12 changes: 6 additions & 6 deletions packages/vitest/src/api/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,14 @@ export function setup(ctx: Vitest, _server?: ViteDevServer) {
return ctx.state.getUnhandledErrors()
},
async getTestFiles() {
const spec = await ctx.globTestFiles()
return spec.map(([project, file, options]) => [
const spec = await ctx.globTestSpecs()
return spec.map(spec => [
{
name: project.config.name,
root: project.config.root,
name: spec.project.config.name,
root: spec.project.config.root,
},
file,
options,
spec.moduleId,
{ pool: spec.pool },
])
},
},
Expand Down
45 changes: 26 additions & 19 deletions packages/vitest/src/node/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import { WebSocketReporter } from '../api/setup'
import type { SerializedCoverageConfig } from '../runtime/config'
import type { SerializedSpec } from '../runtime/types/utils'
import type { ArgumentsType, OnServerRestartHandler, ProvidedContext, UserConsoleLog } from '../types/general'
import { createPool, getFilePoolName } from './pool'
import type { ProcessPool, WorkspaceSpec } from './pool'
import { createPool, getFilePoolName } from './pool'
import { createBenchmarkReporters, createReporters } from './reporters/utils'
import { StateManager } from './state'
import { resolveConfig } from './config/resolveConfig'
Expand Down Expand Up @@ -437,7 +437,7 @@ export class Vitest {
}
}

private async getTestDependencies([project, filepath]: WorkspaceSpec, deps = new Set<string>()) {
private async getTestDependencies(spec: WorkspaceSpec, deps = new Set<string>()) {
const addImports = async (project: WorkspaceProject, filepath: string) => {
if (deps.has(filepath)) {
return
Expand All @@ -459,8 +459,8 @@ export class Vitest {
}))
}

await addImports(project, filepath)
deps.delete(filepath)
await addImports(spec.project.workspaceProject, spec.moduleId)
deps.delete(spec.moduleId)

return deps
}
Expand Down Expand Up @@ -531,18 +531,18 @@ export class Vitest {
for (const project of this.projects) {
if (project.isTestFile(file)) {
const pool = getFilePoolName(project, file)
specs.push([project, file, { pool }])
specs.push(project.createSpec(file, pool))
}
if (project.isTypecheckFile(file)) {
specs.push([project, file, { pool: 'typescript' }])
specs.push(project.createSpec(file, 'typescript'))
}
}
specs.forEach(spec => this.ensureSpecCached(spec))
return specs
}

async initializeGlobalSetup(paths: WorkspaceSpec[]) {
const projects = new Set(paths.map(([project]) => project))
const projects = new Set(paths.map(spec => spec.project.workspaceProject))
const coreProject = this.getCoreWorkspaceProject()
if (!projects.has(coreProject)) {
projects.add(coreProject)
Expand All @@ -566,16 +566,16 @@ export class Vitest {
async runFiles(specs: WorkspaceSpec[], allTestsRun: boolean) {
await this.initializeDistPath()

const filepaths = specs.map(([, file]) => file)
const filepaths = specs.map(spec => spec.moduleId)
this.state.collectPaths(filepaths)

await this.report('onPathsCollected', filepaths)
await this.report('onSpecsCollected', specs.map(
([project, file, options]) =>
spec =>
[{
name: project.config.name,
root: project.config.root,
}, file, options] satisfies SerializedSpec,
name: spec.project.config.name,
root: spec.project.config.root,
}, spec.moduleId, { pool: spec.pool }] satisfies SerializedSpec,
))

// previous run
Expand Down Expand Up @@ -618,7 +618,7 @@ export class Vitest {
})()
.finally(async () => {
// can be duplicate files if different projects are using the same file
const files = Array.from(new Set(specs.map(([, p]) => p)))
const files = Array.from(new Set(specs.map(spec => spec.moduleId)))
const coverage = await this.coverageProvider?.generateCoverage({ allTestsRun })

await this.report('onFinished', this.state.getFiles(files), this.state.getUnhandledErrors(), coverage)
Expand All @@ -638,7 +638,7 @@ export class Vitest {
async collectFiles(specs: WorkspaceSpec[]) {
await this.initializeDistPath()

const filepaths = specs.map(([, file]) => file)
const filepaths = specs.map(spec => spec.moduleId)
this.state.collectPaths(filepaths)

// previous run
Expand Down Expand Up @@ -709,7 +709,7 @@ export class Vitest {
else { this.configOverride.project = pattern }

this.projects = this.resolvedProjects.filter(p => p.getName() === pattern)
const files = (await this.globTestFiles()).map(([, file]) => file)
const files = (await this.globTestSpecs()).map(spec => spec.moduleId)
await this.rerunFiles(files, 'change project filter')
}

Expand Down Expand Up @@ -1083,28 +1083,35 @@ export class Vitest {
}

public async getTestFilepaths() {
return this.globTestFiles().then(files => files.map(([, file]) => file))
return this.globTestSpecs().then(specs => specs.map(spec => spec.moduleId))
}

public async globTestFiles(filters: string[] = []) {
public async globTestSpecs(filters: string[] = []) {
const files: WorkspaceSpec[] = []
await Promise.all(this.projects.map(async (project) => {
const { testFiles, typecheckTestFiles } = await project.globTestFiles(filters)
testFiles.forEach((file) => {
const pool = getFilePoolName(project, file)
const spec: WorkspaceSpec = [project, file, { pool }]
const spec = project.createSpec(file, pool)
this.ensureSpecCached(spec)
files.push(spec)
})
typecheckTestFiles.forEach((file) => {
const spec: WorkspaceSpec = [project, file, { pool: 'typescript' }]
const spec = project.createSpec(file, 'typescript')
this.ensureSpecCached(spec)
files.push(spec)
})
}))
return files
}

/**
* @deprecated use globTestSpecs instead
*/
public async globTestFiles(filters: string[] = []) {
return this.globTestSpecs(filters)
}

private ensureSpecCached(spec: WorkspaceSpec) {
const file = spec[1]
const specs = this._cachedSpecs.get(file) || []
Expand Down
17 changes: 16 additions & 1 deletion packages/vitest/src/node/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,23 @@ import { createVmThreadsPool } from './pools/vmThreads'
import type { WorkspaceProject } from './workspace'
import { createTypecheckPool } from './pools/typecheck'
import { createVmForksPool } from './pools/vmForks'
import type { WorkspaceSpec as _WorkspaceSpec } from './spec'

export type WorkspaceSpec = _WorkspaceSpec & [
/**
* @deprecated use spec.project instead
*/
project: WorkspaceProject,
/**
* @deprecated use spec.moduleId instead
*/
file: string,
/**
* @deprecated use spec.pool instead
*/
options: { pool: Pool },
]

export type WorkspaceSpec = [project: WorkspaceProject, testFile: string, options: { pool: Pool }]
export type RunWithFiles = (
files: WorkspaceSpec[],
invalidates?: string[]
Expand Down
8 changes: 5 additions & 3 deletions packages/vitest/src/node/pools/forks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,17 +166,19 @@ export function createForksPool(
}

const workspaceMap = new Map<string, WorkspaceProject[]>()
for (const [project, file] of specs) {
for (const spec of specs) {
const file = spec.moduleId
const project = spec.project.workspaceProject
const workspaceFiles = workspaceMap.get(file) ?? []
workspaceFiles.push(project)
workspaceMap.set(file, workspaceFiles)
}

const singleFork = specs.filter(
([project]) => project.config.poolOptions?.forks?.singleFork,
spec => spec.project.config.poolOptions?.forks?.singleFork,
)
const multipleForks = specs.filter(
([project]) => !project.config.poolOptions?.forks?.singleFork,
spec => !spec.project.config.poolOptions?.forks?.singleFork,
)

if (multipleForks.length) {
Expand Down
4 changes: 2 additions & 2 deletions packages/vitest/src/node/pools/threads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,10 @@ export function createThreadsPool(
}

const singleThreads = specs.filter(
([project]) => project.config.poolOptions?.threads?.singleThread,
spec => spec.project.config.poolOptions?.threads?.singleThread,
)
const multipleThreads = specs.filter(
([project]) => !project.config.poolOptions?.threads?.singleThread,
spec => !spec.project.config.poolOptions?.threads?.singleThread,
)

if (multipleThreads.length) {
Expand Down
12 changes: 6 additions & 6 deletions packages/vitest/src/node/pools/typecheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,20 @@ export function createTypecheckPool(ctx: Vitest): ProcessPool {
}

async function collectTests(specs: WorkspaceSpec[]) {
const specsByProject = groupBy(specs, ([project]) => project.getName())
const specsByProject = groupBy(specs, spec => spec.project.name)
for (const name in specsByProject) {
const project = specsByProject[name][0][0]
const files = specsByProject[name].map(([_, file]) => file)
const checker = await createWorkspaceTypechecker(project, files)
const project = specsByProject[name][0].project
const files = specsByProject[name].map(spec => spec.moduleId)
const checker = await createWorkspaceTypechecker(project.workspaceProject, files)
checker.setFiles(files)
await checker.collectTests()
ctx.state.collectFiles(project, checker.getTestFiles())
ctx.state.collectFiles(project.workspaceProject, checker.getTestFiles())
await ctx.report('onCollected')
}
}

async function runTests(specs: WorkspaceSpec[]) {
const specsByProject = groupBy(specs, ([project]) => project.getName())
const specsByProject = groupBy(specs, spec => spec.project.name)
const promises: Promise<void>[] = []

for (const name in specsByProject) {
Expand Down
6 changes: 3 additions & 3 deletions packages/vitest/src/node/sequencers/BaseSequencer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class BaseSequencer implements TestSequencer {
const shardEnd = shardSize * index
return [...files]
.map((spec) => {
const fullPath = resolve(slash(config.root), slash(spec[1]))
const fullPath = resolve(slash(config.root), slash(spec.moduleId))
const specPath = fullPath?.slice(config.root.length)
return {
spec,
Expand All @@ -37,8 +37,8 @@ export class BaseSequencer implements TestSequencer {
public async sort(files: WorkspaceSpec[]): Promise<WorkspaceSpec[]> {
const cache = this.ctx.cache
return [...files].sort((a, b) => {
const keyA = `${a[0].getName()}:${relative(this.ctx.config.root, a[1])}`
const keyB = `${b[0].getName()}:${relative(this.ctx.config.root, b[1])}`
const keyA = `${a.project.name}:${relative(this.ctx.config.root, a.moduleId)}`
const keyB = `${b.project.name}:${relative(this.ctx.config.root, b.moduleId)}`

const aState = cache.getFileTestResults(keyA)
const bState = cache.getFileTestResults(keyB)
Expand Down
54 changes: 54 additions & 0 deletions packages/vitest/src/node/spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { TestProject } from './reported-workspace-project'
import type { Pool } from './types/pool-options'
import type { WorkspaceProject } from './workspace'

export class WorkspaceSpec {
// backwards compatibility
/**
* @deprecated
*/
public readonly 0: WorkspaceProject
/**
* @deprecated
*/
public readonly 1: string
/**
* @deprecated
*/
public readonly 2: { pool: Pool }

public readonly project: TestProject
public readonly moduleId: string
public readonly pool: Pool
// public readonly location: WorkspaceSpecLocation | undefined

constructor(
workspaceProject: WorkspaceProject,
moduleId: string,
pool: Pool,
// location?: WorkspaceSpecLocation | undefined,
) {
this[0] = workspaceProject
this[1] = moduleId
this[2] = { pool }
this.project = workspaceProject.testProject
this.moduleId = moduleId
this.pool = pool
// this.location = location
}

/**
* for backwards compatibility
* @deprecated
*/
*[Symbol.iterator]() {
yield this.project.workspaceProject
yield this.moduleId
yield this.pool
}
}

// interface WorkspaceSpecLocation {
// start: number
// end: number
// }
6 changes: 6 additions & 0 deletions packages/vitest/src/node/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import { CoverageTransform } from './plugins/coverageTransform'
import { serializeConfig } from './config/serializeConfig'
import type { Vitest } from './core'
import { TestProject } from './reported-workspace-project'
import { WorkspaceSpec } from './spec'
import type { WorkspaceSpec as DeprecatedWorkspaceSpec } from './pool'

interface InitializeProjectOptions extends UserWorkspaceConfig {
workspaceConfigPath: string
Expand Down Expand Up @@ -151,6 +153,10 @@ export class WorkspaceProject {
}
}

public createSpec(moduleId: string, pool: string): DeprecatedWorkspaceSpec {
return new WorkspaceSpec(this, moduleId, pool) as DeprecatedWorkspaceSpec
}

async initializeGlobalSetup() {
if (this._globalSetups) {
return
Expand Down
4 changes: 3 additions & 1 deletion packages/vitest/src/utils/test-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ export async function groupFilesByEnv(
files: Array<WorkspaceSpec>,
) {
const filesWithEnv = await Promise.all(
files.map(async ([project, file]) => {
files.map(async (spec) => {
const file = spec.moduleId
const project = spec.project.workspaceProject
const code = await fs.readFile(file, 'utf-8')

// 1. Check for control comments in the file
Expand Down
12 changes: 7 additions & 5 deletions test/core/test/sequencers.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Vitest } from 'vitest'
import type { Vitest, WorkspaceProject } from 'vitest/node'
import { describe, expect, test, vi } from 'vitest'
import type { WorkspaceProject } from 'vitest/node'
import { RandomSequencer } from '../../../packages/vitest/src/node/sequencers/RandomSequencer'
import { BaseSequencer } from '../../../packages/vitest/src/node/sequencers/BaseSequencer'
import type { WorkspaceSpec } from '../../../packages/vitest/src/node/pool'
import { WorkspaceSpec } from '../../../packages/vitest/src/node/spec'
import type { WorkspaceSpec as DeprecatedWorkspaceSpec } from '../../../packages/vitest/src/node/pool'

function buildCtx() {
return {
Expand All @@ -19,14 +19,16 @@ function buildCtx() {

function buildWorkspace() {
return {
getName: () => 'test',
testProject: {
name: 'test',
},
} as any as WorkspaceProject
}

const workspace = buildWorkspace()

function workspaced(files: string[]) {
return files.map(file => [workspace, file, { pool: 'forks' }] satisfies WorkspaceSpec)
return files.map(file => new WorkspaceSpec(workspace, file, 'forks')) as DeprecatedWorkspaceSpec[]
}

describe('base sequencer', () => {
Expand Down

0 comments on commit 874a121

Please sign in to comment.