@epoint-testtech/ep-stage-skill 0.0.10 → 0.5.0-alpha.2
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/SKILL.md +27 -27
- package/codex-skill/ep-stage/glue-create-project/SKILL.md +223 -223
- package/codex-skill/ep-stage/glue-generate-testcase/SKILL.md +275 -275
- package/codex-skill/ep-stage/glue-generate-testcase/examples/observable-testcase.json +40 -40
- package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-schema.md +95 -95
- package/codex-skill/ep-stage/glue-run-test/SKILL.md +273 -296
- package/codex-skill/ep-stage/glue-run-test/references/ai-runtime-loop.md +30 -30
- package/codex-skill/ep-stage/glue-run-test/references/crud-pipeline.md +132 -132
- package/codex-skill/ep-stage/glue-run-test/references/gap-review-protocol.md +44 -44
- package/codex-skill/ep-stage/glue-run-test/references/harness-principles.md +46 -46
- package/codex-skill/ep-stage/glue-run-test/references/playwright-cli-odav.md +127 -166
- package/codex-skill/ep-stage/glue-run-test/scripts/generate-crud-spec.mjs +149 -149
- package/codex-skill/ep-stage/recording-to-glue/SKILL.md +28 -28
- package/codex-skill/ep-stage/scripts/validate-skill.mjs +95 -98
- package/dist/src/cli/generate-testcase.d.ts +0 -1
- package/dist/src/cli/generate-testcase.d.ts.map +1 -1
- package/dist/src/cli/generate-testcase.js +1 -6
- package/dist/src/cli/index.d.ts.map +1 -1
- package/dist/src/cli/index.js +14 -38
- package/dist/src/cli/run.d.ts +0 -2
- package/dist/src/cli/run.d.ts.map +1 -1
- package/dist/src/cli/run.js +21 -70
- package/dist/src/cli/spec.d.ts.map +1 -1
- package/dist/src/cli/spec.js +0 -9
- package/dist/src/contracts/glue-contract-v1.d.ts +0 -35
- package/dist/src/contracts/glue-contract-v1.d.ts.map +1 -1
- package/dist/src/extractors/code-list-json5.d.ts +1 -34
- package/dist/src/extractors/code-list-json5.d.ts.map +1 -1
- package/dist/src/extractors/code-list-json5.js +0 -1
- package/dist/src/extractors/html-page.d.ts +0 -9
- package/dist/src/extractors/html-page.d.ts.map +1 -1
- package/dist/src/extractors/html-page.js +2 -26
- package/dist/src/generators/agent-inferred-workflow-script.js +66 -66
- package/dist/src/generators/stage-skeleton-script.d.ts +0 -50
- package/dist/src/generators/stage-skeleton-script.d.ts.map +1 -1
- package/dist/src/generators/stage-skeleton-script.js +116 -449
- package/dist/src/runtime/ai-runtime-trace.d.ts.map +1 -1
- package/dist/src/runtime/ai-runtime-trace.js +0 -8
- package/dist/src/testcase/contract-testcase-assembler.d.ts +0 -28
- package/dist/src/testcase/contract-testcase-assembler.d.ts.map +1 -1
- package/dist/src/testcase/contract-testcase-assembler.js +75 -329
- package/dist/src/validation/contract.d.ts.map +1 -1
- package/dist/src/validation/contract.js +0 -29
- package/dist/test/cli/generate-testcase-json5.test.d.ts +2 -0
- package/dist/test/cli/generate-testcase-json5.test.d.ts.map +1 -0
- package/dist/test/cli/generate-testcase-json5.test.js +154 -0
- package/dist/test/cli/index.test.d.ts +2 -0
- package/dist/test/cli/index.test.d.ts.map +1 -0
- package/dist/test/cli/index.test.js +114 -0
- package/dist/test/cli/run.test.d.ts +2 -0
- package/dist/test/cli/run.test.d.ts.map +1 -0
- package/dist/test/cli/run.test.js +429 -0
- package/dist/test/cli/spec.test.d.ts +2 -0
- package/dist/test/cli/spec.test.d.ts.map +1 -0
- package/dist/test/cli/spec.test.js +112 -0
- package/dist/test/code-list-json5.test.d.ts +2 -0
- package/dist/test/code-list-json5.test.d.ts.map +1 -0
- package/dist/test/code-list-json5.test.js +84 -0
- package/dist/test/code-list.test.d.ts +2 -0
- package/dist/test/code-list.test.d.ts.map +1 -0
- package/dist/test/code-list.test.js +39 -0
- package/dist/test/gap-inference.test.d.ts +2 -0
- package/dist/test/gap-inference.test.d.ts.map +1 -0
- package/dist/test/gap-inference.test.js +597 -0
- package/dist/test/generalization.test.d.ts +2 -0
- package/dist/test/generalization.test.d.ts.map +1 -0
- package/dist/test/generalization.test.js +73 -0
- package/dist/test/material-inventory.test.d.ts +2 -0
- package/dist/test/material-inventory.test.d.ts.map +1 -0
- package/dist/test/material-inventory.test.js +155 -0
- package/dist/test/observable-chain.test.d.ts +2 -0
- package/dist/test/observable-chain.test.d.ts.map +1 -0
- package/dist/test/observable-chain.test.js +123 -0
- package/dist/test/page-structure.test.d.ts +2 -0
- package/dist/test/page-structure.test.d.ts.map +1 -0
- package/dist/test/page-structure.test.js +45 -0
- package/dist/test/runtime/ai-runtime-trace.test.d.ts +2 -0
- package/dist/test/runtime/ai-runtime-trace.test.d.ts.map +1 -0
- package/dist/test/runtime/ai-runtime-trace.test.js +78 -0
- package/dist/test/scenario-inference.test.d.ts +2 -0
- package/dist/test/scenario-inference.test.d.ts.map +1 -0
- package/dist/test/scenario-inference.test.js +73 -0
- package/dist/test/stage-context.test.d.ts +2 -0
- package/dist/test/stage-context.test.d.ts.map +1 -0
- package/dist/test/stage-context.test.js +395 -0
- package/dist/test/testcase/testcase-markdown.test.d.ts +2 -0
- package/dist/test/testcase/testcase-markdown.test.d.ts.map +1 -0
- package/dist/test/testcase/testcase-markdown.test.js +48 -0
- package/dist/test/testcase-generator.test.d.ts +2 -0
- package/dist/test/testcase-generator.test.d.ts.map +1 -0
- package/dist/test/testcase-generator.test.js +276 -0
- package/dist/test/testcase-skeleton.test.d.ts +2 -0
- package/dist/test/testcase-skeleton.test.d.ts.map +1 -0
- package/dist/test/testcase-skeleton.test.js +185 -0
- package/dist/test/testcase-spec-assembly.test.d.ts +2 -0
- package/dist/test/testcase-spec-assembly.test.d.ts.map +1 -0
- package/dist/test/testcase-spec-assembly.test.js +105 -0
- package/dist/test/util/credentials.test.d.ts +2 -0
- package/dist/test/util/credentials.test.d.ts.map +1 -0
- package/dist/test/util/credentials.test.js +64 -0
- package/dist/test/util/i18n-testcase.test.d.ts +2 -0
- package/dist/test/util/i18n-testcase.test.d.ts.map +1 -0
- package/dist/test/util/i18n-testcase.test.js +119 -0
- package/dist/test/util/softlink.test.d.ts +2 -0
- package/dist/test/util/softlink.test.d.ts.map +1 -0
- package/dist/test/util/softlink.test.js +82 -0
- package/dist/test/validation/contract.test.d.ts +2 -0
- package/dist/test/validation/contract.test.d.ts.map +1 -0
- package/dist/test/validation/contract.test.js +116 -0
- package/dist/test/validation/credentials.test.d.ts +2 -0
- package/dist/test/validation/credentials.test.d.ts.map +1 -0
- package/dist/test/validation/credentials.test.js +74 -0
- package/dist/test/validation/projects-index.test.d.ts +2 -0
- package/dist/test/validation/projects-index.test.d.ts.map +1 -0
- package/dist/test/validation/projects-index.test.js +48 -0
- package/dist/test/validation/testcase-v1-draft.test.d.ts +2 -0
- package/dist/test/validation/testcase-v1-draft.test.d.ts.map +1 -0
- package/dist/test/validation/testcase-v1-draft.test.js +173 -0
- package/dist/vitest.config.d.ts +3 -0
- package/dist/vitest.config.d.ts.map +1 -0
- package/dist/vitest.config.js +7 -0
- package/docs/README.md +124 -124
- package/examples/schemeresource-observable-docs/schemeresource.context.md +20 -20
- package/examples/schemeresource.module-hints.json +38 -38
- package/examples/schemeresource.observable.code_list.md +37 -37
- package/examples/zwplace-observable-docs/zwplace.context.md +16 -16
- package/examples/zwplace-placecategory-validation.json +29 -29
- package/examples/zwplace.module-hints.json +69 -69
- package/examples/zwplace.observable.code_list.md +37 -37
- package/package.json +19 -13
- package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-tpl.md +0 -72
- package/codex-skill/ep-stage/glue-run-test/scripts/extract-page-elements-eval.mjs +0 -233
- package/codex-skill/ep-stage/page-element-scan/SKILL.md +0 -91
- package/codex-skill/ep-stage/page-element-scan/evals/evals.json +0 -37
- package/codex-skill/ep-stage/page-element-scan/evals/fixtures/sample-list-page.html +0 -85
- package/codex-skill/ep-stage/page-element-scan/references/pagination-controls.md +0 -153
- package/codex-skill/ep-stage/page-element-scan/scripts/extract-page-elements-eval.mjs +0 -233
- package/codex-skill/ep-stage/page-element-scan/scripts/extract-page-elements.mjs +0 -298
- package/codex-skill/ep-stage/page-element-scan/scripts/scan-page.mjs +0 -749
- package/dist/src/cli/probe.d.ts +0 -46
- package/dist/src/cli/probe.d.ts.map +0 -1
- package/dist/src/cli/probe.js +0 -447
- package/dist/src/cli/run-report.d.ts +0 -46
- package/dist/src/cli/run-report.d.ts.map +0 -1
- package/dist/src/cli/run-report.js +0 -331
- package/dist/src/extractors/prd-extractor.d.ts +0 -70
- package/dist/src/extractors/prd-extractor.d.ts.map +0 -1
- package/dist/src/extractors/prd-extractor.js +0 -248
- package/dist/src/testcase/button-interaction-inference.d.ts +0 -37
- package/dist/src/testcase/button-interaction-inference.d.ts.map +0 -1
- package/dist/src/testcase/button-interaction-inference.js +0 -385
- package/dist/src/testcase/prd-button-mapper.d.ts +0 -72
- package/dist/src/testcase/prd-button-mapper.d.ts.map +0 -1
- package/dist/src/testcase/prd-button-mapper.js +0 -148
- package/dist/src/testcase/testcase-v2.d.ts +0 -50
- package/dist/src/testcase/testcase-v2.d.ts.map +0 -1
- package/dist/src/testcase/testcase-v2.js +0 -1
- package/docs/mvp-usage-guide.md +0 -298
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { i18nTestcase } from '../../src/util/i18n-testcase.js';
|
|
3
|
+
/**
|
|
4
|
+
* REQ-UX-03:testcase.json5 英文字段名 / 状态值 → testcase.md 中文显示文案。
|
|
5
|
+
* 映射表见 spec §4.6。CLI 写 testcase.md 时调用本 util;testcase.json5 内部字段名永远英文。
|
|
6
|
+
*
|
|
7
|
+
* 说明:`unresolved` 在 spec 表中同时出现于「字段名」(未确认项) 和「状态值」(未解决),
|
|
8
|
+
* 这是已知冲突;当前实现以扁平 lookup 表为准(后者覆盖前者),见 i18nTestcase 注释。
|
|
9
|
+
*/
|
|
10
|
+
describe('i18nTestcase', () => {
|
|
11
|
+
describe('审阅状态值', () => {
|
|
12
|
+
it('confirmed → 已确认', () => {
|
|
13
|
+
expect(i18nTestcase('confirmed')).toBe('已确认');
|
|
14
|
+
});
|
|
15
|
+
it('needs_review → 待审阅', () => {
|
|
16
|
+
expect(i18nTestcase('needs_review')).toBe('待审阅');
|
|
17
|
+
});
|
|
18
|
+
it('blocked → 已阻断', () => {
|
|
19
|
+
expect(i18nTestcase('blocked')).toBe('已阻断');
|
|
20
|
+
});
|
|
21
|
+
it('planned → 已计划', () => {
|
|
22
|
+
expect(i18nTestcase('planned')).toBe('已计划');
|
|
23
|
+
});
|
|
24
|
+
it('candidate → 候选', () => {
|
|
25
|
+
expect(i18nTestcase('candidate')).toBe('候选');
|
|
26
|
+
});
|
|
27
|
+
it('resolved → 已解决', () => {
|
|
28
|
+
expect(i18nTestcase('resolved')).toBe('已解决');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe('推荐动作值', () => {
|
|
32
|
+
it('infer → 推理', () => {
|
|
33
|
+
expect(i18nTestcase('infer')).toBe('推理');
|
|
34
|
+
});
|
|
35
|
+
it('supplement-hints → 补充 hints', () => {
|
|
36
|
+
expect(i18nTestcase('supplement-hints')).toBe('补充 hints');
|
|
37
|
+
});
|
|
38
|
+
it('skip → 跳过', () => {
|
|
39
|
+
expect(i18nTestcase('skip')).toBe('跳过');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe('场景值', () => {
|
|
43
|
+
it('crud.single-page → 单页 CRUD', () => {
|
|
44
|
+
expect(i18nTestcase('crud.single-page')).toBe('单页 CRUD');
|
|
45
|
+
});
|
|
46
|
+
it('crud.nested → 嵌套 CRUD', () => {
|
|
47
|
+
expect(i18nTestcase('crud.nested')).toBe('嵌套 CRUD');
|
|
48
|
+
});
|
|
49
|
+
it('statistic → 统计视图', () => {
|
|
50
|
+
expect(i18nTestcase('statistic')).toBe('统计视图');
|
|
51
|
+
});
|
|
52
|
+
it('approval → 审批流', () => {
|
|
53
|
+
expect(i18nTestcase('approval')).toBe('审批流');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe('字段名', () => {
|
|
57
|
+
it('caseId → 用例 ID', () => {
|
|
58
|
+
expect(i18nTestcase('caseId')).toBe('用例 ID');
|
|
59
|
+
});
|
|
60
|
+
it('actionId → 动作 ID', () => {
|
|
61
|
+
expect(i18nTestcase('actionId')).toBe('动作 ID');
|
|
62
|
+
});
|
|
63
|
+
it('scenario → 场景类型', () => {
|
|
64
|
+
expect(i18nTestcase('scenario')).toBe('场景类型');
|
|
65
|
+
});
|
|
66
|
+
it('menuPath → 菜单路径', () => {
|
|
67
|
+
expect(i18nTestcase('menuPath')).toBe('菜单路径');
|
|
68
|
+
});
|
|
69
|
+
it('requiredRole → 操作角色', () => {
|
|
70
|
+
expect(i18nTestcase('requiredRole')).toBe('操作角色');
|
|
71
|
+
});
|
|
72
|
+
it('requiredSystems → 需求系统集', () => {
|
|
73
|
+
expect(i18nTestcase('requiredSystems')).toBe('需求系统集');
|
|
74
|
+
});
|
|
75
|
+
it('requiredRoles → 需求角色集', () => {
|
|
76
|
+
expect(i18nTestcase('requiredRoles')).toBe('需求角色集');
|
|
77
|
+
});
|
|
78
|
+
it('coveredActions → 骨架已命中动作', () => {
|
|
79
|
+
expect(i18nTestcase('coveredActions')).toBe('骨架已命中动作');
|
|
80
|
+
});
|
|
81
|
+
it('uncoveredCandidates → 骨架未命中候选', () => {
|
|
82
|
+
expect(i18nTestcase('uncoveredCandidates')).toBe('骨架未命中候选');
|
|
83
|
+
});
|
|
84
|
+
it('businessIntent → 业务意图', () => {
|
|
85
|
+
expect(i18nTestcase('businessIntent')).toBe('业务意图');
|
|
86
|
+
});
|
|
87
|
+
it('assertionExpectation → 预期断言', () => {
|
|
88
|
+
expect(i18nTestcase('assertionExpectation')).toBe('预期断言');
|
|
89
|
+
});
|
|
90
|
+
it('recommendedAction → 推荐动作', () => {
|
|
91
|
+
expect(i18nTestcase('recommendedAction')).toBe('推荐动作');
|
|
92
|
+
});
|
|
93
|
+
it('reviewStatus → 审阅状态', () => {
|
|
94
|
+
expect(i18nTestcase('reviewStatus')).toBe('审阅状态');
|
|
95
|
+
});
|
|
96
|
+
it('evidence → 证据', () => {
|
|
97
|
+
expect(i18nTestcase('evidence')).toBe('证据');
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe('未知值兜底', () => {
|
|
101
|
+
it('未知值原样返回', () => {
|
|
102
|
+
expect(i18nTestcase('some-unknown-value')).toBe('some-unknown-value');
|
|
103
|
+
});
|
|
104
|
+
it('空字符串原样返回', () => {
|
|
105
|
+
expect(i18nTestcase('')).toBe('');
|
|
106
|
+
});
|
|
107
|
+
it('英文字段名未在表中时原样返回', () => {
|
|
108
|
+
expect(i18nTestcase('createdAt')).toBe('createdAt');
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe('unresolved 冲突(已知 trade-off)', () => {
|
|
112
|
+
// spec §4.6 中 `unresolved` 同时是字段名(未确认项)和状态值(未解决)。
|
|
113
|
+
// 扁平 lookup 表无法同时持有两个映射,后定义的状态值覆盖字段名。
|
|
114
|
+
// 字段名场景下 CLI 可在调用前对 key 做硬编码标签替换;此 util 保留单值兜底。
|
|
115
|
+
it('unresolved → 未解决(状态值优先)', () => {
|
|
116
|
+
expect(i18nTestcase('unresolved')).toBe('未解决');
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"softlink.test.d.ts","sourceRoot":"","sources":["../../../test/util/softlink.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { mkdtempSync, rmSync, writeFileSync, existsSync, readFileSync, lstatSync, mkdirSync, symlinkSync } from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { createSoftlinkWithFallback, softlinkFs } from '../../src/util/softlink.js';
|
|
6
|
+
/**
|
|
7
|
+
* REQ-FILE-02:跨平台软链 fallback util
|
|
8
|
+
* 三档 fallback:symlink(macOS/Linux)→ hardlink → copy + 时间戳警示(Windows 无管理员权限)。
|
|
9
|
+
*
|
|
10
|
+
* 注:fallback 路径通过 softlinkFs.* spy 模拟失败;ESM namespace 不可写,
|
|
11
|
+
* 因此 softlink.ts 显式暴露 softlinkFs 对象供测试替换。
|
|
12
|
+
*/
|
|
13
|
+
describe('createSoftlinkWithFallback', () => {
|
|
14
|
+
let tmpDir;
|
|
15
|
+
let target;
|
|
16
|
+
let linkPath;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
tmpDir = mkdtempSync(path.join(os.tmpdir(), 'softlink-test-'));
|
|
19
|
+
target = path.join(tmpDir, 'code_list.md');
|
|
20
|
+
linkPath = path.join(tmpDir, 'requirement', 'code_list.md');
|
|
21
|
+
writeFileSync(target, '# upstream code list\n');
|
|
22
|
+
mkdirSync(path.dirname(linkPath), { recursive: true });
|
|
23
|
+
});
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
vi.restoreAllMocks();
|
|
26
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
27
|
+
});
|
|
28
|
+
it('symlink 成功时返回 strategy=symlink,且链接指向 target', () => {
|
|
29
|
+
const result = createSoftlinkWithFallback(target, linkPath);
|
|
30
|
+
expect(result.strategy).toBe('symlink');
|
|
31
|
+
expect(result.warned).toBeUndefined();
|
|
32
|
+
expect(existsSync(linkPath)).toBe(true);
|
|
33
|
+
expect(lstatSync(linkPath).isSymbolicLink()).toBe(true);
|
|
34
|
+
// 通过链接读到的内容应该和源文件一致
|
|
35
|
+
expect(readFileSync(linkPath, 'utf8')).toBe('# upstream code list\n');
|
|
36
|
+
});
|
|
37
|
+
it('目标已存在时先 unlink 再创建(覆盖语义)', () => {
|
|
38
|
+
// 先放一个老文件占位
|
|
39
|
+
writeFileSync(linkPath, '# stale content\n');
|
|
40
|
+
const result = createSoftlinkWithFallback(target, linkPath);
|
|
41
|
+
expect(result.strategy).toBe('symlink');
|
|
42
|
+
expect(lstatSync(linkPath).isSymbolicLink()).toBe(true);
|
|
43
|
+
expect(readFileSync(linkPath, 'utf8')).toBe('# upstream code list\n');
|
|
44
|
+
});
|
|
45
|
+
it('目标已存在且为旧 symlink 时也能覆盖', () => {
|
|
46
|
+
// 先建一个指向别处的 symlink
|
|
47
|
+
const altTarget = path.join(tmpDir, 'alt.md');
|
|
48
|
+
writeFileSync(altTarget, '# alt\n');
|
|
49
|
+
symlinkSync(altTarget, linkPath, 'file');
|
|
50
|
+
expect(lstatSync(linkPath).isSymbolicLink()).toBe(true);
|
|
51
|
+
const result = createSoftlinkWithFallback(target, linkPath);
|
|
52
|
+
expect(result.strategy).toBe('symlink');
|
|
53
|
+
expect(readFileSync(linkPath, 'utf8')).toBe('# upstream code list\n');
|
|
54
|
+
});
|
|
55
|
+
it('symlink 失败时回退到 hardlink', () => {
|
|
56
|
+
// mock softlinkFs.symlinkSync 抛错,模拟 Windows 无权限
|
|
57
|
+
vi.spyOn(softlinkFs, 'symlinkSync').mockImplementation(() => {
|
|
58
|
+
throw new Error('EPERM: operation not permitted');
|
|
59
|
+
});
|
|
60
|
+
const result = createSoftlinkWithFallback(target, linkPath);
|
|
61
|
+
expect(result.strategy).toBe('hardlink');
|
|
62
|
+
expect(result.warned).toBeUndefined();
|
|
63
|
+
expect(existsSync(linkPath)).toBe(true);
|
|
64
|
+
expect(lstatSync(linkPath).isSymbolicLink()).toBe(false);
|
|
65
|
+
expect(readFileSync(linkPath, 'utf8')).toBe('# upstream code list\n');
|
|
66
|
+
});
|
|
67
|
+
it('symlink 与 hardlink 均失败时回退到 copy,并返回 warned=true', () => {
|
|
68
|
+
vi.spyOn(softlinkFs, 'symlinkSync').mockImplementation(() => {
|
|
69
|
+
throw new Error('EPERM: symlink not permitted');
|
|
70
|
+
});
|
|
71
|
+
vi.spyOn(softlinkFs, 'linkSync').mockImplementation(() => {
|
|
72
|
+
throw new Error('EXDEV: cross-device link not permitted');
|
|
73
|
+
});
|
|
74
|
+
const result = createSoftlinkWithFallback(target, linkPath);
|
|
75
|
+
expect(result.strategy).toBe('copy');
|
|
76
|
+
expect(result.warned).toBe(true);
|
|
77
|
+
expect(existsSync(linkPath)).toBe(true);
|
|
78
|
+
expect(lstatSync(linkPath).isSymbolicLink()).toBe(false);
|
|
79
|
+
// copy 的文件内容等于源文件
|
|
80
|
+
expect(readFileSync(linkPath, 'utf8')).toBe('# upstream code list\n');
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.test.d.ts","sourceRoot":"","sources":["../../../test/validation/contract.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { validateContract } from '../../src/validation/contract.js';
|
|
3
|
+
/**
|
|
4
|
+
* 构造一份满足 contract v1-draft schema 的最小合法契约对象。
|
|
5
|
+
*
|
|
6
|
+
* @returns 合法 contract v1-draft 对象。
|
|
7
|
+
*/
|
|
8
|
+
function makeValidContract() {
|
|
9
|
+
return {
|
|
10
|
+
schemaVersion: 'ep-stage-contract/v1-draft',
|
|
11
|
+
contractId: 'CONTRACT-001',
|
|
12
|
+
contractKind: 'crud_export',
|
|
13
|
+
base: {
|
|
14
|
+
module: { id: 'org-management', label: '组织管理' },
|
|
15
|
+
navigation: { entryMode: 'menu', menuPath: '系统管理 > 组织管理' },
|
|
16
|
+
credentials: { source: '.env' },
|
|
17
|
+
},
|
|
18
|
+
scenario: {
|
|
19
|
+
kind: 'crud_export',
|
|
20
|
+
searchFields: [
|
|
21
|
+
{ field: 'orgName', label: '组织名称', component: 'input' },
|
|
22
|
+
],
|
|
23
|
+
skeletonSlots: {
|
|
24
|
+
crud: { moduleId: 'org-management', searchConditions: [], frames: [], locators: {}, assertions: [] },
|
|
25
|
+
export: { moduleId: 'org-management', label: '导出', mode: 'button' },
|
|
26
|
+
},
|
|
27
|
+
gapCandidates: [
|
|
28
|
+
{ gapId: 'GAP-001', label: '批量删除', generationStrategy: 'ai' },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
unresolvedSlots: [],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
describe('validateContract', () => {
|
|
35
|
+
it('合法 v1-draft contract 通过', () => {
|
|
36
|
+
const result = validateContract(makeValidContract());
|
|
37
|
+
expect(result.schemaVersion).toBe('ep-stage-contract/v1-draft');
|
|
38
|
+
expect(result.scenario.skeletonSlots.crud).toBeDefined();
|
|
39
|
+
expect(result.scenario.skeletonSlots.export).toBeDefined();
|
|
40
|
+
});
|
|
41
|
+
it('schemaVersion 非 ep-stage-contract/v1-draft 报错', () => {
|
|
42
|
+
const c = { ...makeValidContract(), schemaVersion: 'v2' };
|
|
43
|
+
expect(() => validateContract(c)).toThrow(/schemaVersion/);
|
|
44
|
+
});
|
|
45
|
+
it('缺 scenario.searchFields[] 报错', () => {
|
|
46
|
+
const c = makeValidContract();
|
|
47
|
+
const sc = c.scenario;
|
|
48
|
+
delete sc.searchFields;
|
|
49
|
+
expect(() => validateContract(c)).toThrow(/searchFields/);
|
|
50
|
+
});
|
|
51
|
+
it('searchFields 为空数组报错', () => {
|
|
52
|
+
const c = makeValidContract();
|
|
53
|
+
const sc = c.scenario;
|
|
54
|
+
sc.searchFields = [];
|
|
55
|
+
expect(() => validateContract(c)).toThrow(/searchFields/);
|
|
56
|
+
});
|
|
57
|
+
it('缺 scenario.skeletonSlots 报错', () => {
|
|
58
|
+
const c = makeValidContract();
|
|
59
|
+
const sc = c.scenario;
|
|
60
|
+
delete sc.skeletonSlots;
|
|
61
|
+
expect(() => validateContract(c)).toThrow(/skeletonSlots/);
|
|
62
|
+
});
|
|
63
|
+
it('缺 scenario.skeletonSlots.crud 报错', () => {
|
|
64
|
+
const c = makeValidContract();
|
|
65
|
+
const sc = c.scenario;
|
|
66
|
+
const slots = sc.skeletonSlots;
|
|
67
|
+
delete slots.crud;
|
|
68
|
+
expect(() => validateContract(c)).toThrow(/crud/);
|
|
69
|
+
});
|
|
70
|
+
it('缺 scenario.skeletonSlots.export 报错', () => {
|
|
71
|
+
const c = makeValidContract();
|
|
72
|
+
const sc = c.scenario;
|
|
73
|
+
const slots = sc.skeletonSlots;
|
|
74
|
+
delete slots.export;
|
|
75
|
+
expect(() => validateContract(c)).toThrow(/export/);
|
|
76
|
+
});
|
|
77
|
+
it('缺 unresolvedSlots[] 报错', () => {
|
|
78
|
+
const c = makeValidContract();
|
|
79
|
+
delete c.unresolvedSlots;
|
|
80
|
+
expect(() => validateContract(c)).toThrow(/unresolvedSlots/);
|
|
81
|
+
});
|
|
82
|
+
it('缺 base.module 报错', () => {
|
|
83
|
+
const c = makeValidContract();
|
|
84
|
+
const base = c.base;
|
|
85
|
+
delete base.module;
|
|
86
|
+
expect(() => validateContract(c)).toThrow(/base\.module/);
|
|
87
|
+
});
|
|
88
|
+
it('缺 base.navigation 报错', () => {
|
|
89
|
+
const c = makeValidContract();
|
|
90
|
+
const base = c.base;
|
|
91
|
+
delete base.navigation;
|
|
92
|
+
expect(() => validateContract(c)).toThrow(/base\.navigation/);
|
|
93
|
+
});
|
|
94
|
+
it('base.navigation.entryMode 非法值报错', () => {
|
|
95
|
+
const c = makeValidContract();
|
|
96
|
+
const base = c.base;
|
|
97
|
+
const nav = base.navigation;
|
|
98
|
+
nav.entryMode = 'unknown';
|
|
99
|
+
expect(() => validateContract(c)).toThrow(/entryMode/);
|
|
100
|
+
});
|
|
101
|
+
it('directUrl 模式缺 base.navigation.directUrl 报错', () => {
|
|
102
|
+
const c = makeValidContract();
|
|
103
|
+
const base = c.base;
|
|
104
|
+
const nav = base.navigation;
|
|
105
|
+
nav.entryMode = 'directUrl';
|
|
106
|
+
delete nav.menuPath;
|
|
107
|
+
expect(() => validateContract(c)).toThrow(/base\.navigation\.directUrl/);
|
|
108
|
+
});
|
|
109
|
+
it('gapCandidates[].generationStrategy 非法值报错', () => {
|
|
110
|
+
const c = makeValidContract();
|
|
111
|
+
const sc = c.scenario;
|
|
112
|
+
const gaps = sc.gapCandidates;
|
|
113
|
+
gaps[0].generationStrategy = 'unknown';
|
|
114
|
+
expect(() => validateContract(c)).toThrow(/generationStrategy/);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.test.d.ts","sourceRoot":"","sources":["../../../test/validation/credentials.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { validateCredentials } from '../../src/validation/credentials.js';
|
|
3
|
+
describe('validateCredentials', () => {
|
|
4
|
+
it('合法数组(≥1 条)通过', () => {
|
|
5
|
+
const result = validateCredentials([
|
|
6
|
+
{
|
|
7
|
+
url: 'http://example.test',
|
|
8
|
+
username: 'admin',
|
|
9
|
+
password: 'pwd',
|
|
10
|
+
role: '系统管理员',
|
|
11
|
+
systemName: '基础平台',
|
|
12
|
+
},
|
|
13
|
+
]);
|
|
14
|
+
expect(result).toHaveLength(1);
|
|
15
|
+
expect(result[0]).toMatchObject({
|
|
16
|
+
url: 'http://example.test',
|
|
17
|
+
loginUrl: 'http://example.test',
|
|
18
|
+
username: 'admin',
|
|
19
|
+
password: 'pwd',
|
|
20
|
+
role: '系统管理员',
|
|
21
|
+
systemName: '基础平台',
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
it('允许无 role / systemName 的条目', () => {
|
|
25
|
+
const result = validateCredentials([
|
|
26
|
+
{ url: 'http://example.test', username: 'admin', password: 'pwd' },
|
|
27
|
+
]);
|
|
28
|
+
expect(result[0]).toEqual({
|
|
29
|
+
url: 'http://example.test',
|
|
30
|
+
loginUrl: 'http://example.test',
|
|
31
|
+
username: 'admin',
|
|
32
|
+
password: 'pwd',
|
|
33
|
+
});
|
|
34
|
+
expect(result[0]).not.toHaveProperty('role');
|
|
35
|
+
expect(result[0]).not.toHaveProperty('systemName');
|
|
36
|
+
});
|
|
37
|
+
it('非数组报错', () => {
|
|
38
|
+
expect(() => validateCredentials({ url: 'x' })).toThrow(/数组/);
|
|
39
|
+
});
|
|
40
|
+
it('空数组报错', () => {
|
|
41
|
+
expect(() => validateCredentials([])).toThrow(/至少|1 条/);
|
|
42
|
+
});
|
|
43
|
+
it('条目缺 loginUrl/url 报错', () => {
|
|
44
|
+
expect(() => validateCredentials([{ username: 'admin', password: 'pwd' }])).toThrow(/loginUrl/);
|
|
45
|
+
});
|
|
46
|
+
it('条目缺 username 报错', () => {
|
|
47
|
+
expect(() => validateCredentials([{ url: 'http://example.test', password: 'pwd' }])).toThrow(/username/);
|
|
48
|
+
});
|
|
49
|
+
it('条目缺 password 报错', () => {
|
|
50
|
+
expect(() => validateCredentials([{ url: 'http://example.test', username: 'admin' }])).toThrow(/password/);
|
|
51
|
+
});
|
|
52
|
+
it('条目空字符串字段(空白)报错', () => {
|
|
53
|
+
expect(() => validateCredentials([{ url: ' ', username: 'admin', password: 'pwd' }])).toThrow(/loginUrl/);
|
|
54
|
+
});
|
|
55
|
+
it('同 (url, role) 重复报错', () => {
|
|
56
|
+
expect(() => validateCredentials([
|
|
57
|
+
{ url: 'http://example.test', username: 'a', password: 'p1', role: '管理员' },
|
|
58
|
+
{ url: 'http://example.test', username: 'b', password: 'p2', role: '管理员' },
|
|
59
|
+
])).toThrow(/重复/);
|
|
60
|
+
});
|
|
61
|
+
it('同 url 但 role 不同允许', () => {
|
|
62
|
+
const result = validateCredentials([
|
|
63
|
+
{ url: 'http://example.test', username: 'a', password: 'p1', role: '管理员' },
|
|
64
|
+
{ url: 'http://example.test', username: 'b', password: 'p2', role: '业务员' },
|
|
65
|
+
]);
|
|
66
|
+
expect(result).toHaveLength(2);
|
|
67
|
+
});
|
|
68
|
+
it('同 url 都没 role 视作 (url, "") 也会触发重复', () => {
|
|
69
|
+
expect(() => validateCredentials([
|
|
70
|
+
{ url: 'http://example.test', username: 'a', password: 'p1' },
|
|
71
|
+
{ url: 'http://example.test', username: 'b', password: 'p2' },
|
|
72
|
+
])).toThrow(/重复/);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects-index.test.d.ts","sourceRoot":"","sources":["../../../test/validation/projects-index.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { validateProjectsIndex } from '../../src/validation/projects-index.js';
|
|
3
|
+
describe('validateProjectsIndex', () => {
|
|
4
|
+
it('通过合法结构(含 systems[] 与 stageCreateVersion)', () => {
|
|
5
|
+
const obj = {
|
|
6
|
+
projects: [
|
|
7
|
+
{
|
|
8
|
+
projectName: 'demo',
|
|
9
|
+
projectDir: '/abs/demo',
|
|
10
|
+
mode: 'glue',
|
|
11
|
+
createdAt: '2026-06-25T00:00:00Z',
|
|
12
|
+
stageCreateVersion: '0.0.4-alpha.0',
|
|
13
|
+
requirements: [],
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
systems: [],
|
|
17
|
+
};
|
|
18
|
+
const result = validateProjectsIndex(obj);
|
|
19
|
+
expect(result.projects).toHaveLength(1);
|
|
20
|
+
expect(result.projects[0].stageCreateVersion).toBe('0.0.4-alpha.0');
|
|
21
|
+
expect(result.systems).toEqual([]);
|
|
22
|
+
});
|
|
23
|
+
it('缺 systems 字段时报结构化错误', () => {
|
|
24
|
+
expect(() => validateProjectsIndex({ projects: [] })).toThrow(/systems/);
|
|
25
|
+
});
|
|
26
|
+
it('项目条目缺 stageCreateVersion 报错(stage-context.md 砍掉后的元信息承接)', () => {
|
|
27
|
+
expect(() => validateProjectsIndex({
|
|
28
|
+
projects: [{ projectName: 'x', projectDir: '/x', mode: 'glue', requirements: [] }],
|
|
29
|
+
systems: [],
|
|
30
|
+
})).toThrow(/stageCreateVersion/);
|
|
31
|
+
});
|
|
32
|
+
it('不允许残留 stageContextPath / envPath / knowledgeRoot 字段', () => {
|
|
33
|
+
expect(() => validateProjectsIndex({
|
|
34
|
+
projects: [
|
|
35
|
+
{
|
|
36
|
+
projectName: 'x',
|
|
37
|
+
projectDir: '/x',
|
|
38
|
+
mode: 'glue',
|
|
39
|
+
createdAt: '2026-06-25T00:00:00Z',
|
|
40
|
+
stageCreateVersion: '0.0.4-alpha.0',
|
|
41
|
+
requirements: [],
|
|
42
|
+
stageContextPath: '/x/stage-context.md', // 应被拒绝
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
systems: [],
|
|
46
|
+
})).toThrow(/stageContextPath|removed/);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testcase-v1-draft.test.d.ts","sourceRoot":"","sources":["../../../test/validation/testcase-v1-draft.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { validateTestcase } from '../../src/validation/testcase.js';
|
|
3
|
+
/**
|
|
4
|
+
* 构造一份满足 testcase v1-draft schema 的最小合法用例对象。
|
|
5
|
+
*
|
|
6
|
+
* @returns 合法 testcase v1-draft 对象。
|
|
7
|
+
*/
|
|
8
|
+
function makeValidTestcase() {
|
|
9
|
+
return {
|
|
10
|
+
schemaVersion: 'ep-stage-testcase/v1-draft',
|
|
11
|
+
testcaseId: 'TC-001',
|
|
12
|
+
contractRef: '.ep-stage/contract.json5',
|
|
13
|
+
scenario: '组织管理 CRUD 主流程',
|
|
14
|
+
moduleId: 'org-management',
|
|
15
|
+
moduleName: '组织管理',
|
|
16
|
+
runtimeContext: {
|
|
17
|
+
testProjectDir: '/tmp/glue-project',
|
|
18
|
+
caseDir: '/tmp/glue-project/src/tests/014',
|
|
19
|
+
specPath: '/tmp/glue-project/src/tests/014/crud.spec.ts',
|
|
20
|
+
},
|
|
21
|
+
testAccess: {
|
|
22
|
+
systems: [
|
|
23
|
+
{
|
|
24
|
+
systemName: '政务系统',
|
|
25
|
+
loginUrl: 'http://example/login',
|
|
26
|
+
roles: [
|
|
27
|
+
{
|
|
28
|
+
role: '业务管理员',
|
|
29
|
+
username: 'yz',
|
|
30
|
+
password: 'secret',
|
|
31
|
+
permissions: ['新增', '修改', '删除', '导出'],
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
cases: [
|
|
38
|
+
{
|
|
39
|
+
caseId: 'CASE-001',
|
|
40
|
+
title: '管理员新增组织',
|
|
41
|
+
generationStrategy: 'glue',
|
|
42
|
+
coverageStatus: 'covered',
|
|
43
|
+
reviewStatus: 'confirmed',
|
|
44
|
+
queryKey: '场所名称',
|
|
45
|
+
precondition: '使用业务管理员登录并进入列表页',
|
|
46
|
+
steps: '新增、查询、编辑、删除场所窗口信息',
|
|
47
|
+
assertion: '列表可查询到新增数据,编辑后字段更新,删除后列表不再出现',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
uncoveredCandidates: [
|
|
51
|
+
{
|
|
52
|
+
caseId: 'CASE-002',
|
|
53
|
+
label: '导出',
|
|
54
|
+
generationStrategy: 'ai',
|
|
55
|
+
coverageStatus: 'gap',
|
|
56
|
+
requiredRole: '系统管理员',
|
|
57
|
+
businessIntent: '导出当前查询结果',
|
|
58
|
+
assertionExpectation: '断言下载文件存在',
|
|
59
|
+
queryKey: '导出',
|
|
60
|
+
precondition: '使用系统管理员登录并进入列表页',
|
|
61
|
+
steps: '点击导出并选择当前页',
|
|
62
|
+
assertion: '成功导出当前列表结果',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
describe('validateTestcase (v1-draft)', () => {
|
|
68
|
+
it('合法 v1-draft testcase 通过', () => {
|
|
69
|
+
const result = validateTestcase(makeValidTestcase());
|
|
70
|
+
expect(result.schemaVersion).toBe('ep-stage-testcase/v1-draft');
|
|
71
|
+
expect(result.cases).toHaveLength(1);
|
|
72
|
+
expect(result.uncoveredCandidates).toHaveLength(1);
|
|
73
|
+
expect(result.runtimeContext?.specPath).toBe('/tmp/glue-project/src/tests/014/crud.spec.ts');
|
|
74
|
+
expect(result.testAccess?.systems?.[0]?.roles[0]?.password).toBe('secret');
|
|
75
|
+
expect(result.cases[0]?.steps).toContain('新增');
|
|
76
|
+
expect(result.uncoveredCandidates[0]?.assertion).toContain('导出');
|
|
77
|
+
});
|
|
78
|
+
it('testAccess 允许角色账号密码缺省,供 generate 交互补齐', () => {
|
|
79
|
+
const tc = makeValidTestcase();
|
|
80
|
+
const systems = tc.testAccess.systems;
|
|
81
|
+
delete systems[0]?.roles[0]?.username;
|
|
82
|
+
delete systems[0]?.roles[0]?.password;
|
|
83
|
+
const result = validateTestcase(tc);
|
|
84
|
+
expect(result.testAccess?.systems?.[0]?.roles[0]?.username).toBeUndefined();
|
|
85
|
+
expect(result.testAccess?.systems?.[0]?.roles[0]?.password).toBeUndefined();
|
|
86
|
+
});
|
|
87
|
+
it('testAccess 允许仅引用 credentials.json5 并声明 requiredRoles', () => {
|
|
88
|
+
const tc = makeValidTestcase();
|
|
89
|
+
tc.testAccess = {
|
|
90
|
+
credentialRef: 'credentials.json5',
|
|
91
|
+
requiredRoles: ['业务管理员'],
|
|
92
|
+
};
|
|
93
|
+
const result = validateTestcase(tc);
|
|
94
|
+
expect(result.testAccess?.credentialRef).toBe('credentials.json5');
|
|
95
|
+
expect(result.testAccess?.requiredRoles).toEqual(['业务管理员']);
|
|
96
|
+
});
|
|
97
|
+
it('runtimeContext 结构非法时报错', () => {
|
|
98
|
+
const tc = makeValidTestcase();
|
|
99
|
+
delete tc.runtimeContext.specPath;
|
|
100
|
+
expect(() => validateTestcase(tc)).toThrow(/runtimeContext\.specPath/);
|
|
101
|
+
});
|
|
102
|
+
it('schemaVersion 非 ep-stage-testcase/v1-draft 报错', () => {
|
|
103
|
+
const tc = { ...makeValidTestcase(), schemaVersion: 'v2' };
|
|
104
|
+
expect(() => validateTestcase(tc)).toThrow(/schemaVersion/);
|
|
105
|
+
});
|
|
106
|
+
it('menuPath 可缺省', () => {
|
|
107
|
+
const result = validateTestcase(makeValidTestcase());
|
|
108
|
+
expect(result.menuPath).toBeUndefined();
|
|
109
|
+
});
|
|
110
|
+
it('缺 moduleId 报错', () => {
|
|
111
|
+
const tc = makeValidTestcase();
|
|
112
|
+
delete tc.moduleId;
|
|
113
|
+
expect(() => validateTestcase(tc)).toThrow(/moduleId/);
|
|
114
|
+
});
|
|
115
|
+
it('缺 moduleName 报错', () => {
|
|
116
|
+
const tc = makeValidTestcase();
|
|
117
|
+
delete tc.moduleName;
|
|
118
|
+
expect(() => validateTestcase(tc)).toThrow(/moduleName/);
|
|
119
|
+
});
|
|
120
|
+
it('缺 contractRef 报错', () => {
|
|
121
|
+
const tc = makeValidTestcase();
|
|
122
|
+
delete tc.contractRef;
|
|
123
|
+
expect(() => validateTestcase(tc)).toThrow(/contractRef/);
|
|
124
|
+
});
|
|
125
|
+
it('cases[].generationStrategy 非 glue 报错', () => {
|
|
126
|
+
const tc = makeValidTestcase();
|
|
127
|
+
const cases = tc.cases;
|
|
128
|
+
cases[0].generationStrategy = 'ai';
|
|
129
|
+
expect(() => validateTestcase(tc)).toThrow(/generationStrategy/);
|
|
130
|
+
});
|
|
131
|
+
it('cases[].reviewStatus 缺失报错', () => {
|
|
132
|
+
const tc = makeValidTestcase();
|
|
133
|
+
const cases = tc.cases;
|
|
134
|
+
delete cases[0].reviewStatus;
|
|
135
|
+
expect(() => validateTestcase(tc)).toThrow(/reviewStatus/);
|
|
136
|
+
});
|
|
137
|
+
it('cases[].reviewStatus 非法值报错', () => {
|
|
138
|
+
const tc = makeValidTestcase();
|
|
139
|
+
const cases = tc.cases;
|
|
140
|
+
cases[0].reviewStatus = 'unknown';
|
|
141
|
+
expect(() => validateTestcase(tc)).toThrow(/reviewStatus/);
|
|
142
|
+
});
|
|
143
|
+
it('uncoveredCandidates[].generationStrategy=glue 报错', () => {
|
|
144
|
+
const tc = makeValidTestcase();
|
|
145
|
+
const unc = tc.uncoveredCandidates;
|
|
146
|
+
unc[0].generationStrategy = 'glue';
|
|
147
|
+
expect(() => validateTestcase(tc)).toThrow(/generationStrategy/);
|
|
148
|
+
});
|
|
149
|
+
it('uncoveredCandidates[].generationStrategy=manual 报错', () => {
|
|
150
|
+
const tc = makeValidTestcase();
|
|
151
|
+
const unc = tc.uncoveredCandidates;
|
|
152
|
+
unc[0].generationStrategy = 'manual';
|
|
153
|
+
expect(() => validateTestcase(tc)).toThrow(/generationStrategy/);
|
|
154
|
+
});
|
|
155
|
+
it('uncoveredCandidates[].requiredRole 缺失报错', () => {
|
|
156
|
+
const tc = makeValidTestcase();
|
|
157
|
+
const unc = tc.uncoveredCandidates;
|
|
158
|
+
delete unc[0].requiredRole;
|
|
159
|
+
expect(() => validateTestcase(tc)).toThrow(/requiredRole/);
|
|
160
|
+
});
|
|
161
|
+
it('uncoveredCandidates[].businessIntent 缺失报错', () => {
|
|
162
|
+
const tc = makeValidTestcase();
|
|
163
|
+
const unc = tc.uncoveredCandidates;
|
|
164
|
+
delete unc[0].businessIntent;
|
|
165
|
+
expect(() => validateTestcase(tc)).toThrow(/businessIntent/);
|
|
166
|
+
});
|
|
167
|
+
it('uncoveredCandidates[].assertionExpectation 缺失报错', () => {
|
|
168
|
+
const tc = makeValidTestcase();
|
|
169
|
+
const unc = tc.uncoveredCandidates;
|
|
170
|
+
delete unc[0].assertionExpectation;
|
|
171
|
+
expect(() => validateTestcase(tc)).toThrow(/assertionExpectation/);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest.config.d.ts","sourceRoot":"","sources":["../vitest.config.ts"],"names":[],"mappings":";AAEA,wBAKG"}
|