Skip to content

Commit

Permalink
fix(workspace): set CWD to config directory, allow overriding local .…
Browse files Browse the repository at this point in the history
…env (#5476)
  • Loading branch information
sheremet-va committed Apr 3, 2024
1 parent 1f54834 commit d400388
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 27 deletions.
50 changes: 39 additions & 11 deletions packages/vitest/src/node/core.ts
@@ -1,4 +1,5 @@
import { existsSync, promises as fs } from 'node:fs'
import { isMainThread } from 'node:worker_threads'
import type { ViteDevServer } from 'vite'
import { mergeConfig } from 'vite'
import { basename, dirname, join, normalize, relative, resolve } from 'pathe'
Expand Down Expand Up @@ -312,23 +313,50 @@ export class Vitest {
return acc
}, {} as UserConfig)

const projects = filteredWorkspaces.map(async (workspacePath) => {
// don't start a new server, but reuse existing one
if (
this.server.config.configFile === workspacePath
)
return this.createCoreProject()
return initializeProject(workspacePath, this, { workspaceConfigPath, test: cliOverrides })
})
const cwd = process.cwd()

const projects: (() => Promise<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))
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 }))
}
}
finally {
if (isMainThread)
process.chdir(cwd)
}

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

projectsOptions.forEach((options, index) => {
projects.push(initializeProject(index, this, mergeConfig(options, { workspaceConfigPath, test: cliOverrides }) as any))
// we can resolve these in parallel because process.cwd() is not changed
projectPromises.push(initializeProject(index, this, mergeConfig(options, { workspaceConfigPath, test: cliOverrides }) as any))
})

if (!projects.length)
if (!projects.length && !projectPromises.length)
return [await this.createCoreProject()]

const resolvedProjects = await Promise.all(projects)
const resolvedProjectsReceivers = [
...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
51 changes: 37 additions & 14 deletions packages/vitest/src/node/workspace.ts
Expand Up @@ -12,6 +12,7 @@ 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 @@ -39,22 +40,40 @@ export async function initializeProject(workspacePath: string | number, ctx: Vit
: workspacePath.endsWith('/') ? workspacePath : dirname(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 }),
],
}
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 }),
],
}

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

return project
return project
})
}

export class WorkspaceProject {
Expand Down Expand Up @@ -393,6 +412,10 @@ export class WorkspaceProject {
inspectBrk: this.ctx.config.inspectBrk,
alias: [],
includeTaskLocation: this.config.includeTaskLocation ?? this.ctx.config.includeTaskLocation,
env: {
...this.server?.config.env,
...this.config.env,
},
}, this.ctx.configOverride || {} as any) as ResolvedConfig
}

Expand Down
8 changes: 8 additions & 0 deletions packages/vitest/src/runtime/setup-common.ts
Expand Up @@ -10,6 +10,7 @@ let globalSetup = false
export async function setupCommonEnv(config: ResolvedConfig) {
resetRunOnceCounter()
setupDefines(config.defines)
setupEnv(config.env)

if (globalSetup)
return
Expand All @@ -26,6 +27,13 @@ function setupDefines(defines: Record<string, any>) {
(globalThis as any)[key] = defines[key]
}

function setupEnv(env: Record<string, any>) {
if (typeof process === 'undefined')
return
for (const key in env)
process.env[key] = env[key]
}

export async function loadDiffConfig(config: ResolvedConfig, executor: VitestExecutor) {
if (typeof config.diff !== 'string')
return
Expand Down
4 changes: 4 additions & 0 deletions test/workspaces/.env.local
@@ -0,0 +1,4 @@
VITE_MY_TEST_VARIABLE=core
VITE_CORE_VARIABLE=core
CUSTOM_ROOT=custom
ROOT_VARIABLE=root
4 changes: 2 additions & 2 deletions test/workspaces/globalTest.ts
Expand Up @@ -33,8 +33,8 @@ export async function teardown() {
try {
assert.ok(results.success)
assert.equal(results.numTotalTestSuites, 28)
assert.equal(results.numTotalTests, 29)
assert.equal(results.numPassedTests, 29)
assert.equal(results.numTotalTests, 30)
assert.equal(results.numPassedTests, 30)

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

Expand Down
2 changes: 2 additions & 0 deletions test/workspaces/space_1/.env.local
@@ -0,0 +1,2 @@
VITE_MY_TEST_VARIABLE=local
CUSTOM_MY_TEST_VARIABLE=custom
16 changes: 16 additions & 0 deletions test/workspaces/space_1/test/env-injected.spec.ts
Expand Up @@ -7,3 +7,19 @@ declare global {
test('dev is injected', () => {
expect(__DEV__).toBe(true)
})

test('env variable is assigned', () => {
// we override it with "local" in .env.local, but dotenv prefers the root .env
// this is consistent with how Vite works
expect(import.meta.env.VITE_MY_TEST_VARIABLE).toBe('core')
expect(process.env.VITE_MY_TEST_VARIABLE).toBe('core')
expect(import.meta.env.CUSTOM_MY_TEST_VARIABLE).toBe('custom')
expect(process.env.CUSTOM_MY_TEST_VARIABLE).toBe('custom')

expect(process.env.VITE_CORE_VARIABLE).toBe('core')
expect(process.env.CUSTOM_ROOT).toBe('custom')
expect(process.env.ROOT_VARIABLE).toBe('root')
expect(process.env.CONFIG_VAR).toBe('root')
expect(process.env.CONFIG_LOCAL).toBe('local')
expect(process.env.CONFIG_OVERRIDE).toBe('local')
})
5 changes: 5 additions & 0 deletions test/workspaces/space_1/vite.config.ts
@@ -1,11 +1,16 @@
import { defineProject } from 'vitest/config'

export default defineProject({
envPrefix: ['VITE_', 'CUSTOM_'],
define: {
__DEV__: 'true',
},
test: {
name: 'space_1',
environment: 'happy-dom',
env: {
CONFIG_LOCAL: 'local',
CONFIG_OVERRIDE: 'local',
},
},
})
5 changes: 5 additions & 0 deletions test/workspaces/vitest.config.ts
Expand Up @@ -7,6 +7,7 @@ if (process.env.TEST_WATCH) {
}

export default defineConfig({
envPrefix: ['VITE_', 'CUSTOM_', 'ROOT_'],
test: {
coverage: {
enabled: true,
Expand All @@ -15,5 +16,9 @@ export default defineConfig({
reporters: ['default', 'json'],
outputFile: './results.json',
globalSetup: './globalTest.ts',
env: {
CONFIG_VAR: 'root',
CONFIG_OVERRIDE: 'root',
},
},
})

0 comments on commit d400388

Please sign in to comment.