Skip to content

Commit 9e50bbb

Browse files
authored
feat: support inline project config (#522)
1 parent b1158c7 commit 9e50bbb

File tree

8 files changed

+204
-13
lines changed

8 files changed

+204
-13
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { defineConfig } from '@rstest/core';
2+
3+
export default defineConfig({
4+
projects: [
5+
{
6+
root: 'packages/node',
7+
globals: true,
8+
},
9+
{
10+
root: 'packages/client',
11+
globals: true,
12+
include: ['test/index.test.ts'],
13+
},
14+
],
15+
});

e2e/projects/inline.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { dirname, join } from 'node:path';
2+
import { fileURLToPath } from 'node:url';
3+
import { describe, expect, it } from '@rstest/core';
4+
import { runRstestCli } from '../scripts';
5+
6+
const __filename = fileURLToPath(import.meta.url);
7+
const __dirname = dirname(__filename);
8+
9+
describe('test projects inline projectConfig', () => {
10+
it('should run projects correctly with inline projectConfig', async () => {
11+
const { cli, expectExecSuccess } = await runRstestCli({
12+
command: 'rstest',
13+
args: ['run', '-c', 'rstest.inline.config.ts'],
14+
options: {
15+
nodeOptions: {
16+
cwd: join(__dirname, 'fixtures'),
17+
},
18+
},
19+
});
20+
21+
await expectExecSuccess();
22+
const logs = cli.stdout.split('\n').filter(Boolean);
23+
24+
// test log print
25+
expect(
26+
logs.find((log) => log.includes('packages/node/test/index.test.ts')),
27+
).toBeTruthy();
28+
expect(
29+
logs.find((log) => log.includes('packages/client/test/index.test.ts')),
30+
).toBeTruthy();
31+
expect(
32+
logs.find((log) => log.includes('packages/client/test/App.test.tsx')),
33+
).toBeFalsy();
34+
});
35+
});

packages/core/src/cli/init.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,27 @@ export async function resolveProjects({
135135
return glob(patterns, globOptions);
136136
};
137137

138-
const { projectPaths, projectPatterns } = (config.projects || []).reduce(
138+
const formatRootStr = (rootStr: string) => {
139+
return rootStr.replace('<rootDir>', root);
140+
};
141+
142+
const { projectPaths, projectPatterns, projectConfigs } = (
143+
config.projects || []
144+
).reduce(
139145
(total, p) => {
140-
const projectStr = p.replace('<rootDir>', root);
146+
if (typeof p === 'object') {
147+
const projectRoot = p.root ? formatRootStr(p.root) : root;
148+
total.projectConfigs.push({
149+
config: {
150+
root: projectRoot,
151+
name: p.name ? p.name : getDefaultProjectName(projectRoot),
152+
...p,
153+
},
154+
configFilePath: undefined,
155+
});
156+
return total;
157+
}
158+
const projectStr = formatRootStr(p);
141159

142160
if (isDynamicPattern(projectStr)) {
143161
total.projectPatterns.push(projectStr);
@@ -154,6 +172,10 @@ export async function resolveProjects({
154172
{
155173
projectPaths: [] as string[],
156174
projectPatterns: [] as string[],
175+
projectConfigs: [] as {
176+
config: RstestConfig;
177+
configFilePath: string | undefined;
178+
}[],
157179
},
158180
);
159181

@@ -181,7 +203,9 @@ export async function resolveProjects({
181203
configFilePath,
182204
};
183205
}),
184-
).then((projects) => filterProjects(projects, options));
206+
).then((projects) =>
207+
filterProjects(projects.concat(projectConfigs), options),
208+
);
185209

186210
if (!projects.length) {
187211
let errorMsg = `No projects found, please make sure you have at least one valid project.

packages/core/src/core/rstest.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { GithubActionsReporter } from '../reporter/githubActions';
66
import { VerboseReporter } from '../reporter/verbose';
77
import type {
88
NormalizedConfig,
9+
NormalizedProjectConfig,
910
Project,
1011
ProjectContext,
1112
Reporter,
@@ -93,7 +94,10 @@ export class Rstest implements RstestContext {
9394
this.projects = projects.length
9495
? projects.map((project) => {
9596
// TODO: support extend projects config
96-
const config = withDefaultConfig(project.config);
97+
const config = withDefaultConfig(
98+
project.config,
99+
) as NormalizedProjectConfig;
100+
config.isolate = rstestConfig.isolate;
97101
return {
98102
configFilePath: project.configFilePath,
99103
rootPath: config.root,

packages/core/src/types/config.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,17 @@ export type RstestPoolOptions = {
1818
execArgv?: string[];
1919
};
2020

21+
export type ProjectConfig = Omit<
22+
RstestConfig,
23+
'projects' | 'reporters' | 'pool' | 'isolate'
24+
>;
25+
2126
/**
2227
* A list of glob patterns or files that match your test projects.
2328
*
2429
* eg. ['packages/*', 'examples/node/rstest.config.ts']
2530
*/
26-
type TestProject = string;
31+
type TestProject = string | ProjectConfig;
2732

2833
export interface RstestConfig {
2934
/**
@@ -235,6 +240,12 @@ type OptionalKeys =
235240
| 'dev'
236241
| 'onConsoleLog';
237242

243+
export type NormalizedProjectConfig = Required<
244+
Omit<RstestConfig, OptionalKeys | 'projects' | 'reporters' | 'pool'>
245+
> & {
246+
[key in OptionalKeys]?: RstestConfig[key];
247+
};
248+
238249
export type NormalizedConfig = Required<
239250
Omit<RstestConfig, OptionalKeys | 'pool' | 'projects'>
240251
> & {

packages/core/src/types/core.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import type { SnapshotManager } from '@vitest/snapshot/manager';
2-
import type { NormalizedConfig, RstestConfig } from './config';
2+
import type {
3+
NormalizedConfig,
4+
NormalizedProjectConfig,
5+
RstestConfig,
6+
} from './config';
37
import type { Reporter } from './reporter';
48

59
export type RstestCommand = 'watch' | 'run' | 'list';
@@ -11,7 +15,7 @@ export type ProjectContext = {
1115
environmentName: string;
1216
rootPath: string;
1317
configFilePath?: string;
14-
normalizedConfig: NormalizedConfig;
18+
normalizedConfig: NormalizedProjectConfig;
1519
};
1620

1721
export type RstestContext = {

website/docs/en/config/test/projects.mdx

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
# projects
22

3-
- **Type:** `string[]`
3+
- **Type:**
4+
5+
```ts
6+
type ProjectConfig = Omit<
7+
RstestConfig,
8+
'projects' | 'reporters' | 'pool' | 'isolate'
9+
>;
10+
11+
type Projects = (string | ProjectConfig)[];
12+
```
13+
414
- **Default:** `[<rootDir>]`
515

6-
An array of directories, config files, or glob patterns that define multiple test projects.
16+
An array of directories, config files, or glob patterns, or an object to define multiple test projects.
717

818
`rstest` will run the tests for each project according to the configuration defined in each project, and the test results from all projects will be combined and displayed.
919

@@ -27,11 +37,26 @@ export default defineConfig({
2737

2838
// A specific project's config file
2939
'./projects/web/rstest.config.ts',
40+
41+
// inline project configs
42+
{
43+
name: 'node',
44+
include: ['tests/node/**/*.{test,spec}.{js,cjs,mjs,ts,tsx}'],
45+
},
46+
{
47+
name: 'react',
48+
include: ['tests/react/**/*.{test,spec}.{js,cjs,mjs,ts,tsx}'],
49+
testEnvironment: 'jsdom',
50+
},
3051
],
3152
});
3253
```
3354

34-
It should be noted that project configuration does not inherit root configuration. If there is shared configuration between your sub-projects, you can extract the shared configuration and import it in the sub-project:
55+
## Configuration notes
56+
57+
- Project configuration does not inherit root configuration. If there is shared configuration between your sub-projects, you can extract the shared configuration and import it in the sub-project.
58+
- Some root-level options such as `reporters`, `pool`, and `isolate` are not valid in a project configuration.
59+
- `projects` does not support nesting.
3560

3661
```ts title='packages/pkg-a/rstest.config.ts'
3762
import { defineConfig } from '@rstest/core';
@@ -41,3 +66,26 @@ export default defineConfig({
4166
...sharedConfig,
4267
});
4368
```
69+
70+
## Inline configuration
71+
72+
`rstest` supports configuring projects inline in the `projects` field. This lets you define multiple test projects in a single root without creating separate config files for each test project.
73+
74+
```ts name='rstest.config.ts'
75+
import { defineConfig } from '@rstest/core';
76+
77+
export default defineConfig({
78+
projects: [
79+
// inline project configs
80+
{
81+
name: 'node',
82+
include: ['tests/node/**/*.{test,spec}.{js,cjs,mjs,ts,tsx}'],
83+
},
84+
{
85+
name: 'react',
86+
include: ['tests/react/**/*.{test,spec}.{js,cjs,mjs,ts,tsx}'],
87+
testEnvironment: 'jsdom',
88+
},
89+
],
90+
});
91+
```

website/docs/zh/config/test/projects.mdx

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
# projects
22

3-
- **类型:** `string[]`
3+
- **类型:**
4+
5+
```ts
6+
type ProjectConfig = Omit<
7+
RstestConfig,
8+
'projects' | 'reporters' | 'pool' | 'isolate'
9+
>;
10+
11+
type Projects = (string | ProjectConfig)[];
12+
```
13+
414
- **默认值:** `[<rootDir>]`
515

6-
一组目录、配置文件或 glob 模式,用于定义多个测试项目。 `rstest` 将会按照各个项目定义的配置运行对应的测试,所有项目的测试结果将会合并展示。
16+
可以是一个目录、配置文件或 glob 模式,也可以是一个对象,用于定义多个测试项目。 `rstest` 将会按照各个项目定义的配置运行对应的测试,所有项目的测试结果将会合并展示。
717

818
你可以通过 [--project](/guide/basic/test-filter#根据项目名称过滤) 选项来过滤运行特定项目。
919

@@ -25,11 +35,28 @@ export default defineConfig({
2535

2636
// A specific project's config file
2737
'./projects/web/rstest.config.ts',
38+
39+
// inline project configs
40+
{
41+
name: 'node',
42+
include: ['tests/node/**/*.{test,spec}.{js,cjs,mjs,ts,tsx}'],
43+
},
44+
{
45+
name: 'react',
46+
include: ['tests/react/**/*.{test,spec}.{js,cjs,mjs,ts,tsx}'],
47+
testEnvironment: 'jsdom',
48+
},
2849
],
2950
});
3051
```
3152

32-
需要注意的是,project 配置并不会继承全局配置,如果你的子项目间存在共享配置,可以抽取 shared 配置,并在子项目中引入:
53+
## 配置说明
54+
55+
需要注意的是:
56+
57+
- project 配置并不会继承全局配置,如果你的子项目间存在共享配置,可以抽取 shared 配置,并在子项目中引入
58+
- 一些全局配置,如 `reporters``pool``isolate` 等,在 project 配置中是无效的
59+
- 不支持嵌套的 `projects`
3360

3461
```ts title='packages/pkg-a/rstest.config.ts'
3562
import { defineConfig } from '@rstest/core';
@@ -39,3 +66,26 @@ export default defineConfig({
3966
...sharedConfig,
4067
});
4168
```
69+
70+
## 内联配置
71+
72+
Rstest 支持在 `projects` 字段中直接通过内联的方式配置 project。这允许你在单个项目下定义多个测试项目,而无需为每个测试项目创建单独的配置文件。
73+
74+
```ts name='rstest.config.ts'
75+
import { defineConfig } from '@rstest/core';
76+
77+
export default defineConfig({
78+
projects: [
79+
// inline project configs
80+
{
81+
name: 'node',
82+
include: ['tests/node/**/*.{test,spec}.{js,cjs,mjs,ts,tsx}'],
83+
},
84+
{
85+
name: 'react',
86+
include: ['tests/react/**/*.{test,spec}.{js,cjs,mjs,ts,tsx}'],
87+
testEnvironment: 'jsdom',
88+
},
89+
],
90+
});
91+
```

0 commit comments

Comments
 (0)