Skip to content

Commit 6bbaea2

Browse files
chareicedream2023
andauthored
feat: run vitest with coverage (nocobase#3802)
* chore: vitest coverage * chore: vitest coverage config * chore: vitest coverage config * chore: vitest coverage config * chore: report storage dir * chore: exclude lib dir * chore: coverage report dir * fix: client and server coverage * fix: bug * fix: coverage storage dir --------- Co-authored-by: dream2023 <[email protected]>
1 parent 19c6695 commit 6bbaea2

21 files changed

+488
-80
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ tsconfig.paths.json
4242
ncc-cache/
4343
yarn--**
4444
v8-compile-cache-**
45+
vitest.config.mts.timestamp-*

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
"tar": "nocobase tar",
2727
"test": "nocobase test",
2828
"test:server": "nocobase test:server",
29+
"test:server-coverage": "nocobase test-coverage:server",
2930
"test:client": "nocobase test:client",
31+
"test:client-coverage": "nocobase test-coverage:client",
3032
"e2e": "nocobase e2e",
3133
"ts": "nocobase test:server",
3234
"tc": "nocobase test:client",

packages/core/cli/src/commands/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module.exports = (cli) => {
1818
require('./doc')(cli);
1919
require('./pm2')(cli);
2020
require('./test')(cli);
21+
require('./test-coverage')(cli);
2122
require('./umi')(cli);
2223
require('./upgrade')(cli);
2324
require('./postinstall')(cli);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const { run } = require('../util');
2+
const fg = require('fast-glob');
3+
4+
const coreClientPackages = ['packages/core/client', 'packages/core/sdk'];
5+
const isCore = (dir) => dir.startsWith('packages/core');
6+
7+
const getPackagesDir = (isClient) => {
8+
if (process.argv.length > 3 && !process.argv[3].startsWith('-')) {
9+
return [process.argv[3]];
10+
}
11+
12+
const allPackageJson = fg.sync(['packages/*/*/package.json', 'packages/*/*/*/package.json'], {
13+
cwd: process.cwd(),
14+
onlyFiles: true,
15+
});
16+
const res = allPackageJson.map((pkg) => pkg.replace('/package.json', ''));
17+
return isClient
18+
? res.filter((dir) => (isCore(dir) ? coreClientPackages.includes(dir) : true))
19+
: res.filter((dir) => (isCore(dir) ? !coreClientPackages.includes(dir) : true));
20+
};
21+
22+
module.exports = (cli) => {
23+
cli.command('test-coverage:server').action(async () => {
24+
const packageRoots = getPackagesDir(false);
25+
for (const dir of packageRoots) {
26+
try {
27+
await run('yarn', ['test:server', '--coverage', dir]);
28+
} catch (e) {
29+
continue;
30+
}
31+
}
32+
});
33+
34+
cli.command('test-coverage:client').action(async () => {
35+
const packageRoots = getPackagesDir(true);
36+
for (const dir of packageRoots) {
37+
try {
38+
await run('yarn', ['test:client', '--coverage', dir]);
39+
} catch (e) {
40+
continue;
41+
}
42+
}
43+
});
44+
};

packages/core/cli/src/commands/test.js

+10
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ function addTestCommand(name, cli) {
1414
.option('--run')
1515
.option('--allowOnly')
1616
.option('--bail')
17+
.option('--coverage')
1718
.option('-h, --help')
1819
.option('--single-thread [singleThread]')
1920
.arguments('[paths...]')
@@ -29,14 +30,18 @@ function addTestCommand(name, cli) {
2930
process.env.TEST_ENV = 'server-side';
3031
process.argv.splice(process.argv.indexOf('--server'), 1);
3132
}
33+
3234
if (opts.client) {
3335
process.env.TEST_ENV = 'client-side';
3436
process.argv.splice(process.argv.indexOf('--client'), 1);
3537
}
38+
3639
process.env.NODE_ENV = 'test';
40+
3741
if (!opts.watch && !opts.run) {
3842
process.argv.push('--run');
3943
}
44+
4045
const first = paths?.[0];
4146
if (!process.env.TEST_ENV && first) {
4247
const key = first.split(path.sep).join('/');
@@ -46,17 +51,22 @@ function addTestCommand(name, cli) {
4651
process.env.TEST_ENV = 'server-side';
4752
}
4853
}
54+
4955
if (process.env.TEST_ENV === 'server-side' && opts.singleThread !== 'false') {
5056
process.argv.push('--poolOptions.threads.singleThread=true');
5157
}
58+
5259
if (opts.singleThread === 'false') {
5360
process.argv.splice(process.argv.indexOf('--single-thread=false'), 1);
5461
}
62+
5563
const cliArgs = ['--max_old_space_size=14096', './node_modules/.bin/vitest', ...process.argv.slice(3)];
64+
5665
if (process.argv.includes('-h') || process.argv.includes('--help')) {
5766
await run('node', cliArgs);
5867
return;
5968
}
69+
6070
if (process.env.TEST_ENV) {
6171
console.log('process.env.TEST_ENV', process.env.TEST_ENV, cliArgs);
6272
await run('node', cliArgs);

packages/core/test/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@
6060
"supertest": "^6.1.6",
6161
"vite": "^5.0.0",
6262
"vitest": "^1.4.0",
63+
"@vitest/coverage-v8": "^1.4.0",
64+
"@vitest/coverage-istanbul": "^1.4.0",
65+
"vitest-dom": "^0.1.1",
6366
"ws": "^8.13.0"
6467
},
6568
"gitHead": "d0b4efe4be55f8c79a98a331d99d9f8cf99021a1"

packages/core/test/vitest.mjs

+158-71
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
/// <reference types="vitest" />
2+
13
import react from '@vitejs/plugin-react';
24
import fs from 'fs';
35
import path, { resolve } from 'path';
46
import { URL } from 'url';
5-
import { defineConfig as vitestConfig } from 'vitest/config';
7+
import { mergeConfig, defineConfig as vitestConfig } from 'vitest/config';
8+
9+
const CORE_CLIENT_PACKAGES = ['sdk', 'client'];
610

711
const __dirname = new URL('.', import.meta.url).pathname;
812

@@ -44,75 +48,158 @@ function tsConfigPathsToAlias() {
4448
];
4549
}
4650

47-
export const defineConfig = (config = {}) => {
48-
return vitestConfig(
49-
process.env.TEST_ENV === 'server-side'
50-
? {
51-
root: process.cwd(),
52-
resolve: {
53-
mainFields: ['module'],
54-
},
55-
test: {
56-
globals: true,
57-
setupFiles: resolve(__dirname, './setup/server.ts'),
58-
alias: tsConfigPathsToAlias(),
59-
include: ['packages/**/__tests__/**/*.test.ts'],
60-
exclude: [
61-
'**/node_modules/**',
62-
'**/dist/**',
63-
'**/lib/**',
64-
'**/es/**',
65-
'**/e2e/**',
66-
'**/__e2e__/**',
67-
'**/{vitest,commitlint}.config.*',
68-
'packages/**/{dumi-theme-nocobase,sdk,client}/**/__tests__/**/*.{test,spec}.{ts,tsx}',
69-
],
70-
testTimeout: 300000,
71-
hookTimeout: 300000,
72-
// bail: 1,
73-
// 在 GitHub Actions 中不输出日志
74-
silent: !!process.env.GITHUB_ACTIONS,
75-
// poolOptions: {
76-
// threads: {
77-
// singleThread: process.env.SINGLE_THREAD == 'false' ? false : true,
78-
// },
79-
// },
80-
},
81-
}
82-
: {
83-
plugins: [react()],
84-
resolve: {
85-
mainFields: ['module'],
86-
},
87-
define: {
88-
'process.env.__TEST__': true,
89-
'process.env.__E2E__': false,
90-
},
91-
test: {
92-
globals: true,
93-
setupFiles: resolve(__dirname, './setup/client.ts'),
94-
environment: 'jsdom',
95-
css: false,
96-
alias: tsConfigPathsToAlias(),
97-
include: ['packages/**/{dumi-theme-nocobase,sdk,client}/**/__tests__/**/*.{test,spec}.{ts,tsx}'],
98-
exclude: [
99-
'**/node_modules/**',
100-
'**/dist/**',
101-
'**/lib/**',
102-
'**/es/**',
103-
'**/e2e/**',
104-
'**/__e2e__/**',
105-
'**/{vitest,commitlint}.config.*',
106-
],
107-
testTimeout: 300000,
108-
// 在 GitHub Actions 中不输出日志
109-
silent: !!process.env.GITHUB_ACTIONS,
110-
server: {
111-
deps: {
112-
inline: ['@juggle/resize-observer', 'clsx'],
113-
},
114-
},
115-
},
51+
const defineCommonConfig = () => {
52+
return vitestConfig({
53+
root: process.cwd(),
54+
resolve: {
55+
mainFields: ['module'],
56+
},
57+
test: {
58+
globals: true,
59+
alias: tsConfigPathsToAlias(),
60+
testTimeout: 300000,
61+
hookTimeout: 300000,
62+
silent: !!process.env.GITHUB_ACTIONS,
63+
include: [
64+
'packages/**/src/**/__tests__/**/*.test.ts',
65+
],
66+
exclude: [
67+
'**/node_modules/**',
68+
'**/dist/**',
69+
'**/lib/**',
70+
'**/es/**',
71+
'**/.dumi/**',
72+
'**/e2e/**',
73+
'**/__e2e__/**',
74+
'**/{vitest,commitlint}.config.*',
75+
],
76+
watchExclude: [
77+
'**/node_modules/**',
78+
'**/dist/**',
79+
'**/lib/**',
80+
'**/es/**',
81+
'**/.dumi/**',
82+
'**/e2e/**',
83+
'**/__e2e__/**',
84+
'**/{vitest,commitlint}.config.*',
85+
],
86+
coverage: {
87+
provider: 'istanbul',
88+
include: [
89+
'packages/**/src/**/*.{ts,tsx}',
90+
],
91+
exclude: ['**/swagger/**', '**/.dumi/**', '**/.umi/**', '**/.plugins/**', '**/lib/**', '**/__tests__/**', '**/e2e/**', '**/client.js', '**/server.js', '**/*.d.ts']
92+
}
93+
}
94+
})
95+
}
96+
97+
function getExclude(isServer) {
98+
return [
99+
`packages/core/${isServer ? '' : '!'}(${CORE_CLIENT_PACKAGES.join('|')})/**/*`,
100+
`packages/**/src/${isServer ? 'client' : 'server'}/**/*`,
101+
]
102+
}
103+
104+
const defineServerConfig = () => {
105+
return vitestConfig({
106+
test: {
107+
setupFiles: resolve(__dirname, './setup/server.ts'),
108+
exclude: getExclude(true)
109+
},
110+
coverage: {
111+
exclude: getExclude(true)
112+
}
113+
})
114+
}
115+
116+
const defineClientConfig = () => {
117+
return vitestConfig({
118+
plugins: [react()],
119+
define: {
120+
'process.env.__TEST__': true,
121+
'process.env.__E2E__': false,
122+
},
123+
test: {
124+
environment: 'jsdom',
125+
css: false,
126+
setupFiles: resolve(__dirname, './setup/client.ts'),
127+
server: {
128+
deps: {
129+
inline: ['@juggle/resize-observer', 'clsx'],
116130
},
117-
);
131+
},
132+
exclude: getExclude(false),
133+
coverage: {
134+
exclude: getExclude(false)
135+
}
136+
}
137+
})
138+
}
139+
140+
export const getFilterInclude = (isServer, isCoverage) => {
141+
let filterFileOrDir = process.argv.slice(2).find(arg => !arg.startsWith('-'));
142+
if (!filterFileOrDir) return;
143+
const absPath = path.join(process.cwd(), filterFileOrDir);
144+
const isDir = fs.existsSync(absPath) && fs.statSync(absPath).isDirectory();
145+
// 如果是文件,则只测试当前文件
146+
if (!isDir) {
147+
return [filterFileOrDir];
148+
}
149+
150+
const suffix = isCoverage ? `**/*.{ts,tsx}` : `**/__tests__/**/*.{test,spec}.{ts,tsx}`
151+
152+
// 判断是否为包目录,如果不是包目录,则只测试当前目录
153+
const isPackage = fs.existsSync(path.join(absPath, 'package.json'));
154+
if (!isPackage) {
155+
return [`${filterFileOrDir}/${suffix}`];
156+
}
157+
158+
// 判断是否为 core 包目录,不分 client 和 server
159+
const isCore = absPath.includes('packages/core');
160+
if (isCore) {
161+
return [`${filterFileOrDir}/src/${suffix}`];
162+
}
163+
164+
// 插件目录,区分 client 和 server
165+
return [`${filterFileOrDir}/src/${isServer ? 'server' : 'client'}/${suffix}`];
166+
}
167+
168+
export const getReportsDirectory = (isServer) => {
169+
let filterFileOrDir = process.argv.slice(2).find(arg => !arg.startsWith('-'));
170+
if (!filterFileOrDir) return;
171+
const isPackage = fs.existsSync(path.join(process.cwd(), filterFileOrDir, 'package.json'));
172+
if (isPackage) {
173+
let reportsDirectory = `./storage/coverage/${filterFileOrDir.replace('packages/', '')}`;
174+
175+
const isCore = filterFileOrDir.includes('packages/core');
176+
177+
if (!isCore) {
178+
reportsDirectory = `${reportsDirectory}/${isServer ? 'server' : 'client'}`;
179+
}
180+
181+
return reportsDirectory;
182+
}
183+
}
184+
185+
export const defineConfig = () => {
186+
const isServer = process.env.TEST_ENV === 'server-side';
187+
const config = vitestConfig(mergeConfig(defineCommonConfig(), isServer ? defineServerConfig() : defineClientConfig()));
188+
189+
const filterInclude = getFilterInclude(isServer);
190+
if (filterInclude) {
191+
config.test.include = getFilterInclude(isServer);
192+
}
193+
const isCoverage = process.argv.includes('--coverage');
194+
if (!isCoverage) {
195+
return config;
196+
}
197+
198+
config.test.coverage.include = getFilterInclude(isServer, true);
199+
const reportsDirectory = getReportsDirectory(isServer);
200+
if (reportsDirectory) {
201+
config.test.coverage.reportsDirectory = reportsDirectory;
202+
}
203+
204+
return config;
118205
};

0 commit comments

Comments
 (0)