Skip to content

Commit

Permalink
fix: resolve cwd correctly when initiating projects (#5582)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va authored Apr 22, 2024
1 parent e40f992 commit ec9d7c9
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 48 deletions.
21 changes: 8 additions & 13 deletions packages/vitest/src/node/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,31 +315,30 @@ export class Vitest {

const cwd = process.cwd()

const projects: (() => Promise<WorkspaceProject>)[] = []
const projects: WorkspaceProject[] = []

try {
// we have to resolve them one by one because CWD should depend on the project
for (const filepath of filteredWorkspaces) {
if (this.server.config.configFile === filepath) {
const project = await this.createCoreProject()
projects.push(() => Promise.resolve(project))
projects.push(project)
continue
}
const dir = filepath.endsWith('/') ? filepath.slice(0, -1) : dirname(filepath)
if (isMainThread)
process.chdir(dir)
// this just resolves the config, later we also wait when the server is resolved,
// but we can do that in parallel because it doesn't depend on process.cwd()
// this is strictly a performance optimization so we don't need to wait for server to start
projects.push(await initializeProject(filepath, this, { workspaceConfigPath, test: cliOverrides }))
projects.push(
await initializeProject(filepath, this, { workspaceConfigPath, test: cliOverrides }),
)
}
}
finally {
if (isMainThread)
process.chdir(cwd)
}

const projectPromises: Promise<() => Promise<WorkspaceProject>>[] = []
const projectPromises: Promise<WorkspaceProject>[] = []

projectsOptions.forEach((options, index) => {
// we can resolve these in parallel because process.cwd() is not changed
Expand All @@ -349,14 +348,10 @@ export class Vitest {
if (!projects.length && !projectPromises.length)
return [await this.createCoreProject()]

const resolvedProjectsReceivers = [
const resolvedProjects = await Promise.all([
...projects,
...await Promise.all(projectPromises),
]
// we need to wait when the server is resolved, we can do that in parallel
const resolvedProjects = await Promise.all(
resolvedProjectsReceivers.map(receiver => receiver()),
)
])
const names = new Set<string>()

for (const project of resolvedProjects) {
Expand Down
47 changes: 14 additions & 33 deletions packages/vitest/src/node/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { deepMerge } from '../utils'
import type { Typechecker } from '../typecheck/typechecker'
import type { BrowserProvider } from '../types/browser'
import { getBrowserProvider } from '../integrations/browser'
import { createDefer } from '../public/utils'
import { isBrowserEnabled, resolveConfig } from './config'
import { WorkspaceVitestPlugin } from './plugins/workspace'
import { createViteServer } from './vite'
Expand Down Expand Up @@ -40,40 +39,22 @@ export async function initializeProject(workspacePath: string | number, ctx: Vit
: workspacePath.endsWith('/') ? workspacePath : dirname(workspacePath)
)

return new Promise<() => Promise<WorkspaceProject>>((resolve, reject) => {
const resolution = createDefer<WorkspaceProject>()
let configResolved = false
const config: ViteInlineConfig = {
...options,
root,
logLevel: 'error',
configFile,
// this will make "mode": "test" | "benchmark" inside defineConfig
mode: options.test?.mode || options.mode || ctx.config.mode,
plugins: [
{
name: 'vitest:workspace:resolve',
configResolved() {
configResolved = true
resolve(() => resolution)
},
},
...options.plugins || [],
WorkspaceVitestPlugin(project, { ...options, root, workspacePath }),
],
}
const config: ViteInlineConfig = {
...options,
root,
logLevel: 'error',
configFile,
// this will make "mode": "test" | "benchmark" inside defineConfig
mode: options.test?.mode || options.mode || ctx.config.mode,
plugins: [
...options.plugins || [],
WorkspaceVitestPlugin(project, { ...options, root, workspacePath }),
],
}

createViteServer(config)
.then(() => resolution.resolve(project))
.catch((err) => {
if (configResolved)
resolution.reject(err)
else
reject(err)
})
await createViteServer(config)

return project
})
return project
}

export class WorkspaceProject {
Expand Down
3 changes: 3 additions & 0 deletions test/workspaces/cwdPlugin.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { Plugin } from 'vite'

export function cwdPlugin(name: string): Plugin
13 changes: 13 additions & 0 deletions test/workspaces/cwdPlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @ts-check

export function cwdPlugin(name) {
return {
name: `vitest:test:workspace-${name}`,
configResolved() {
process.env[`${name}_CWD_CONFIG`] = process.cwd()
},
configureServer() {
process.env[`${name}_CWD_SERVER`] = process.cwd()
},
}
}
4 changes: 2 additions & 2 deletions test/workspaces/globalTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export async function teardown() {
try {
assert.ok(results.success)
assert.equal(results.numTotalTestSuites, 28)
assert.equal(results.numTotalTests, 30)
assert.equal(results.numPassedTests, 30)
assert.equal(results.numTotalTests, 31)
assert.equal(results.numPassedTests, 31)

const shared = results.testResults.filter((r: any) => r.name.includes('space_shared/test.spec.ts'))

Expand Down
11 changes: 11 additions & 0 deletions test/workspaces/space_1/test/env-injected.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { resolve } from 'node:path'
import { expect, test } from 'vitest'

declare global {
Expand All @@ -23,3 +24,13 @@ test('env variable is assigned', () => {
expect(process.env.CONFIG_LOCAL).toBe('local')
expect(process.env.CONFIG_OVERRIDE).toBe('local')
})

test('cwd is resolved correctly', () => {
const spaceRoot = resolve(import.meta.dirname, '..')
const rootPath = resolve(spaceRoot, '..')

expect(process.env.ROOT_CWD_CONFIG).toBe(rootPath)
expect(process.env.ROOT_CWD_SERVER).toBe(rootPath)
expect(process.env.SPACE_2_CWD_CONFIG).toBe(spaceRoot)
expect(process.env.SPACE_2_CWD_SERVER).toBe(spaceRoot)
})
2 changes: 2 additions & 0 deletions test/workspaces/space_1/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { defineProject } from 'vitest/config'
import { cwdPlugin } from '../cwdPlugin'

export default defineProject({
envPrefix: ['VITE_', 'CUSTOM_'],
plugins: [cwdPlugin('SPACE_2')],
define: {
__DEV__: 'true',
},
Expand Down
2 changes: 2 additions & 0 deletions test/workspaces/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { defineConfig } from 'vitest/config'
import { cwdPlugin } from './cwdPlugin.js'

if (process.env.TEST_WATCH) {
// Patch stdin on the process so that we can fake it to seem like a real interactive terminal and pass the TTY checks
Expand All @@ -8,6 +9,7 @@ if (process.env.TEST_WATCH) {

export default defineConfig({
envPrefix: ['VITE_', 'CUSTOM_', 'ROOT_'],
plugins: [cwdPlugin('ROOT')],
test: {
coverage: {
enabled: true,
Expand Down

0 comments on commit ec9d7c9

Please sign in to comment.