Skip to content

Commit a820b13

Browse files
committed
fix(vitest): add migration to prefix reportsDirectory with {projectRoot}
The reportsDirectory option is now resolved relative to the workspace root instead of the project root. This migration prepends {projectRoot}/ to existing reportsDirectory values so the resolved path remains the same.
1 parent ee7d965 commit a820b13

File tree

4 files changed

+475
-0
lines changed

4 files changed

+475
-0
lines changed

packages/vitest/migrations.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
},
1818
"description": "Create AI Instructions to help migrate users workspaces past breaking changes for Vitest 4.",
1919
"implementation": "./src/migrations/update-22-1-0/create-ai-instructions-for-vitest-4"
20+
},
21+
"update-22-6-0-prefix-reports-directory": {
22+
"version": "22.6.0-beta.0",
23+
"description": "Prefix reportsDirectory with {projectRoot} to maintain correct resolution after workspace-root-relative behavior change.",
24+
"implementation": "./src/migrations/update-22-6-0/prefix-reports-directory-with-project-root"
2025
}
2126
},
2227
"packageJsonUpdates": {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#### Prefix `reportsDirectory` with `{projectRoot}`
2+
3+
The `reportsDirectory` option for `@nx/vitest:test` (and `@nx/vite:test`) is now resolved relative to the workspace root instead of the project root. This migration prepends `{projectRoot}/` to existing `reportsDirectory` values so the resolved path remains the same.
4+
5+
#### Sample Code Changes
6+
7+
##### Before
8+
9+
```json title="project.json"
10+
{
11+
"targets": {
12+
"test": {
13+
"executor": "@nx/vitest:test",
14+
"options": {
15+
"reportsDirectory": "coverage/libs/my-lib"
16+
}
17+
}
18+
}
19+
}
20+
```
21+
22+
##### After
23+
24+
```json title="project.json" {6}
25+
{
26+
"targets": {
27+
"test": {
28+
"executor": "@nx/vitest:test",
29+
"options": {
30+
"reportsDirectory": "{projectRoot}/coverage/libs/my-lib"
31+
}
32+
}
33+
}
34+
}
35+
```
Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
import {
2+
addProjectConfiguration,
3+
readNxJson,
4+
readProjectConfiguration,
5+
type Tree,
6+
updateNxJson,
7+
} from '@nx/devkit';
8+
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
9+
import prefixReportsDirectoryWithProjectRoot from './prefix-reports-directory-with-project-root';
10+
11+
describe('prefix-reports-directory-with-project-root', () => {
12+
let tree: Tree;
13+
14+
beforeEach(() => {
15+
tree = createTreeWithEmptyWorkspace();
16+
});
17+
18+
describe('project configuration migration', () => {
19+
it('should prepend {projectRoot}/ to reportsDirectory for @nx/vitest:test', () => {
20+
addProjectConfiguration(tree, 'my-lib', {
21+
root: 'libs/my-lib',
22+
targets: {
23+
test: {
24+
executor: '@nx/vitest:test',
25+
options: {
26+
reportsDirectory: 'coverage/libs/my-lib',
27+
},
28+
},
29+
},
30+
});
31+
32+
prefixReportsDirectoryWithProjectRoot(tree);
33+
34+
const projectConfig = readProjectConfiguration(tree, 'my-lib');
35+
expect(projectConfig.targets.test.options.reportsDirectory).toBe(
36+
'{projectRoot}/coverage/libs/my-lib'
37+
);
38+
});
39+
40+
it('should prepend {projectRoot}/ to reportsDirectory for @nx/vite:test', () => {
41+
addProjectConfiguration(tree, 'my-lib', {
42+
root: 'libs/my-lib',
43+
targets: {
44+
test: {
45+
executor: '@nx/vite:test',
46+
options: {
47+
reportsDirectory: 'coverage/libs/my-lib',
48+
},
49+
},
50+
},
51+
});
52+
53+
prefixReportsDirectoryWithProjectRoot(tree);
54+
55+
const projectConfig = readProjectConfiguration(tree, 'my-lib');
56+
expect(projectConfig.targets.test.options.reportsDirectory).toBe(
57+
'{projectRoot}/coverage/libs/my-lib'
58+
);
59+
});
60+
61+
it('should migrate reportsDirectory in target configurations', () => {
62+
addProjectConfiguration(tree, 'my-lib', {
63+
root: 'libs/my-lib',
64+
targets: {
65+
test: {
66+
executor: '@nx/vitest:test',
67+
options: {
68+
reportsDirectory: 'coverage/libs/my-lib',
69+
},
70+
configurations: {
71+
ci: {
72+
reportsDirectory: 'reports/ci/my-lib',
73+
},
74+
},
75+
},
76+
},
77+
});
78+
79+
prefixReportsDirectoryWithProjectRoot(tree);
80+
81+
const projectConfig = readProjectConfiguration(tree, 'my-lib');
82+
expect(projectConfig.targets.test.options.reportsDirectory).toBe(
83+
'{projectRoot}/coverage/libs/my-lib'
84+
);
85+
expect(
86+
projectConfig.targets.test.configurations.ci.reportsDirectory
87+
).toBe('{projectRoot}/reports/ci/my-lib');
88+
});
89+
90+
it('should not modify reportsDirectory that already has {projectRoot}', () => {
91+
addProjectConfiguration(tree, 'my-lib', {
92+
root: 'libs/my-lib',
93+
targets: {
94+
test: {
95+
executor: '@nx/vitest:test',
96+
options: {
97+
reportsDirectory: '{projectRoot}/coverage',
98+
},
99+
},
100+
},
101+
});
102+
103+
prefixReportsDirectoryWithProjectRoot(tree);
104+
105+
const projectConfig = readProjectConfiguration(tree, 'my-lib');
106+
expect(projectConfig.targets.test.options.reportsDirectory).toBe(
107+
'{projectRoot}/coverage'
108+
);
109+
});
110+
111+
it('should not modify absolute reportsDirectory paths', () => {
112+
addProjectConfiguration(tree, 'my-lib', {
113+
root: 'libs/my-lib',
114+
targets: {
115+
test: {
116+
executor: '@nx/vitest:test',
117+
options: {
118+
reportsDirectory: '/absolute/path/to/coverage',
119+
},
120+
},
121+
},
122+
});
123+
124+
prefixReportsDirectoryWithProjectRoot(tree);
125+
126+
const projectConfig = readProjectConfiguration(tree, 'my-lib');
127+
expect(projectConfig.targets.test.options.reportsDirectory).toBe(
128+
'/absolute/path/to/coverage'
129+
);
130+
});
131+
132+
it('should not modify projects without reportsDirectory', () => {
133+
addProjectConfiguration(tree, 'my-lib', {
134+
root: 'libs/my-lib',
135+
targets: {
136+
test: {
137+
executor: '@nx/vitest:test',
138+
options: {
139+
configFile: 'libs/my-lib/vite.config.ts',
140+
},
141+
},
142+
},
143+
});
144+
145+
prefixReportsDirectoryWithProjectRoot(tree);
146+
147+
const projectConfig = readProjectConfiguration(tree, 'my-lib');
148+
expect(
149+
projectConfig.targets.test.options.reportsDirectory
150+
).toBeUndefined();
151+
});
152+
153+
it('should not modify other executors', () => {
154+
addProjectConfiguration(tree, 'my-lib', {
155+
root: 'libs/my-lib',
156+
targets: {
157+
test: {
158+
executor: '@nx/jest:jest',
159+
options: {
160+
reportsDirectory: 'coverage/libs/my-lib',
161+
},
162+
},
163+
},
164+
});
165+
166+
prefixReportsDirectoryWithProjectRoot(tree);
167+
168+
const projectConfig = readProjectConfiguration(tree, 'my-lib');
169+
expect(projectConfig.targets.test.options.reportsDirectory).toBe(
170+
'coverage/libs/my-lib'
171+
);
172+
});
173+
174+
it('should migrate multiple projects', () => {
175+
addProjectConfiguration(tree, 'lib-a', {
176+
root: 'libs/lib-a',
177+
targets: {
178+
test: {
179+
executor: '@nx/vitest:test',
180+
options: {
181+
reportsDirectory: 'coverage/libs/lib-a',
182+
},
183+
},
184+
},
185+
});
186+
187+
addProjectConfiguration(tree, 'lib-b', {
188+
root: 'libs/lib-b',
189+
targets: {
190+
test: {
191+
executor: '@nx/vitest:test',
192+
options: {
193+
reportsDirectory: 'coverage/libs/lib-b',
194+
},
195+
},
196+
},
197+
});
198+
199+
prefixReportsDirectoryWithProjectRoot(tree);
200+
201+
expect(
202+
readProjectConfiguration(tree, 'lib-a').targets.test.options
203+
.reportsDirectory
204+
).toBe('{projectRoot}/coverage/libs/lib-a');
205+
expect(
206+
readProjectConfiguration(tree, 'lib-b').targets.test.options
207+
.reportsDirectory
208+
).toBe('{projectRoot}/coverage/libs/lib-b');
209+
});
210+
});
211+
212+
describe('targetDefaults migration', () => {
213+
it('should migrate reportsDirectory in executor-keyed targetDefaults', () => {
214+
const nxJson = readNxJson(tree);
215+
nxJson.targetDefaults = {
216+
'@nx/vitest:test': {
217+
options: {
218+
reportsDirectory: 'coverage',
219+
},
220+
},
221+
};
222+
updateNxJson(tree, nxJson);
223+
224+
prefixReportsDirectoryWithProjectRoot(tree);
225+
226+
const updatedNxJson = readNxJson(tree);
227+
expect(
228+
updatedNxJson.targetDefaults['@nx/vitest:test'].options.reportsDirectory
229+
).toBe('{projectRoot}/coverage');
230+
});
231+
232+
it('should migrate reportsDirectory in target-name-keyed targetDefaults', () => {
233+
const nxJson = readNxJson(tree);
234+
nxJson.targetDefaults = {
235+
test: {
236+
executor: '@nx/vitest:test',
237+
options: {
238+
reportsDirectory: 'coverage',
239+
},
240+
},
241+
};
242+
updateNxJson(tree, nxJson);
243+
244+
prefixReportsDirectoryWithProjectRoot(tree);
245+
246+
const updatedNxJson = readNxJson(tree);
247+
expect(updatedNxJson.targetDefaults.test.options.reportsDirectory).toBe(
248+
'{projectRoot}/coverage'
249+
);
250+
});
251+
252+
it('should not modify targetDefaults for unrelated executors', () => {
253+
const nxJson = readNxJson(tree);
254+
nxJson.targetDefaults = {
255+
'@nx/jest:jest': {
256+
options: {
257+
reportsDirectory: 'coverage',
258+
},
259+
},
260+
};
261+
updateNxJson(tree, nxJson);
262+
263+
prefixReportsDirectoryWithProjectRoot(tree);
264+
265+
const updatedNxJson = readNxJson(tree);
266+
expect(
267+
updatedNxJson.targetDefaults['@nx/jest:jest'].options.reportsDirectory
268+
).toBe('coverage');
269+
});
270+
271+
it('should handle workspace without targetDefaults', () => {
272+
const nxJson = readNxJson(tree);
273+
delete nxJson.targetDefaults;
274+
updateNxJson(tree, nxJson);
275+
276+
// Should not throw
277+
prefixReportsDirectoryWithProjectRoot(tree);
278+
279+
const updatedNxJson = readNxJson(tree);
280+
expect(updatedNxJson.targetDefaults).toBeUndefined();
281+
});
282+
});
283+
284+
describe('no-op scenarios', () => {
285+
it('should handle empty workspace', () => {
286+
// Should not throw
287+
prefixReportsDirectoryWithProjectRoot(tree);
288+
});
289+
});
290+
});

0 commit comments

Comments
 (0)