@epoint-testtech/ep-stage-skill 0.0.4-alpha.1 → 0.0.6

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.
Files changed (213) hide show
  1. package/SKILL.md +27 -27
  2. package/codex-skill/ep-stage/glue-create-project/SKILL.md +223 -206
  3. package/codex-skill/ep-stage/glue-generate-testcase/SKILL.md +275 -274
  4. package/codex-skill/ep-stage/glue-generate-testcase/examples/observable-testcase.json +40 -40
  5. package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-schema.md +95 -112
  6. package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-tpl.md +72 -72
  7. package/codex-skill/ep-stage/glue-run-test/SKILL.md +295 -289
  8. package/codex-skill/ep-stage/glue-run-test/references/ai-runtime-loop.md +30 -0
  9. package/codex-skill/ep-stage/glue-run-test/references/crud-pipeline.md +132 -145
  10. package/codex-skill/ep-stage/glue-run-test/references/gap-review-protocol.md +44 -43
  11. package/codex-skill/ep-stage/glue-run-test/references/harness-principles.md +46 -46
  12. package/codex-skill/ep-stage/glue-run-test/references/playwright-cli-odav.md +166 -0
  13. package/codex-skill/ep-stage/glue-run-test/scripts/extract-page-elements-eval.mjs +233 -0
  14. package/codex-skill/ep-stage/glue-run-test/scripts/generate-crud-spec.mjs +149 -149
  15. package/codex-skill/ep-stage/page-element-scan/SKILL.md +91 -0
  16. package/codex-skill/ep-stage/page-element-scan/evals/evals.json +37 -0
  17. package/codex-skill/ep-stage/page-element-scan/evals/fixtures/sample-list-page.html +85 -0
  18. package/codex-skill/ep-stage/page-element-scan/references/pagination-controls.md +153 -0
  19. package/codex-skill/ep-stage/page-element-scan/scripts/extract-page-elements-eval.mjs +233 -0
  20. package/codex-skill/ep-stage/page-element-scan/scripts/extract-page-elements.mjs +298 -0
  21. package/codex-skill/ep-stage/page-element-scan/scripts/scan-page.mjs +749 -0
  22. package/codex-skill/ep-stage/recording-to-glue/SKILL.md +28 -28
  23. package/codex-skill/ep-stage/scripts/validate-skill.mjs +98 -95
  24. package/dist/src/cli/generate-testcase.d.ts +17 -51
  25. package/dist/src/cli/generate-testcase.d.ts.map +1 -1
  26. package/dist/src/cli/generate-testcase.js +74 -181
  27. package/dist/src/cli/index.d.ts +3 -2
  28. package/dist/src/cli/index.d.ts.map +1 -1
  29. package/dist/src/cli/index.js +51 -17
  30. package/dist/src/cli/run.d.ts +136 -29
  31. package/dist/src/cli/run.d.ts.map +1 -1
  32. package/dist/src/cli/run.js +507 -65
  33. package/dist/src/cli/spec.d.ts +17 -13
  34. package/dist/src/cli/spec.d.ts.map +1 -1
  35. package/dist/src/cli/spec.js +59 -33
  36. package/dist/src/contracts/crud-business-module.d.ts +6 -0
  37. package/dist/src/contracts/crud-business-module.d.ts.map +1 -1
  38. package/dist/src/contracts/glue-contract-v1.d.ts +98 -0
  39. package/dist/src/contracts/glue-contract-v1.d.ts.map +1 -0
  40. package/dist/src/contracts/glue-contract-v1.js +9 -0
  41. package/dist/src/contracts/observable-chain.d.ts +4 -0
  42. package/dist/src/contracts/observable-chain.d.ts.map +1 -1
  43. package/dist/src/extractors/code-list-json5.d.ts +192 -0
  44. package/dist/src/extractors/code-list-json5.d.ts.map +1 -0
  45. package/dist/src/extractors/code-list-json5.js +91 -0
  46. package/dist/src/extractors/html-page.d.ts +9 -0
  47. package/dist/src/extractors/html-page.d.ts.map +1 -1
  48. package/dist/src/extractors/html-page.js +26 -2
  49. package/dist/src/extractors/prd-extractor.d.ts +70 -0
  50. package/dist/src/extractors/prd-extractor.d.ts.map +1 -0
  51. package/dist/src/extractors/prd-extractor.js +248 -0
  52. package/dist/src/generators/agent-inferred-workflow-script.js +66 -66
  53. package/dist/src/generators/stage-skeleton-script.d.ts +87 -20
  54. package/dist/src/generators/stage-skeleton-script.d.ts.map +1 -1
  55. package/dist/src/generators/stage-skeleton-script.js +647 -238
  56. package/dist/src/index.d.ts +2 -2
  57. package/dist/src/index.d.ts.map +1 -1
  58. package/dist/src/index.js +1 -1
  59. package/dist/src/normalizers/crud-contract.d.ts +3 -0
  60. package/dist/src/normalizers/crud-contract.d.ts.map +1 -1
  61. package/dist/src/normalizers/crud-contract.js +3 -0
  62. package/dist/src/runtime/ai-runtime-trace.d.ts +88 -0
  63. package/dist/src/runtime/ai-runtime-trace.d.ts.map +1 -0
  64. package/dist/src/runtime/ai-runtime-trace.js +119 -0
  65. package/dist/src/testcase/button-interaction-inference.d.ts +37 -0
  66. package/dist/src/testcase/button-interaction-inference.d.ts.map +1 -0
  67. package/dist/src/testcase/button-interaction-inference.js +385 -0
  68. package/dist/src/testcase/contract-testcase-assembler.d.ts +44 -0
  69. package/dist/src/testcase/contract-testcase-assembler.d.ts.map +1 -0
  70. package/dist/src/testcase/contract-testcase-assembler.js +806 -0
  71. package/dist/src/testcase/prd-button-mapper.d.ts +72 -0
  72. package/dist/src/testcase/prd-button-mapper.d.ts.map +1 -0
  73. package/dist/src/testcase/prd-button-mapper.js +148 -0
  74. package/dist/src/testcase/runtime-context.d.ts +9 -0
  75. package/dist/src/testcase/runtime-context.d.ts.map +1 -0
  76. package/dist/src/testcase/runtime-context.js +1 -0
  77. package/dist/src/testcase/test-access.d.ts +38 -0
  78. package/dist/src/testcase/test-access.d.ts.map +1 -0
  79. package/dist/src/testcase/test-access.js +106 -0
  80. package/dist/src/testcase/testcase-execution-fields.d.ts +10 -0
  81. package/dist/src/testcase/testcase-execution-fields.d.ts.map +1 -0
  82. package/dist/src/testcase/testcase-execution-fields.js +1 -0
  83. package/dist/src/testcase/testcase-generator.d.ts.map +1 -1
  84. package/dist/src/testcase/testcase-generator.js +6 -0
  85. package/dist/src/testcase/testcase-markdown.d.ts +11 -0
  86. package/dist/src/testcase/testcase-markdown.d.ts.map +1 -0
  87. package/dist/src/testcase/testcase-markdown.js +181 -0
  88. package/dist/src/testcase/testcase-skeleton.d.ts +4 -0
  89. package/dist/src/testcase/testcase-skeleton.d.ts.map +1 -1
  90. package/dist/src/testcase/testcase-spec-assembly.d.ts +4 -0
  91. package/dist/src/testcase/testcase-spec-assembly.d.ts.map +1 -1
  92. package/dist/src/testcase/testcase-v1-draft.d.ts +58 -0
  93. package/dist/src/testcase/testcase-v1-draft.d.ts.map +1 -0
  94. package/dist/src/testcase/testcase-v1-draft.js +13 -0
  95. package/dist/src/validation/contract.d.ts +14 -0
  96. package/dist/src/validation/contract.d.ts.map +1 -0
  97. package/dist/src/validation/contract.js +132 -0
  98. package/dist/src/validation/credentials.d.ts +7 -2
  99. package/dist/src/validation/credentials.d.ts.map +1 -1
  100. package/dist/src/validation/credentials.js +15 -4
  101. package/dist/src/validation/index.d.ts +1 -0
  102. package/dist/src/validation/index.d.ts.map +1 -1
  103. package/dist/src/validation/index.js +1 -0
  104. package/dist/src/validation/testcase.d.ts +9 -7
  105. package/dist/src/validation/testcase.d.ts.map +1 -1
  106. package/dist/src/validation/testcase.js +181 -34
  107. package/docs/README.md +124 -134
  108. package/docs/mvp-usage-guide.md +298 -298
  109. package/examples/schemeresource-observable-docs/schemeresource.context.md +20 -20
  110. package/examples/schemeresource.module-hints.json +38 -38
  111. package/examples/schemeresource.observable.code_list.md +37 -37
  112. package/examples/zwplace-observable-docs/zwplace.context.md +16 -16
  113. package/examples/zwplace-placecategory-validation.json +29 -29
  114. package/examples/zwplace.module-hints.json +69 -69
  115. package/examples/zwplace.observable.code_list.md +37 -37
  116. package/package.json +2 -2
  117. package/codex-skill/ep-stage/.omc/state/sessions/d636ad35-5fc9-4d5e-85b0-2bcad7d86ee5/pre-tool-advisory-throttle.json +0 -10
  118. package/dist/test/cli/extract-contract.test.d.ts +0 -2
  119. package/dist/test/cli/extract-contract.test.d.ts.map +0 -1
  120. package/dist/test/cli/extract-contract.test.js +0 -32
  121. package/dist/test/cli/generate-testcase-inference.test.d.ts +0 -2
  122. package/dist/test/cli/generate-testcase-inference.test.d.ts.map +0 -1
  123. package/dist/test/cli/generate-testcase-inference.test.js +0 -63
  124. package/dist/test/cli/generate-testcase-paths.test.d.ts +0 -2
  125. package/dist/test/cli/generate-testcase-paths.test.d.ts.map +0 -1
  126. package/dist/test/cli/generate-testcase-paths.test.js +0 -165
  127. package/dist/test/cli/generate-testcase-requirements.test.d.ts +0 -2
  128. package/dist/test/cli/generate-testcase-requirements.test.d.ts.map +0 -1
  129. package/dist/test/cli/generate-testcase-requirements.test.js +0 -229
  130. package/dist/test/cli/generate-testcase.test.d.ts +0 -2
  131. package/dist/test/cli/generate-testcase.test.d.ts.map +0 -1
  132. package/dist/test/cli/generate-testcase.test.js +0 -130
  133. package/dist/test/cli/index.test.d.ts +0 -2
  134. package/dist/test/cli/index.test.d.ts.map +0 -1
  135. package/dist/test/cli/index.test.js +0 -113
  136. package/dist/test/cli/probe.test.d.ts +0 -2
  137. package/dist/test/cli/probe.test.d.ts.map +0 -1
  138. package/dist/test/cli/probe.test.js +0 -31
  139. package/dist/test/cli/run.test.d.ts +0 -2
  140. package/dist/test/cli/run.test.d.ts.map +0 -1
  141. package/dist/test/cli/run.test.js +0 -149
  142. package/dist/test/cli/spec-boundary.test.d.ts +0 -2
  143. package/dist/test/cli/spec-boundary.test.d.ts.map +0 -1
  144. package/dist/test/cli/spec-boundary.test.js +0 -198
  145. package/dist/test/cli/spec.test.d.ts +0 -2
  146. package/dist/test/cli/spec.test.d.ts.map +0 -1
  147. package/dist/test/cli/spec.test.js +0 -196
  148. package/dist/test/code-list.test.d.ts +0 -2
  149. package/dist/test/code-list.test.d.ts.map +0 -1
  150. package/dist/test/code-list.test.js +0 -39
  151. package/dist/test/crud-contract.test.d.ts +0 -2
  152. package/dist/test/crud-contract.test.d.ts.map +0 -1
  153. package/dist/test/crud-contract.test.js +0 -799
  154. package/dist/test/gap-inference.test.d.ts +0 -2
  155. package/dist/test/gap-inference.test.d.ts.map +0 -1
  156. package/dist/test/gap-inference.test.js +0 -597
  157. package/dist/test/generalization.test.d.ts +0 -2
  158. package/dist/test/generalization.test.d.ts.map +0 -1
  159. package/dist/test/generalization.test.js +0 -73
  160. package/dist/test/inference/module-hints-inference.test.d.ts +0 -2
  161. package/dist/test/inference/module-hints-inference.test.d.ts.map +0 -1
  162. package/dist/test/inference/module-hints-inference.test.js +0 -25
  163. package/dist/test/inference/source-analysis.test.d.ts +0 -2
  164. package/dist/test/inference/source-analysis.test.d.ts.map +0 -1
  165. package/dist/test/inference/source-analysis.test.js +0 -23
  166. package/dist/test/material-inventory.test.d.ts +0 -2
  167. package/dist/test/material-inventory.test.d.ts.map +0 -1
  168. package/dist/test/material-inventory.test.js +0 -154
  169. package/dist/test/observable-chain.test.d.ts +0 -2
  170. package/dist/test/observable-chain.test.d.ts.map +0 -1
  171. package/dist/test/observable-chain.test.js +0 -123
  172. package/dist/test/observable-pipeline.test.d.ts +0 -2
  173. package/dist/test/observable-pipeline.test.d.ts.map +0 -1
  174. package/dist/test/observable-pipeline.test.js +0 -470
  175. package/dist/test/page-structure.test.d.ts +0 -2
  176. package/dist/test/page-structure.test.d.ts.map +0 -1
  177. package/dist/test/page-structure.test.js +0 -45
  178. package/dist/test/scenario-inference.test.d.ts +0 -2
  179. package/dist/test/scenario-inference.test.d.ts.map +0 -1
  180. package/dist/test/scenario-inference.test.js +0 -73
  181. package/dist/test/stage-context.test.d.ts +0 -2
  182. package/dist/test/stage-context.test.d.ts.map +0 -1
  183. package/dist/test/stage-context.test.js +0 -395
  184. package/dist/test/testcase-generator.test.d.ts +0 -2
  185. package/dist/test/testcase-generator.test.d.ts.map +0 -1
  186. package/dist/test/testcase-generator.test.js +0 -276
  187. package/dist/test/testcase-skeleton.test.d.ts +0 -2
  188. package/dist/test/testcase-skeleton.test.d.ts.map +0 -1
  189. package/dist/test/testcase-skeleton.test.js +0 -185
  190. package/dist/test/testcase-spec-assembly.test.d.ts +0 -2
  191. package/dist/test/testcase-spec-assembly.test.d.ts.map +0 -1
  192. package/dist/test/testcase-spec-assembly.test.js +0 -105
  193. package/dist/test/util/credentials.test.d.ts +0 -2
  194. package/dist/test/util/credentials.test.d.ts.map +0 -1
  195. package/dist/test/util/credentials.test.js +0 -64
  196. package/dist/test/util/i18n-testcase.test.d.ts +0 -2
  197. package/dist/test/util/i18n-testcase.test.d.ts.map +0 -1
  198. package/dist/test/util/i18n-testcase.test.js +0 -119
  199. package/dist/test/util/softlink.test.d.ts +0 -2
  200. package/dist/test/util/softlink.test.d.ts.map +0 -1
  201. package/dist/test/util/softlink.test.js +0 -82
  202. package/dist/test/validation/credentials.test.d.ts +0 -2
  203. package/dist/test/validation/credentials.test.d.ts.map +0 -1
  204. package/dist/test/validation/credentials.test.js +0 -72
  205. package/dist/test/validation/projects-index.test.d.ts +0 -2
  206. package/dist/test/validation/projects-index.test.d.ts.map +0 -1
  207. package/dist/test/validation/projects-index.test.js +0 -48
  208. package/dist/test/validation/testcase.test.d.ts +0 -2
  209. package/dist/test/validation/testcase.test.d.ts.map +0 -1
  210. package/dist/test/validation/testcase.test.js +0 -129
  211. package/dist/vitest.config.d.ts +0 -3
  212. package/dist/vitest.config.d.ts.map +0 -1
  213. package/dist/vitest.config.js +0 -7
