Skip to content

Commit 67d93ed

Browse files
sheremet-vaAriPerkkio
andauthoredDec 19, 2023
feat: add --workspace option, fix root resolution in workspaces (#4773)
Co-authored-by: Ari Perkkiö <ari.perkkio@gmail.com>
1 parent 8dabef8 commit 67d93ed

File tree

13 files changed

+87
-36
lines changed

13 files changed

+87
-36
lines changed
 

‎docs/config/index.md

+9
Original file line numberDiff line numberDiff line change
@@ -2028,3 +2028,12 @@ Relevant only when using with `shouldAdvanceTime: true`. increment mocked time b
20282028
- **Default:** `false`
20292029

20302030
Tells fake timers to clear "native" (i.e. not fake) timers by delegating to their respective handlers. These are not cleared by default, leading to potentially unexpected behavior if timers existed prior to starting fake timers session.
2031+
2032+
### workspace
2033+
2034+
- **Type:** `string`
2035+
- **CLI:** `--workspace=./file.js`
2036+
- **Default:** `vitest.{workspace,projects}.{js,ts,json}` close to the config file or root
2037+
- **Version:** Since Vitest 1.1.0
2038+
2039+
Path to a [workspace](/guide/workspace) config file relative to [root](#root).

‎docs/guide/cli.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ Run only [benchmark](https://vitest.dev/guide/features.html#benchmarking-experim
7878
| `--outputFile <filename/-s>` | Write test results to a file when the `--reporter=json` or `--reporter=junit` option is also specified <br /> Via [cac's dot notation] you can specify individual outputs for multiple reporters |
7979
| `--coverage` | Enable coverage report |
8080
| `--run` | Do not watch |
81-
| `--mode` | Override Vite mode (default: `test`) |
8281
| `--mode <name>` | Override Vite mode (default: `test`) |
82+
| `--workspace <path>` | Path to a workspace configuration file |
8383
| `--globals` | Inject APIs globally |
8484
| `--dom` | Mock browser API with happy-dom |
8585
| `--browser [options]` | Run tests in [the browser](/guide/browser) (default: `false`) |

‎packages/vitest/src/node/cli.ts

+6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ cli
2929
.option('--coverage.all', 'Whether to include all files, including the untested ones into report', { default: true })
3030
.option('--run', 'Disable watch mode')
3131
.option('--mode <name>', 'Override Vite mode (default: test)')
32+
.option('--workspace <path>', 'Path to a workspace configuration file')
3233
.option('--globals', 'Inject apis globally')
3334
.option('--dom', 'Mock browser API with happy-dom')
3435
.option('--browser [options]', 'Run tests in the browser (default: false)')
@@ -150,6 +151,11 @@ function normalizeCliOptions(argv: CliOptions): CliOptions {
150151
else
151152
delete argv.config
152153

154+
if (argv.workspace)
155+
argv.workspace = normalize(argv.workspace)
156+
else
157+
delete argv.workspace
158+
153159
if (argv.dir)
154160
argv.dir = normalize(argv.dir)
155161
else

‎packages/vitest/src/node/config.ts

+22-27
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ const extraInlineDeps = [
2222
'@nuxt/test-utils',
2323
]
2424

25+
function resolvePath(path: string, root: string) {
26+
return normalize(
27+
resolveModule(path, { paths: [root] })
28+
?? resolve(root, path),
29+
)
30+
}
31+
2532
export function resolveApiServerConfig<Options extends ApiConfig & UserConfig>(
2633
options: Options,
2734
): ApiConfig | undefined {
@@ -193,10 +200,8 @@ export function resolveConfig(
193200
resolved.server.deps.moduleDirectories ??= []
194201
resolved.server.deps.moduleDirectories.push(...resolved.deps.moduleDirectories)
195202

196-
if (resolved.runner) {
197-
resolved.runner = resolveModule(resolved.runner, { paths: [resolved.root] })
198-
?? resolve(resolved.root, resolved.runner)
199-
}
203+
if (resolved.runner)
204+
resolved.runner = resolvePath(resolved.runner, resolved.root)
200205

201206
resolved.testNamePattern = resolved.testNamePattern
202207
? resolved.testNamePattern instanceof RegExp
@@ -274,19 +279,18 @@ export function resolveConfig(
274279
}
275280
}
276281

277-
if (!builtinPools.includes(resolved.pool as BuiltinPool)) {
278-
resolved.pool = normalize(
279-
resolveModule(resolved.pool, { paths: [resolved.root] })
280-
?? resolve(resolved.root, resolved.pool),
281-
)
282+
if (resolved.workspace) {
283+
// if passed down from the CLI and it's relative, resolve relative to CWD
284+
resolved.workspace = options.workspace && options.workspace[0] === '.'
285+
? resolve(process.cwd(), options.workspace)
286+
: resolvePath(resolved.workspace, resolved.root)
282287
}
288+
289+
if (!builtinPools.includes(resolved.pool as BuiltinPool))
290+
resolved.pool = resolvePath(resolved.pool, resolved.root)
283291
resolved.poolMatchGlobs = (resolved.poolMatchGlobs || []).map(([glob, pool]) => {
284-
if (!builtinPools.includes(pool as BuiltinPool)) {
285-
pool = normalize(
286-
resolveModule(pool, { paths: [resolved.root] })
287-
?? resolve(resolved.root, pool),
288-
)
289-
}
292+
if (!builtinPools.includes(pool as BuiltinPool))
293+
pool = resolvePath(pool, resolved.root)
290294
return [glob, pool]
291295
})
292296

@@ -315,16 +319,10 @@ export function resolveConfig(
315319
}
316320

317321
resolved.setupFiles = toArray(resolved.setupFiles || []).map(file =>
318-
normalize(
319-
resolveModule(file, { paths: [resolved.root] })
320-
?? resolve(resolved.root, file),
321-
),
322+
resolvePath(file, resolved.root),
322323
)
323324
resolved.globalSetup = toArray(resolved.globalSetup || []).map(file =>
324-
normalize(
325-
resolveModule(file, { paths: [resolved.root] })
326-
?? resolve(resolved.root, file),
327-
),
325+
resolvePath(file, resolved.root),
328326
)
329327
resolved.coverage.exclude.push(...resolved.setupFiles.map(file => `${resolved.coverage.allowExternal ? '**/' : ''}${relative(resolved.root, file)}`))
330328

@@ -334,10 +332,7 @@ export function resolveConfig(
334332
]
335333

336334
if (resolved.diff) {
337-
resolved.diff = normalize(
338-
resolveModule(resolved.diff, { paths: [resolved.root] })
339-
?? resolve(resolved.root, resolved.diff),
340-
)
335+
resolved.diff = resolvePath(resolved.diff, resolved.root)
341336
resolved.forceRerunTriggers.push(resolved.diff)
342337
}
343338

‎packages/vitest/src/node/core.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -182,19 +182,31 @@ export class Vitest {
182182
|| this.projects[0]
183183
}
184184

185-
private async resolveWorkspace(cliOptions: UserConfig) {
185+
private async getWorkspaceConfigPath() {
186+
if (this.config.workspace)
187+
return this.config.workspace
188+
186189
const configDir = this.server.config.configFile
187190
? dirname(this.server.config.configFile)
188191
: this.config.root
192+
189193
const rootFiles = await fs.readdir(configDir)
194+
190195
const workspaceConfigName = workspaceFiles.find((configFile) => {
191196
return rootFiles.includes(configFile)
192197
})
193198

194199
if (!workspaceConfigName)
195-
return [await this.createCoreProject()]
200+
return null
196201

197-
const workspaceConfigPath = join(configDir, workspaceConfigName)
202+
return join(configDir, workspaceConfigName)
203+
}
204+
205+
private async resolveWorkspace(cliOptions: UserConfig) {
206+
const workspaceConfigPath = await this.getWorkspaceConfigPath()
207+
208+
if (!workspaceConfigPath)
209+
return [await this.createCoreProject()]
198210

199211
const workspaceModule = await this.runner.executeFile(workspaceConfigPath) as {
200212
default: (string | UserWorkspaceConfig)[]
@@ -244,7 +256,7 @@ export class Vitest {
244256

245257
const workspacesByFolder = resolvedWorkspacesPaths
246258
.reduce((configByFolder, filepath) => {
247-
const dir = dirname(filepath)
259+
const dir = filepath.endsWith('/') ? filepath.slice(0, -1) : dirname(filepath)
248260
configByFolder[dir] ??= []
249261
configByFolder[dir].push(filepath)
250262
return configByFolder

‎packages/vitest/src/node/plugins/workspace.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { dirname, relative } from 'pathe'
1+
import { basename, dirname, relative } from 'pathe'
22
import type { UserConfig as ViteConfig, Plugin as VitePlugin } from 'vite'
33
import { configDefaults } from '../../defaults'
44
import { generateScopedClassName } from '../../integrations/css/css-modules'
@@ -36,7 +36,7 @@ export function WorkspaceVitestPlugin(project: WorkspaceProject, options: Worksp
3636
let name = testConfig.name
3737
if (!name) {
3838
if (typeof options.workspacePath === 'string')
39-
name = dirname(options.workspacePath).split('/').pop()
39+
name = basename(options.workspacePath.endsWith('/') ? options.workspacePath.slice(0, -1) : dirname(options.workspacePath))
4040
else
4141
name = options.workspacePath.toString()
4242
}

‎packages/vitest/src/node/workspace.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ export async function initializeProject(workspacePath: string | number, ctx: Vit
3333
? false
3434
: workspacePath
3535

36-
const root = options.root || (typeof workspacePath === 'number' ? undefined : dirname(workspacePath))
36+
const root = options.root || (
37+
typeof workspacePath === 'number'
38+
? undefined
39+
: workspacePath.endsWith('/') ? workspacePath : dirname(workspacePath)
40+
)
3741

3842
const config: ViteInlineConfig = {
3943
...options,

‎packages/vitest/src/types/config.ts

+5
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,11 @@ export interface InlineConfig {
309309
*/
310310
poolMatchGlobs?: [string, Exclude<Pool, 'browser'>][]
311311

312+
/**
313+
* Path to a workspace configuration file
314+
*/
315+
workspace?: string
316+
312317
/**
313318
* Update snapshot
314319
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default ['project-1']
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { expect, it } from 'vitest';
2+
3+
it('1 + 1 = 2', () => {
4+
expect(1 + 1).toBe(2);
5+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { expect, it } from 'vitest';
2+
3+
it('2 + 2 = 4', () => {
4+
expect(2 + 2).toBe(4);
5+
})

‎test/config/test/workspace.test.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { expect, it } from 'vitest'
2+
import { runVitestCli } from '../../test-utils'
3+
4+
it('correctly runs workspace tests when workspace config path is specified', async () => {
5+
const { stderr, stdout } = await runVitestCli('run', '--root', 'fixtures/workspace', '--workspace', './nested/e2e.projects.js')
6+
expect(stderr).toBe('')
7+
expect(stdout).toContain('1 + 1 = 2')
8+
expect(stdout).not.toContain('2 + 2 = 4')
9+
})

‎test/workspaces/vitest.workspace.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import remapping from '@ampproject/remapping'
44
import type { Plugin } from 'vite'
55

66
export default defineWorkspace([
7-
'./space_2/*',
7+
'space_2',
88
'./space_*/*.config.ts',
99
{
1010
test: {

0 commit comments

Comments
 (0)
Please sign in to comment.