covgate 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/docs/setup.md +5 -3
- package/docs/ui-result.png +0 -0
- package/docs/ui-viewer.png +0 -0
- package/package.json +1 -1
- package/src/setup/index.js +55 -12
- package/src/ui/page.html +31 -9
- package/src/ui/server.js +32 -7
package/README.md
CHANGED
|
@@ -29,6 +29,7 @@ npx --no-install covgate ui --root D:/Projects/my-app --port 4319 --no-open
|
|
|
29
29
|
- 指定客户端、服务端源码路径,路径有自动检测的建议列表;
|
|
30
30
|
- 从项目 `package.json` 脚本中**下拉选择测试命令**,不需要手敲;
|
|
31
31
|
- 单独指定客户端/服务端命令,自动生成临时配置并运行;
|
|
32
|
+
- 客户端、服务端可以分别设置覆盖率报告路径,适配前后端分开产出的项目;
|
|
32
33
|
- 工具内嵌覆盖率报告:左侧文件列表,中间源码,绿色已覆盖、白色未覆盖;
|
|
33
34
|
- 运行前检查测试文件;没有测试时禁用运行,并提供可复制的 **AI 补测任务**,避免生成空报告;
|
|
34
35
|
- 测试命令失败或没有生成报告时,直接显示日志和原因,不展示空结果页;
|
|
@@ -80,7 +81,7 @@ covgate run
|
|
|
80
81
|
|
|
81
82
|
### 从 npm registry 安装(推荐)
|
|
82
83
|
|
|
83
|
-
包已发布到 npm:[covgate](https://www.npmjs.com/package/covgate),当前 latest 为 **0.3.
|
|
84
|
+
包已发布到 npm:[covgate](https://www.npmjs.com/package/covgate),当前 latest 为 **0.3.3**。
|
|
84
85
|
|
|
85
86
|
```bash
|
|
86
87
|
npm install --save-dev covgate
|
|
@@ -132,6 +133,8 @@ setup 会:
|
|
|
132
133
|
|
|
133
134
|
**CovGate 不生成测试,也不补测试。** 行为变更的测试必须由项目自己补;没有测试时 `test:coverage` 会直接失败,不会生成空覆盖率来蒙混过关。
|
|
134
135
|
|
|
136
|
+
如果 setup 找不到覆盖率命令,不会静默跳过:会打印具体的 `test:coverage` 建议、`--test-command` 用法和 GUI 命令。已有 `covgate.config.json` 会保留,并在输出中说明现有 scopes。
|
|
137
|
+
|
|
135
138
|
`postinstall` 也会尝试同样的流程,但 **npm 11.19+ 默认不执行依赖安装脚本**。如果安装时没有弹出询问,直接运行 `npx covgate setup` 即可;详细说明见 [安装与自动配置](docs/setup.md)。
|
|
136
139
|
|
|
137
140
|
### 在 CovGate 仓库中开发
|
|
@@ -509,7 +512,7 @@ macOS 用 `open .covgate/report.html`,Linux 用 `xdg-open .covgate/report.html
|
|
|
509
512
|
文本输出示例:
|
|
510
513
|
|
|
511
514
|
```text
|
|
512
|
-
CovGate · 覆盖率门禁 v0.3.
|
|
515
|
+
CovGate · 覆盖率门禁 v0.3.3
|
|
513
516
|
模式 执行测试并检查 · 配置 covgate.config.json · 用时 12.4s
|
|
514
517
|
|
|
515
518
|
✗ 客户端 [client/client] 未通过
|
package/docs/setup.md
CHANGED
|
@@ -9,7 +9,7 @@ npm install --save-dev covgate
|
|
|
9
9
|
npx --no-install covgate setup
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
包页面:[covgate](https://www.npmjs.com/package/covgate)。当前 latest 是 **0.3.
|
|
12
|
+
包页面:[covgate](https://www.npmjs.com/package/covgate)。当前 latest 是 **0.3.3**。
|
|
13
13
|
|
|
14
14
|
> `0.0.0-stage` 是 npm 暂存发布产生的占位版本,不会被 `npm install covgate` 选中,可以忽略。
|
|
15
15
|
|
|
@@ -38,7 +38,7 @@ npm 11.19+ 默认不执行依赖的 `postinstall`,会显示类似:
|
|
|
38
38
|
|
|
39
39
|
```text
|
|
40
40
|
npm warn install-scripts 1 package has install scripts not yet covered by allowScripts
|
|
41
|
-
npm warn install-scripts covgate@0.3.
|
|
41
|
+
npm warn install-scripts covgate@0.3.3 (postinstall: node bin/covgate-postinstall.js)
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
因此:
|
|
@@ -82,7 +82,9 @@ npm warn install-scripts covgate@0.3.1 (postinstall: node bin/covgate-postinst
|
|
|
82
82
|
- `.github/workflows/covgate.yml`(如果存在 `.github/`)
|
|
83
83
|
- `.gitignore` 加入 `.covgate/`、`coverage/`
|
|
84
84
|
- 若已有 `CLAUDE.md`、`.cursor`、`.github/copilot-instructions.md`,同步写入对应说明。
|
|
85
|
-
6.
|
|
85
|
+
6. 输出下一步命令,包括可视化工作台:`npx --no-install covgate ui`。
|
|
86
|
+
|
|
87
|
+
> 找不到覆盖率测试命令时,setup 不会静默跳过:会给出 `test:coverage` 建议、`--test-command` 示例和 GUI 命令。如果项目已有 `covgate.config.json`,会保留并列出其中 scopes,不会因为缺少通用命令而覆盖或误报。
|
|
86
88
|
|
|
87
89
|
> **CovGate 不生成测试,也不补测试。** setup 只配置测试框架和门禁;本次行为变更的测试必须由项目自己补。没有测试时 `test:coverage` 会直接失败(已移除 `--passWithNoTests`),不会产生可蒙混过关的空覆盖率。
|
|
88
90
|
|
package/docs/ui-result.png
CHANGED
|
Binary file
|
package/docs/ui-viewer.png
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/setup/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import {
|
|
5
5
|
SetupError,
|
|
@@ -20,6 +20,33 @@ import { scaffoldAgent } from "./agent.js";
|
|
|
20
20
|
import { askYesNo, canPrompt } from "./prompt.js";
|
|
21
21
|
|
|
22
22
|
const DEFAULT_COVERAGE_PATH = "coverage/coverage-final.json";
|
|
23
|
+
const UI_COMMAND = "npx --no-install covgate ui";
|
|
24
|
+
|
|
25
|
+
async function existingScopeNames(configPath) {
|
|
26
|
+
try {
|
|
27
|
+
const data = JSON.parse(await readFile(configPath, "utf8"));
|
|
28
|
+
return Object.keys(data.scopes || {});
|
|
29
|
+
} catch {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function printConfigGuidance(log, framework) {
|
|
35
|
+
log("[covgate] 未生成 covgate.config.json:缺少可用的覆盖率测试命令。");
|
|
36
|
+
if (framework.id === "vitest") {
|
|
37
|
+
log('[covgate] 在 package.json 添加 "test:coverage": "vitest run --coverage --coverage.reporter=json --coverage.reporter=lcov",然后重新运行 setup。');
|
|
38
|
+
} else if (framework.id === "jest") {
|
|
39
|
+
log('[covgate] 在 package.json 添加 "test:coverage": "jest --coverage --coverageReporters=json --coverageReporters=lcov",然后重新运行 setup。');
|
|
40
|
+
} else if (framework.id === "node-test") {
|
|
41
|
+
log("[covgate] 检测到 Node.js 内置 test runner。安装 c8 后添加覆盖率命令:");
|
|
42
|
+
log(' "test:coverage": "c8 --reporter=json --reporter=lcov node --test"');
|
|
43
|
+
} else {
|
|
44
|
+
log("[covgate] 先安装/配置测试框架,并在 package.json 添加 test:coverage。");
|
|
45
|
+
}
|
|
46
|
+
log('[covgate] 也可以直接指定命令:npx --no-install covgate setup --test-command "<覆盖率命令>"');
|
|
47
|
+
log("[covgate] 然后运行 npx --no-install covgate validate 检查配置。");
|
|
48
|
+
log("[covgate] 可视化配置工作台:" + UI_COMMAND);
|
|
49
|
+
}
|
|
23
50
|
|
|
24
51
|
/**
|
|
25
52
|
* @param {string} root
|
|
@@ -134,12 +161,22 @@ export async function runSetup(options = {}) {
|
|
|
134
161
|
}
|
|
135
162
|
const coveragePath = normalizeGlob(root, options.coverage || DEFAULT_COVERAGE_PATH);
|
|
136
163
|
const configPath = path.join(root, "covgate.config.json");
|
|
164
|
+
const configExists = existsSync(configPath);
|
|
137
165
|
let configWritten = false;
|
|
166
|
+
let configPreserved = false;
|
|
138
167
|
|
|
139
|
-
if (options.noConfig !== true
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
|
|
168
|
+
if (options.noConfig !== true) {
|
|
169
|
+
if (configExists && options.force !== true) {
|
|
170
|
+
configPreserved = true;
|
|
171
|
+
const names = await existingScopeNames(configPath);
|
|
172
|
+
log(
|
|
173
|
+
"[covgate] " +
|
|
174
|
+
displayPath(root, configPath) +
|
|
175
|
+
" 已存在" +
|
|
176
|
+
(names.length ? "(scopes: " + names.join(", ") + ")" : "") +
|
|
177
|
+
",保留现有配置,不因缺少通用覆盖率命令而覆盖或跳过。"
|
|
178
|
+
);
|
|
179
|
+
} else if (testCommand && scopeSpecs.length > 0) {
|
|
143
180
|
const config = await buildStarterConfig({
|
|
144
181
|
root,
|
|
145
182
|
scopes: scopeSpecs,
|
|
@@ -163,13 +200,16 @@ export async function runSetup(options = {}) {
|
|
|
163
200
|
if (framework.id && !coverageScriptFor(framework.id) && !options.testCommand) {
|
|
164
201
|
log("[covgate] 注意:该测试框架的覆盖率命令需要你确认报告路径。");
|
|
165
202
|
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
203
|
+
} else if (configExists) {
|
|
204
|
+
configPreserved = true;
|
|
205
|
+
log("[covgate] " + displayPath(root, configPath) + " 无法重新生成,保留现有配置。");
|
|
206
|
+
if (!testCommand) {
|
|
207
|
+
log("[covgate] 现有配置仍可使用;如需重新生成,请先补覆盖率命令。");
|
|
208
|
+
}
|
|
209
|
+
} else if (!testCommand) {
|
|
210
|
+
printConfigGuidance(log, framework);
|
|
211
|
+
} else if (scopeSpecs.length === 0) {
|
|
212
|
+
log("[covgate] 未找到源码目录,未生成 covgate.config.json;请用 --source 指定。");
|
|
173
213
|
}
|
|
174
214
|
}
|
|
175
215
|
|
|
@@ -195,6 +235,8 @@ export async function runSetup(options = {}) {
|
|
|
195
235
|
testCommand: testCommand || null,
|
|
196
236
|
coveragePath,
|
|
197
237
|
configPath: configWritten ? configPath : existsSync(configPath) ? configPath : null,
|
|
238
|
+
configPreserved,
|
|
239
|
+
uiCommand: UI_COMMAND,
|
|
198
240
|
packageChanged,
|
|
199
241
|
packageScripts: scriptChanges,
|
|
200
242
|
agentChanges,
|
|
@@ -219,6 +261,7 @@ export async function runSetup(options = {}) {
|
|
|
219
261
|
);
|
|
220
262
|
}
|
|
221
263
|
log("[covgate] 运行门禁: " + (summary.dryRun ? "npx covgate run" : "npx --no-install covgate run"));
|
|
264
|
+
log("[covgate] 可视化工作台: " + UI_COMMAND);
|
|
222
265
|
log("[covgate] 注意:CovGate 不会生成或补写测试;项目必须为行为变更补真实测试后才能通过门禁。");
|
|
223
266
|
if (!options.noAgent) {
|
|
224
267
|
log("[covgate] AI 接入: AGENTS.md 与 scripts/covgate-agent-gate.*");
|
package/src/ui/page.html
CHANGED
|
@@ -201,7 +201,8 @@
|
|
|
201
201
|
<div id="healthCard" class="health" hidden>
|
|
202
202
|
<div class="health-row"><span>测试框架</span><b id="healthFramework">—</b></div>
|
|
203
203
|
<div class="health-row"><span>测试文件</span><b id="healthTests">—</b></div>
|
|
204
|
-
<div class="health-row"><span
|
|
204
|
+
<div class="health-row"><span>客户端报告</span><b id="healthClientCoverage">—</b></div>
|
|
205
|
+
<div class="health-row"><span>服务端报告</span><b id="healthServerCoverage">—</b></div>
|
|
205
206
|
</div>
|
|
206
207
|
<div id="healthWarn" class="health-warn" hidden>
|
|
207
208
|
没有检测到测试文件。CovGate 不会生成测试,请先让项目/AI 补真实测试。
|
|
@@ -243,8 +244,12 @@
|
|
|
243
244
|
<span class="hint">自动检测</span>
|
|
244
245
|
</div>
|
|
245
246
|
<div class="field">
|
|
246
|
-
<label for="
|
|
247
|
-
<input id="
|
|
247
|
+
<label for="clientCoveragePath">客户端报告</label>
|
|
248
|
+
<input id="clientCoveragePath" list="coverageOptions" spellcheck="false" placeholder="coverage/client/coverage-final.json">
|
|
249
|
+
</div>
|
|
250
|
+
<div class="field">
|
|
251
|
+
<label for="serverCoveragePath">服务端报告</label>
|
|
252
|
+
<input id="serverCoveragePath" list="coverageOptions" spellcheck="false" placeholder="coverage/server/coverage-final.json">
|
|
248
253
|
</div>
|
|
249
254
|
<div id="configChip" class="config-chip temp">使用临时配置</div>
|
|
250
255
|
</section>
|
|
@@ -322,7 +327,8 @@
|
|
|
322
327
|
var server = $("serverPath").value.trim() || "(未指定)";
|
|
323
328
|
var clientCommand = $("clientCommand").value || "(未选择)";
|
|
324
329
|
var serverCommand = $("serverCommand").value || "(未选择)";
|
|
325
|
-
var
|
|
330
|
+
var clientCoverage = $("clientCoveragePath").value.trim() || "(未指定)";
|
|
331
|
+
var serverCoverage = $("serverCoveragePath").value.trim() || "(未指定)";
|
|
326
332
|
var framework = state && state.frameworkLabel ? state.frameworkLabel : "项目现有测试框架";
|
|
327
333
|
return [
|
|
328
334
|
"你正在为以下项目补真实测试,目标是让 CovGate 真正通过。",
|
|
@@ -333,7 +339,8 @@
|
|
|
333
339
|
"服务端源码:" + server + "(目标 95%)",
|
|
334
340
|
"客户端测试命令:" + clientCommand,
|
|
335
341
|
"服务端测试命令:" + serverCommand,
|
|
336
|
-
"
|
|
342
|
+
"客户端覆盖率报告:" + clientCoverage,
|
|
343
|
+
"服务端覆盖率报告:" + serverCoverage,
|
|
337
344
|
"",
|
|
338
345
|
"当前测试文件数量:" + ((state && state.testCount) || 0) + "。请先建立/完善测试基础设施,再补真实测试。",
|
|
339
346
|
"",
|
|
@@ -359,6 +366,14 @@
|
|
|
359
366
|
area.remove();
|
|
360
367
|
return Promise.resolve();
|
|
361
368
|
}
|
|
369
|
+
function syncCoverageHealth() {
|
|
370
|
+
var client = $("clientCoveragePath").value.trim() || "未指定";
|
|
371
|
+
var server = $("serverCoveragePath").value.trim() || "未指定";
|
|
372
|
+
$("healthClientCoverage").textContent = client;
|
|
373
|
+
$("healthClientCoverage").title = client;
|
|
374
|
+
$("healthServerCoverage").textContent = server;
|
|
375
|
+
$("healthServerCoverage").title = server;
|
|
376
|
+
}
|
|
362
377
|
function updateHealth(data) {
|
|
363
378
|
var hasTests = (data.testCount || 0) > 0;
|
|
364
379
|
$("healthCard").hidden = false;
|
|
@@ -366,7 +381,7 @@
|
|
|
366
381
|
$("healthFramework").className = data.framework ? "good" : "bad";
|
|
367
382
|
$("healthTests").textContent = hasTests ? data.testCount + " 个" : "0 个 · 需要补测试";
|
|
368
383
|
$("healthTests").className = hasTests ? "good" : "bad";
|
|
369
|
-
|
|
384
|
+
syncCoverageHealth();
|
|
370
385
|
$("healthWarn").hidden = hasTests;
|
|
371
386
|
$("emptyMark").textContent = hasTests ? "⌁" : "!";
|
|
372
387
|
$("emptyTitle").textContent = hasTests ? "把覆盖率跑起来" : "项目还没有测试";
|
|
@@ -374,6 +389,7 @@
|
|
|
374
389
|
? "在左侧选择源码路径和测试命令,点击“运行覆盖率”。<br>完成后会在这里显示文件列表和逐行覆盖情况。"
|
|
375
390
|
: "CovGate 不会生成测试。先让项目或编码代理补真实测试,再运行覆盖率。<br>可以复制下面的补测任务作为 AI 的输入。";
|
|
376
391
|
$("emptyActions").hidden = hasTests;
|
|
392
|
+
syncCoverageHealth();
|
|
377
393
|
setRunning(false);
|
|
378
394
|
}
|
|
379
395
|
function showView(name) {
|
|
@@ -422,12 +438,15 @@
|
|
|
422
438
|
$("root").value = data.root;
|
|
423
439
|
$("clientPath").value = data.clientPath || "";
|
|
424
440
|
$("serverPath").value = data.serverPath || "";
|
|
425
|
-
$("
|
|
441
|
+
$("clientCoveragePath").value = data.clientCoveragePath || data.coveragePath || "coverage/coverage-final.json";
|
|
442
|
+
$("serverCoveragePath").value = data.serverCoveragePath || data.coveragePath || "coverage/coverage-final.json";
|
|
426
443
|
fillCommands($("clientCommand"), data.commands || [], data.clientCommand);
|
|
427
444
|
fillCommands($("serverCommand"), data.commands || [], data.serverCommand);
|
|
428
445
|
fillDatalist("sourceDirs", data.sourceDirs || []);
|
|
429
446
|
var coverageOptions = (data.coverageFiles || []).slice();
|
|
430
|
-
|
|
447
|
+
[data.clientCoveragePath, data.serverCoveragePath, data.coveragePath].forEach(function (value) {
|
|
448
|
+
if (value && coverageOptions.indexOf(value) === -1) coverageOptions.unshift(value);
|
|
449
|
+
});
|
|
431
450
|
fillDatalist("coverageOptions", coverageOptions);
|
|
432
451
|
var chip = $("configChip");
|
|
433
452
|
chip.textContent = data.existingConfig ? "已加载现有 covgate.config.json" : "使用临时配置";
|
|
@@ -578,7 +597,8 @@
|
|
|
578
597
|
serverPath: serverPath,
|
|
579
598
|
clientCommand: clientCommand,
|
|
580
599
|
serverCommand: serverCommand,
|
|
581
|
-
|
|
600
|
+
clientCoveragePath: $("clientCoveragePath").value.trim(),
|
|
601
|
+
serverCoveragePath: $("serverCoveragePath").value.trim(),
|
|
582
602
|
allowNoTests: allowNoTests
|
|
583
603
|
})
|
|
584
604
|
});
|
|
@@ -598,6 +618,8 @@
|
|
|
598
618
|
$("detectBtn").addEventListener("click", detect);
|
|
599
619
|
$("root").addEventListener("keydown", function (event) { if (event.key === "Enter") detect(); });
|
|
600
620
|
$("runBtn").addEventListener("click", run);
|
|
621
|
+
$("clientCoveragePath").addEventListener("input", syncCoverageHealth);
|
|
622
|
+
$("serverCoveragePath").addEventListener("input", syncCoverageHealth);
|
|
601
623
|
$("copyPromptBtn").addEventListener("click", function () {
|
|
602
624
|
copyText(buildPrompt()).then(function () {
|
|
603
625
|
toast("补测任务已复制");
|
package/src/ui/server.js
CHANGED
|
@@ -178,8 +178,17 @@ async function inspectProject(root) {
|
|
|
178
178
|
for (const candidate of COVERAGE_CANDIDATES) {
|
|
179
179
|
if (existsSync(path.join(projectRoot, candidate))) coverageFiles.push(candidate);
|
|
180
180
|
}
|
|
181
|
-
const
|
|
181
|
+
const configuredClientCoverage = scopes.client?.coverage?.paths?.[0];
|
|
182
|
+
const configuredServerCoverage = scopes.server?.coverage?.paths?.[0];
|
|
183
|
+
const configuredCoverage = configuredClientCoverage || configuredServerCoverage;
|
|
182
184
|
const coveragePath = configuredCoverage || coverageFiles[0] || COVERAGE_CANDIDATES[0];
|
|
185
|
+
const clientCoveragePath = configuredClientCoverage || coveragePath;
|
|
186
|
+
const serverCoveragePath = configuredServerCoverage || coveragePath;
|
|
187
|
+
const coverageSuggestions = [...new Set([
|
|
188
|
+
clientCoveragePath,
|
|
189
|
+
serverCoveragePath,
|
|
190
|
+
...coverageFiles
|
|
191
|
+
].filter(Boolean))];
|
|
183
192
|
|
|
184
193
|
const pickExisting = (names) => names.find((name) => existsSync(path.join(projectRoot, name))) || "";
|
|
185
194
|
const clientPath =
|
|
@@ -200,8 +209,10 @@ async function inspectProject(root) {
|
|
|
200
209
|
frameworkLabel: framework.label,
|
|
201
210
|
commands,
|
|
202
211
|
sourceDirs,
|
|
203
|
-
coverageFiles,
|
|
212
|
+
coverageFiles: coverageSuggestions,
|
|
204
213
|
coveragePath,
|
|
214
|
+
clientCoveragePath,
|
|
215
|
+
serverCoveragePath,
|
|
205
216
|
clientPath,
|
|
206
217
|
serverPath,
|
|
207
218
|
clientCommand,
|
|
@@ -216,10 +227,21 @@ async function inspectProject(root) {
|
|
|
216
227
|
async function buildConfig(state, input) {
|
|
217
228
|
const clientPath = relativeInput(state.root, input.clientPath);
|
|
218
229
|
const serverPath = relativeInput(state.root, input.serverPath);
|
|
219
|
-
const
|
|
230
|
+
const fallbackCoveragePath = relativeInput(
|
|
231
|
+
state.root,
|
|
232
|
+
input.coveragePath || state.coveragePath
|
|
233
|
+
);
|
|
234
|
+
const clientCoveragePath = relativeInput(
|
|
235
|
+
state.root,
|
|
236
|
+
input.clientCoveragePath || state.clientCoveragePath || fallbackCoveragePath
|
|
237
|
+
);
|
|
238
|
+
const serverCoveragePath = relativeInput(
|
|
239
|
+
state.root,
|
|
240
|
+
input.serverCoveragePath || state.serverCoveragePath || fallbackCoveragePath
|
|
241
|
+
);
|
|
220
242
|
const existing = state.existing || {};
|
|
221
243
|
const scopes = {};
|
|
222
|
-
const makeScope = async (name, profile, label, sourcePath, command) => {
|
|
244
|
+
const makeScope = async (name, profile, label, sourcePath, command, coveragePath) => {
|
|
223
245
|
if (!sourcePath) return;
|
|
224
246
|
const old = existing.scopes?.[name] || {};
|
|
225
247
|
const oldExclude = Array.isArray(old.exclude) ? old.exclude : [];
|
|
@@ -247,8 +269,8 @@ async function buildConfig(state, input) {
|
|
|
247
269
|
exclude
|
|
248
270
|
};
|
|
249
271
|
};
|
|
250
|
-
await makeScope("client", "client", "客户端", clientPath, input.clientCommand || state.clientCommand);
|
|
251
|
-
await makeScope("server", "server", "服务端", serverPath, input.serverCommand || state.serverCommand);
|
|
272
|
+
await makeScope("client", "client", "客户端", clientPath, input.clientCommand || state.clientCommand, clientCoveragePath);
|
|
273
|
+
await makeScope("server", "server", "服务端", serverPath, input.serverCommand || state.serverCommand, serverCoveragePath);
|
|
252
274
|
if (Object.keys(scopes).length === 0) {
|
|
253
275
|
throw new HttpError(400, "至少需要指定客户端或服务端源码路径。");
|
|
254
276
|
}
|
|
@@ -379,7 +401,10 @@ export async function startUiServer(options = {}) {
|
|
|
379
401
|
serverPath: body.serverPath || "",
|
|
380
402
|
clientCommand: body.clientCommand || state.clientCommand || "",
|
|
381
403
|
serverCommand: body.serverCommand || state.serverCommand || "",
|
|
382
|
-
|
|
404
|
+
clientCoveragePath:
|
|
405
|
+
body.clientCoveragePath || state.clientCoveragePath || body.coveragePath || "",
|
|
406
|
+
serverCoveragePath:
|
|
407
|
+
body.serverCoveragePath || state.serverCoveragePath || body.coveragePath || ""
|
|
383
408
|
}
|
|
384
409
|
};
|
|
385
410
|
jobs.set(id, job);
|