@@ -1,31 +0,0 @@
1
- import { dirname, join, resolve } from 'node:path';
2
- import { fileURLToPath } from 'node:url';
3
- import { describe, expect, it } from 'vitest';
4
- import { runProbeAndCoverage } from '../../src/cli/probe.js';
5
- const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../../../..');
6
- const knowledgeRoot = join(repoRoot, 'knowledge-project/epoint-web-v9.5.2');
7
- const requirementDir = join(knowledgeRoot, '_docs/014-测试需求10-20260622');
8
- const codeListPath = join(requirementDir, 'code_list.md');
9
- const webappDir = join(knowledgeRoot, 'src/main/webapp/perpage/zwplace');
10
- const javaActionsDir = join(knowledgeRoot, 'src/main/java/com/epoint/zwplace/action');
11
- describe('runProbeAndCoverage spec fallback', () => {
12
- it('在 code_list 缺少 spec.yaml 与 module-hints 时,仍能基于 HTML/Java 生成可审阅 testcase', () => {
13
- const result = runProbeAndCoverage({
14
- moduleId: 'zwplace',
15
- codeListPath,
16
- docsDir: requirementDir,
17
- webappDir,
18
- javaActionsDir,
19
- scenario: 'crud.single-page',
20
- });
21
- expect(result.contract.dataKey.field).toBe('placename');
22
- expect(result.contract.searchConditions?.[0]?.field).toBe('placename');
23
- // 推理 hints 流入后,dataKey 已解析为 placename;unresolvedSlots 只剩非 dataKey 项。
24
- expect(result.contract.unresolvedSlots.map((slot) => slot.slotId)).not.toContain('dataKey.selectedField');
25
- expect(result.testcaseV2.moduleId).toBe('zwplace');
26
- expect(result.testcaseV2.cases).toHaveLength(4);
27
- // Task 3: inference trace 随 probe 返回。
28
- expect(result.inferenceTrace.schemaVersion).toBe('inference-trace/v1');
29
- expect(result.inferenceTrace.moduleHintsInference.dataKey.selectedField).toBe('placename');
30
- });
31
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=run.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"run.test.d.ts","sourceRoot":"","sources":["../../../test/cli/run.test.ts"],"names":[],"mappings":""}
@@ -1,149 +0,0 @@
1
- /**
2
- * run.ts 单元测试(REQ-CHAIN-02 / REQ-SPEC-01)。
3
- *
4
- * 覆盖:
5
- * - parseRunArgs 缺参 / 非法 / --headless 默认 true / --headless false 关闭无头;
6
- * - runRun:
7
- * · v1 spec → 追加 AI 段产 v2(含 AI_APPEND_MARKER);
8
- * · 已有 AI 追加段时重跑覆盖(幂等);
9
- * · planned 为空时擦掉历史追加段(幂等收敛);
10
- * · 不存在 spec 时抛错;
11
- * · 报告目录基于 projectDir 推导(ISO 时间戳)。
12
- */
13
- import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
14
- import os from 'node:os';
15
- import path from 'node:path';
16
- import { beforeEach, describe, expect, it } from 'vitest';
17
- import { AI_APPEND_MARKER, parseRunArgs, runRun } from '../../src/cli/run.js';
18
- let tmpRoot;
19
- beforeEach(() => {
20
- tmpRoot = mkdtempSync(path.join(os.tmpdir(), 'ep-stage-run-'));
21
- });
22
- function makeTestcase(plannedCount = 1) {
23
- const uncoveredCandidates = Array.from({ length: plannedCount }, (_, i) => ({
24
- actionId: `gap-${i}`,
25
- label: `导出 CSV ${i}`,
26
- status: 'planned',
27
- requiredRole: 'admin',
28
- businessIntent: `导出列表 ${i}`,
29
- assertionExpectation: `下载完成 ${i}`,
30
- evidence: ['contract=crud-business-module/v1'],
31
- }));
32
- return {
33
- schemaVersion: 'v2',
34
- scenario: 'crud.single-page',
35
- moduleId: 'zwplace',
36
- moduleName: '场所窗口信息管理',
37
- menuPath: '业务管理 > 场所',
38
- requiredSystems: [{ url: '' }],
39
- requiredRoles: [{ role: 'admin' }],
40
- coveredActions: [],
41
- uncoveredCandidates,
42
- cases: [
43
- {
44
- caseId: 'zwplace.create',
45
- title: '新增',
46
- scenario: 'crud.single-page',
47
- reviewStatus: 'needs_review',
48
- requiredRole: 'admin',
49
- evidence: ['contract=crud-business-module/v1'],
50
- steps: [],
51
- assertions: [],
52
- unresolved: [],
53
- },
54
- ],
55
- reasoningSummary: {
56
- conclusion: '',
57
- evidenceChain: [],
58
- alternatives: [],
59
- confidence: 'medium',
60
- risks: [],
61
- needsHumanReview: true,
62
- },
63
- };
64
- }
65
- function writeFixtures(opts) {
66
- const dir = path.join(tmpRoot, 'req');
67
- mkdirSync(dir, { recursive: true });
68
- const specPath = path.join(dir, 'crud.spec.ts');
69
- const testcasePath = path.join(dir, 'testcase.json5');
70
- writeFileSync(specPath, opts.specBody, 'utf8');
71
- writeFileSync(testcasePath, JSON.stringify(makeTestcase(opts.plannedCount ?? 1)), 'utf8');
72
- return { specPath, testcasePath };
73
- }
74
- describe('parseRunArgs', () => {
75
- it('缺 --spec 时抛错', () => {
76
- expect(() => parseRunArgs(['--testcase', '/x'])).toThrow(/缺少 --spec/);
77
- });
78
- it('缺 --testcase 时抛错', () => {
79
- expect(() => parseRunArgs(['--spec', '/x'])).toThrow(/缺少 --testcase/);
80
- });
81
- it('参数对非法时抛错', () => {
82
- expect(() => parseRunArgs(['--spec'])).toThrow(/无效参数对/);
83
- });
84
- it('--headless 默认 true', () => {
85
- const args = parseRunArgs(['--spec', '/s', '--testcase', '/t']);
86
- expect(args.headless).toBe(true);
87
- });
88
- it('--headless false 关闭无头', () => {
89
- const args = parseRunArgs(['--spec', '/s', '--testcase', '/t', '--headless', 'false']);
90
- expect(args.headless).toBe(false);
91
- });
92
- it('--project-dir 透传', () => {
93
- const args = parseRunArgs(['--spec', '/s', '--testcase', '/t', '--project-dir', '/p']);
94
- expect(args.projectDir).toBe('/p');
95
- });
96
- });
97
- describe('runRun', () => {
98
- it('v1 spec 追加 AI 段产 v2', async () => {
99
- const v1Body = '// === 胶水模式生成 ===\nconst x = 1;\n';
100
- const { specPath, testcasePath } = writeFixtures({ specBody: v1Body });
101
- const result = await runRun({
102
- spec: specPath,
103
- testcase: testcasePath,
104
- headless: true,
105
- projectDir: tmpRoot,
106
- });
107
- expect(result.version).toBe('v1-with-planned-stubs');
108
- const body = readFileSync(specPath, 'utf8');
109
- expect(body).toContain(AI_APPEND_MARKER);
110
- expect(body).toMatch(/test\("导出 CSV 0"/);
111
- expect(body).toMatch(/requiredRole: admin/);
112
- expect(result.reportDir.startsWith(path.join(tmpRoot, 'e2e/glue-code-mvp/glue-report'))).toBe(true);
113
- });
114
- it('已有 AI 追加段时重跑覆盖(幂等)', async () => {
115
- const v1Body = '// === 胶水模式生成 ===\nconst x = 1;\n';
116
- const oldAppended = `\n\n${AI_APPEND_MARKER}\ntest("旧追加段", async () => {});\n`;
117
- const { specPath, testcasePath } = writeFixtures({ specBody: v1Body + oldAppended });
118
- await runRun({ spec: specPath, testcase: testcasePath, headless: true });
119
- const body = readFileSync(specPath, 'utf8');
120
- // 旧追加段被擦掉,只剩当次推理结果
121
- expect(body).not.toMatch(/旧追加段/);
122
- // 标记只出现一次(重跑不叠加)
123
- expect(body.match(/AI 钻探追加/g)?.length).toBe(1);
124
- expect(body).toMatch(/test\("导出 CSV 0"/);
125
- });
126
- it('planned 为空时擦掉历史追加段(幂等收敛)', async () => {
127
- const v1Body = '// === 胶水模式生成 ===\nconst x = 1;';
128
- const oldAppended = `\n\n${AI_APPEND_MARKER}\ntest("旧", async () => {});\n`;
129
- const { specPath, testcasePath } = writeFixtures({
130
- specBody: v1Body + oldAppended,
131
- plannedCount: 0,
132
- });
133
- await runRun({ spec: specPath, testcase: testcasePath, headless: true });
134
- const body = readFileSync(specPath, 'utf8');
135
- expect(body).not.toContain(AI_APPEND_MARKER);
136
- expect(body).not.toMatch(/旧追加段|旧/);
137
- });
138
- it('spec 不存在时抛错', async () => {
139
- const dir = path.join(tmpRoot, 'r2');
140
- mkdirSync(dir, { recursive: true });
141
- const testcasePath = path.join(dir, 'testcase.json5');
142
- writeFileSync(testcasePath, JSON.stringify(makeTestcase()), 'utf8');
143
- await expect(runRun({
144
- spec: path.join(dir, 'missing.spec.ts'),
145
- testcase: testcasePath,
146
- headless: true,
147
- })).rejects.toThrow(/spec 不存在/);
148
- });
149
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=spec-boundary.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"spec-boundary.test.d.ts","sourceRoot":"","sources":["../../../test/cli/spec-boundary.test.ts"],"names":[],"mappings":""}
@@ -1,198 +0,0 @@
1
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import { join } from 'node:path';
4
- import JSON5 from 'json5';
5
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
6
- // 边界测试目的:验证 runSpec 只消费 testcase.json5 + contract.json,不消费 inference-trace.json。
7
- // 因此 mock 掉 generateStageSkeletonCrudSpec,隔离骨架生成器的实现细节(避免 fixture 不完整导致崩溃)。
8
- vi.mock('../../src/generators/stage-skeleton-script.js', () => ({
9
- generateStageSkeletonCrudSpec: vi.fn(() => '// skeleton stub\n'),
10
- }));
11
- import { generateStageSkeletonCrudSpec } from '../../src/generators/stage-skeleton-script.js';
12
- import { runSpec } from '../../src/cli/spec.js';
13
- /** 构造一份最小可用的 testcase v2 对象(用例与未覆盖项均为空 → 空数组满足门禁)。 */
14
- function makeBaseTestcase(overrides = {}) {
15
- return {
16
- schemaVersion: 'v2',
17
- scenario: 'crud.single-page',
18
- moduleId: 'm1',
19
- moduleName: '模块',
20
- menuPath: '一级>二级',
21
- requiredSystems: [{ url: '', systemName: undefined }],
22
- requiredRoles: [{ role: 'admin', hint: undefined }],
23
- coveredActions: [],
24
- uncoveredCandidates: [],
25
- cases: [],
26
- reasoningSummary: {
27
- conclusion: 'ok',
28
- evidenceChain: [],
29
- alternatives: [],
30
- confidence: 'high',
31
- risks: [],
32
- needsHumanReview: false,
33
- },
34
- ...overrides,
35
- };
36
- }
37
- /** 构造一份最小 contract(mock 了生成器,结构不需 skeleton 完备)。 */
38
- function makeBaseContract(overrides = {}) {
39
- return {
40
- contractVersion: 'crud-business-module/v1',
41
- module: { id: 'm1', label: '模块' },
42
- pages: { list: { pageId: 'list', role: 'list', title: '列表', fields: [], buttons: [], grids: [] } },
43
- dataKey: { field: 'name', label: '名称', availability: 'human_required', generationStrategy: 'human_required', sources: [] },
44
- searchConditions: [],
45
- flows: {},
46
- assertions: [],
47
- businessRules: [],
48
- agentInferredWorkflows: [],
49
- unresolvedSlots: [],
50
- ...overrides,
51
- };
52
- }
53
- describe('runSpec input boundary', () => {
54
- let tmpDir = '';
55
- beforeEach(() => {
56
- vi.mocked(generateStageSkeletonCrudSpec).mockClear();
57
- });
58
- afterEach(() => {
59
- if (tmpDir)
60
- rmSync(tmpDir, { recursive: true, force: true });
61
- tmpDir = '';
62
- });
63
- /** 在 tmpDir/case 下铺好 testcase + contract 文件,返回路径句柄。 */
64
- function setupFixture(testcase, contract, opts) {
65
- tmpDir = mkdtempSync(join(tmpdir(), 'ep-stage-spec-boundary-'));
66
- const testsDir = join(tmpDir, 'case');
67
- mkdirSync(join(testsDir, '.ep-stage'), { recursive: true });
68
- const testcasePath = join(testsDir, 'testcase.json5');
69
- const specOut = join(testsDir, 'crud.spec.ts');
70
- writeFileSync(testcasePath, JSON5.stringify(testcase, null, 2), 'utf8');
71
- writeFileSync(join(testsDir, '.ep-stage', 'contract.json'), JSON.stringify(contract, null, 2), 'utf8');
72
- if (opts?.writeTrace) {
73
- writeFileSync(join(testsDir, '.ep-stage', 'inference-trace.json'), '{"broken": true}\n', 'utf8');
74
- }
75
- return { testcasePath, specOut, testsDir };
76
- }
77
- it('不依赖 inference-trace.json,只消费 testcase.json5 与 contract.json(REQ-INFER-008)', () => {
78
- // 写一份 trace 后立即删除,断言 runSpec 不依赖它
79
- const { testcasePath, specOut, testsDir } = setupFixture(makeBaseTestcase(), makeBaseContract(), { writeTrace: true });
80
- rmSync(join(testsDir, '.ep-stage', 'inference-trace.json'), { force: true });
81
- const result = runSpec({ testcase: testcasePath, out: specOut });
82
- expect(result.specPath).toBe(specOut);
83
- expect(generateStageSkeletonCrudSpec).toHaveBeenCalledTimes(1);
84
- });
85
- it('缺失 contract.json 时抛中文错误,引导先跑 testcase(REQ-INFER-006)', () => {
86
- const { testcasePath, specOut } = setupFixture(makeBaseTestcase(), makeBaseContract());
87
- // 删掉 contract.json
88
- rmSync(join(tmpDir, 'case', '.ep-stage', 'contract.json'));
89
- expect(() => runSpec({ testcase: testcasePath, out: specOut })).toThrow(/contract\.json/);
90
- expect(generateStageSkeletonCrudSpec).not.toHaveBeenCalled();
91
- });
92
- it('testcase 存在 needs_review 用例时阻断 spec 组装(REQ-INFER-007)', () => {
93
- const testcase = makeBaseTestcase({
94
- cases: [
95
- {
96
- caseId: 'c1',
97
- title: 't',
98
- scenario: 's',
99
- reviewStatus: 'needs_review',
100
- requiredRole: 'admin',
101
- evidence: [],
102
- steps: [],
103
- assertions: [],
104
- unresolved: [],
105
- },
106
- ],
107
- });
108
- const { testcasePath, specOut } = setupFixture(testcase, makeBaseContract());
109
- expect(() => runSpec({ testcase: testcasePath, out: specOut })).toThrow(/未确认用例.*needs_review\/blocked/);
110
- expect(generateStageSkeletonCrudSpec).not.toHaveBeenCalled();
111
- });
112
- it('testcase 存在 blocked 用例时阻断 spec 组装(REQ-INFER-007)', () => {
113
- const testcase = makeBaseTestcase({
114
- cases: [
115
- {
116
- caseId: 'c1',
117
- title: 't',
118
- scenario: 's',
119
- reviewStatus: 'blocked',
120
- requiredRole: 'admin',
121
- evidence: [],
122
- steps: [],
123
- assertions: [],
124
- unresolved: [],
125
- },
126
- ],
127
- });
128
- const { testcasePath, specOut } = setupFixture(testcase, makeBaseContract());
129
- expect(() => runSpec({ testcase: testcasePath, out: specOut })).toThrow(/未确认用例.*needs_review\/blocked/);
130
- });
131
- it('testcase 存在 unresolved 未覆盖项时阻断 spec 组装(REQ-INFER-007)', () => {
132
- const testcase = makeBaseTestcase({
133
- uncoveredCandidates: [
134
- {
135
- actionId: 'a1',
136
- label: 'l',
137
- status: 'unresolved',
138
- requiredRole: 'admin',
139
- businessIntent: 'b',
140
- assertionExpectation: 'e',
141
- evidence: [],
142
- },
143
- ],
144
- });
145
- const { testcasePath, specOut } = setupFixture(testcase, makeBaseContract());
146
- expect(() => runSpec({ testcase: testcasePath, out: specOut })).toThrow(/candidate\/unresolved 未覆盖项/);
147
- expect(generateStageSkeletonCrudSpec).not.toHaveBeenCalled();
148
- });
149
- it('testcase 存在 candidate 未覆盖项时阻断 spec 组装(REQ-INFER-007)', () => {
150
- const testcase = makeBaseTestcase({
151
- uncoveredCandidates: [
152
- {
153
- actionId: 'a1',
154
- label: 'l',
155
- status: 'candidate',
156
- requiredRole: 'admin',
157
- businessIntent: 'b',
158
- assertionExpectation: 'e',
159
- evidence: [],
160
- },
161
- ],
162
- });
163
- const { testcasePath, specOut } = setupFixture(testcase, makeBaseContract());
164
- expect(() => runSpec({ testcase: testcasePath, out: specOut })).toThrow(/candidate\/unresolved 未覆盖项/);
165
- });
166
- it('全部 confirmed + planned/resolved 时正常组装(门禁 happy path)', () => {
167
- const testcase = makeBaseTestcase({
168
- cases: [
169
- {
170
- caseId: 'c1',
171
- title: 't',
172
- scenario: 's',
173
- reviewStatus: 'confirmed',
174
- requiredRole: 'admin',
175
- evidence: [],
176
- steps: [],
177
- assertions: [],
178
- unresolved: [],
179
- },
180
- ],
181
- uncoveredCandidates: [
182
- {
183
- actionId: 'a1',
184
- label: 'l',
185
- status: 'planned',
186
- requiredRole: 'admin',
187
- businessIntent: 'b',
188
- assertionExpectation: 'e',
189
- evidence: [],
190
- },
191
- ],
192
- });
193
- const { testcasePath, specOut } = setupFixture(testcase, makeBaseContract());
194
- const result = runSpec({ testcase: testcasePath, out: specOut });
195
- expect(result.specPath).toBe(specOut);
196
- expect(generateStageSkeletonCrudSpec).toHaveBeenCalledTimes(1);
197
- });
198
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=spec.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"spec.test.d.ts","sourceRoot":"","sources":["../../../test/cli/spec.test.ts"],"names":[],"mappings":""}
@@ -1,196 +0,0 @@
1
- /**
2
- * spec.ts 单元测试(REQ-SPEC-01)。
3
- *
4
- * 覆盖:
5
- * - parseSpecArgs 缺参 / 非法 / -- 分隔符
6
- * - runSpec:
7
- * · 缺 contract.json 时报错(引导用户先跑 testcase 子命令);
8
- * · 正确装配 v1 .spec.ts(含 SKELETON_MARKER_LINE 头部标记 + skeleton 主体);
9
- * · testcase.json5 不通过 validateTestcase 时抛错(透传 v2 schema 校验)。
10
- */
11
- import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
12
- import os from 'node:os';
13
- import path from 'node:path';
14
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
15
- import { parseSpecArgs, runSpec } from '../../src/cli/spec.js';
16
- let tmpRoot;
17
- beforeEach(() => {
18
- tmpRoot = mkdtempSync(path.join(os.tmpdir(), 'ep-stage-spec-'));
19
- });
20
- afterEach(() => {
21
- // 留 tmpdir 给宿主清理,避免删 fixture 时误命中其他测试。
22
- });
23
- function createContract() {
24
- return {
25
- contractVersion: 'crud-business-module/v1',
26
- module: { id: 'zwplace', label: '场所窗口信息管理' },
27
- pages: {
28
- list: {
29
- role: 'list',
30
- pageId: 'gxhzwplacelist',
31
- title: '场所窗口信息列表',
32
- iframeSrcKeyword: 'gxhzwplacelist',
33
- fields: [
34
- { name: 'windowname', label: '窗口名称', controlType: 'minitext', required: false, availability: 'auto_extractable', sources: [] },
35
- ],
36
- buttons: [],
37
- grids: [],
38
- dialogs: [],
39
- sources: [],
40
- },
41
- add: {
42
- role: 'add',
43
- pageId: 'gxhzwplaceadd',
44
- title: '新增场所窗口',
45
- iframeSrcKeyword: 'gxhzwplaceadd',
46
- fields: [
47
- { name: 'windowname', label: '窗口名称', controlType: 'minitext', required: true, availability: 'auto_extractable', sources: [] },
48
- ],
49
- buttons: [],
50
- grids: [],
51
- dialogs: [],
52
- sources: [],
53
- },
54
- edit: {
55
- role: 'edit',
56
- pageId: 'gxhzwplaceedit',
57
- title: '修改场所窗口',
58
- iframeSrcKeyword: 'gxhzwplaceedit',
59
- fields: [
60
- { name: 'windowname', label: '窗口名称', controlType: 'minitext', required: true, availability: 'auto_extractable', sources: [] },
61
- ],
62
- buttons: [],
63
- grids: [],
64
- dialogs: [],
65
- sources: [],
66
- },
67
- },
68
- dataKey: {
69
- field: 'windowname',
70
- label: '窗口名称',
71
- availability: 'derivable',
72
- generationStrategy: 'timestamp_prefix',
73
- sources: [],
74
- },
75
- searchConditions: [
76
- {
77
- field: 'windowname',
78
- label: '窗口名称',
79
- component: 'input',
80
- seedValue: '自动化测试窗口',
81
- generationStrategy: 'timestamp_prefix',
82
- sources: [],
83
- },
84
- ],
85
- flows: {
86
- create: {
87
- entryButton: { pageRole: 'list', label: '新增', sources: [] },
88
- targetPageId: 'gxhzwplaceadd',
89
- saveButton: { pageRole: 'add', label: '保存并关闭', sources: [] },
90
- overrideFields: [],
91
- },
92
- search: {
93
- searchField: { pageRole: 'list', field: 'windowname', label: '窗口名称', sources: [] },
94
- submitControl: { pageRole: 'list', label: '查询', sources: [] },
95
- resultGrid: 'datagrid',
96
- },
97
- update: {
98
- entryAction: { pageRole: 'list', label: '修改', sources: [] },
99
- targetPageId: 'gxhzwplaceedit',
100
- updateField: { pageRole: 'edit', field: 'windowname', label: '窗口名称', sources: [] },
101
- saveButton: { pageRole: 'edit', label: '保存并关闭', sources: [] },
102
- },
103
- delete: {
104
- entryButton: { pageRole: 'list', label: '删除', sources: [] },
105
- expectedOutcome: 'success_delete',
106
- confirmControl: { pageRole: 'list', label: '确定', sources: [] },
107
- },
108
- },
109
- assertions: [],
110
- businessRules: [],
111
- unresolvedSlots: [],
112
- };
113
- }
114
- function createTestcase() {
115
- return {
116
- schemaVersion: 'v2',
117
- scenario: 'crud.single-page',
118
- moduleId: 'zwplace',
119
- moduleName: '场所窗口信息管理',
120
- menuPath: '业务管理 > 场所窗口',
121
- requiredSystems: [{ url: '' }],
122
- requiredRoles: [{ role: 'admin' }],
123
- coveredActions: [
124
- { actionId: 'create', kind: '新增', evidence: ['contract=crud-business-module/v1'] },
125
- ],
126
- uncoveredCandidates: [],
127
- cases: [
128
- {
129
- caseId: 'zwplace.create',
130
- title: '新增',
131
- scenario: 'crud.single-page',
132
- reviewStatus: 'confirmed',
133
- requiredRole: 'admin',
134
- evidence: ['contract=crud-business-module/v1'],
135
- steps: [],
136
- assertions: [],
137
- unresolved: [],
138
- },
139
- ],
140
- reasoningSummary: {
141
- conclusion: '',
142
- evidenceChain: [],
143
- alternatives: [],
144
- confidence: 'medium',
145
- risks: [],
146
- needsHumanReview: true,
147
- },
148
- };
149
- }
150
- function writeFixtures() {
151
- const testsDir = path.join(tmpRoot, 'requirement-X');
152
- mkdirSync(path.join(testsDir, '.ep-stage'), { recursive: true });
153
- const testcasePath = path.join(testsDir, 'testcase.json5');
154
- const contractPath = path.join(testsDir, '.ep-stage', 'contract.json');
155
- writeFileSync(testcasePath, JSON.stringify(createTestcase()), 'utf8');
156
- writeFileSync(contractPath, JSON.stringify(createContract()), 'utf8');
157
- return { testcasePath, specOut: path.join(testsDir, 'crud.spec.ts') };
158
- }
159
- describe('parseSpecArgs', () => {
160
- it('缺 --testcase 时抛错', () => {
161
- expect(() => parseSpecArgs(['--out', '/x'])).toThrow(/缺少 --testcase/);
162
- });
163
- it('缺 --out 时抛错', () => {
164
- expect(() => parseSpecArgs(['--testcase', '/x'])).toThrow(/缺少 --out/);
165
- });
166
- it('参数对非法时抛错', () => {
167
- expect(() => parseSpecArgs(['--testcase'])).toThrow(/无效参数对/);
168
- });
169
- it('-- 分隔符被忽略', () => {
170
- const args = parseSpecArgs(['--', '--testcase', '/a', '--out', '/b']);
171
- expect(args).toEqual({ testcase: '/a', out: '/b' });
172
- });
173
- });
174
- describe('runSpec', () => {
175
- it('正常装配 v1 .spec.ts(含 SKELETON_MARKER_LINE)', () => {
176
- const { testcasePath, specOut } = writeFixtures();
177
- const result = runSpec({ testcase: testcasePath, out: specOut });
178
- expect(result).toEqual({ specPath: specOut, version: 'v1' });
179
- const body = readFileSync(specOut, 'utf8');
180
- expect(body).toMatch(/^\/\/ === 胶水模式生成(基于 contract \+ coveredActions)===/);
181
- // skeleton 主体(来自 generateStageSkeletonCrudSpec)应含 Playwright import 或 test 入口
182
- expect(body.length).toBeGreaterThan(100);
183
- });
184
- it('缺 .ep-stage/contract.json 时抛错引导', () => {
185
- const testsDir = path.join(tmpRoot, 'requirement-Y');
186
- mkdirSync(testsDir, { recursive: true });
187
- const testcasePath = path.join(testsDir, 'testcase.json5');
188
- writeFileSync(testcasePath, JSON.stringify(createTestcase()), 'utf8');
189
- expect(() => runSpec({ testcase: testcasePath, out: path.join(testsDir, 'out.spec.ts') })).toThrow(/未找到.*contract\.json/);
190
- });
191
- it('testcase.json5 schemaVersion 不是 v2 时由 validateTestcase 抛错', () => {
192
- const { testcasePath, specOut } = writeFixtures();
193
- writeFileSync(testcasePath, JSON.stringify({ schemaVersion: 'v1' }), 'utf8');
194
- expect(() => runSpec({ testcase: testcasePath, out: specOut })).toThrow(/schemaVersion 不是 v2/);
195
- });
196
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=code-list.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"code-list.test.d.ts","sourceRoot":"","sources":["../../test/code-list.test.ts"],"names":[],"mappings":""}
@@ -1,39 +0,0 @@
1
- import { mkdtempSync, writeFileSync } from 'node:fs';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
- import { describe, expect, it } from 'vitest';
5
- import { extractCodeListSummary } from '../src/index.js';
6
- /**
7
- * code_list.md 解析器单元测试。
8
- *
9
- * 重点覆盖菜单字段的人工读写约定:
10
- * - 中文键「菜单」为 code_list.md 人工首选(与「整理目录」同风格),支持 blockquote 与裸行;
11
- * - 英文键 menu / menuPath 向后兼容(编码内部字段名仍为 menu)。
12
- */
13
- describe('code_list.md 菜单字段解析', () => {
14
- /** 在临时目录写一份最小 code_list.md 并返回其解析摘要。 */
15
- function summarizeMenu(lines) {
16
- const root = mkdtempSync(path.join(os.tmpdir(), 'ep-stage-codelist-'));
17
- const codeListPath = path.join(root, 'code_list.md');
18
- writeFileSync(codeListPath, lines.join('\n'), 'utf8');
19
- return extractCodeListSummary(codeListPath).menu;
20
- }
21
- it('中文键「菜单」blockquote 形式(无反引号)', () => {
22
- expect(summarizeMenu(['# 场所窗口信息管理代码清单', '> 菜单:场所窗口信息管理>场所窗口信息列表'])).toBe('场所窗口信息管理>场所窗口信息列表');
23
- });
24
- it('中文键「菜单」blockquote 形式(反引号包裹)', () => {
25
- expect(summarizeMenu(['# 场所窗口信息管理代码清单', '> 菜单:`场所窗口信息管理>场所窗口信息列表`'])).toBe('场所窗口信息管理>场所窗口信息列表');
26
- });
27
- it('中文键「菜单」裸行形式', () => {
28
- expect(summarizeMenu(['# 场所窗口信息管理代码清单', '菜单: 场所窗口信息管理>场所窗口信息列表'])).toBe('场所窗口信息管理>场所窗口信息列表');
29
- });
30
- it('英文键 menu 向后兼容', () => {
31
- expect(summarizeMenu(['# 场所窗口信息管理代码清单', 'menu: 场所窗口信息管理>场所窗口信息列表'])).toBe('场所窗口信息管理>场所窗口信息列表');
32
- });
33
- it('英文键 menuPath 向后兼容', () => {
34
- expect(summarizeMenu(['# 场所窗口信息管理代码清单', 'menuPath: 场所窗口信息管理>场所窗口信息列表'])).toBe('场所窗口信息管理>场所窗口信息列表');
35
- });
36
- it('未声明菜单时返回 undefined', () => {
37
- expect(summarizeMenu(['# 场所窗口信息管理代码清单'])).toBeUndefined();
38
- });
39
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=crud-contract.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"crud-contract.test.d.ts","sourceRoot":"","sources":["../../test/crud-contract.test.ts"],"names":[],"mappings":""}