@epoint-testtech/ep-stage-skill 0.0.4-alpha.0 → 0.0.5
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 -186
- package/codex-skill/ep-stage/glue-generate-testcase/SKILL.md +275 -199
- 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 -112
- package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-tpl.md +72 -0
- package/codex-skill/ep-stage/glue-run-test/SKILL.md +292 -249
- package/codex-skill/ep-stage/glue-run-test/references/ai-runtime-loop.md +30 -0
- package/codex-skill/ep-stage/glue-run-test/references/crud-pipeline.md +132 -145
- package/codex-skill/ep-stage/glue-run-test/references/gap-review-protocol.md +44 -43
- 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 +166 -0
- package/codex-skill/ep-stage/glue-run-test/scripts/extract-page-elements-eval.mjs +233 -0
- package/codex-skill/ep-stage/glue-run-test/scripts/generate-crud-spec.mjs +149 -149
- package/codex-skill/ep-stage/page-element-scan/SKILL.md +91 -0
- package/codex-skill/ep-stage/page-element-scan/evals/evals.json +37 -0
- package/codex-skill/ep-stage/page-element-scan/evals/fixtures/sample-list-page.html +85 -0
- package/codex-skill/ep-stage/page-element-scan/references/pagination-controls.md +153 -0
- package/codex-skill/ep-stage/page-element-scan/scripts/extract-page-elements-eval.mjs +233 -0
- package/codex-skill/ep-stage/page-element-scan/scripts/extract-page-elements.mjs +298 -0
- package/codex-skill/ep-stage/page-element-scan/scripts/scan-page.mjs +749 -0
- package/codex-skill/ep-stage/recording-to-glue/SKILL.md +28 -28
- package/codex-skill/ep-stage/scripts/validate-skill.mjs +98 -95
- package/dist/src/cli/generate-testcase.d.ts +18 -51
- package/dist/src/cli/generate-testcase.d.ts.map +1 -1
- package/dist/src/cli/generate-testcase.js +127 -131
- package/dist/src/cli/index.d.ts +11 -2
- package/dist/src/cli/index.d.ts.map +1 -1
- package/dist/src/cli/index.js +49 -16
- package/dist/src/cli/probe.d.ts +2 -0
- package/dist/src/cli/probe.d.ts.map +1 -1
- package/dist/src/cli/probe.js +239 -13
- package/dist/src/cli/run.d.ts +136 -29
- package/dist/src/cli/run.d.ts.map +1 -1
- package/dist/src/cli/run.js +494 -65
- package/dist/src/cli/spec.d.ts +18 -11
- package/dist/src/cli/spec.d.ts.map +1 -1
- package/dist/src/cli/spec.js +74 -20
- package/dist/src/contracts/crud-business-module.d.ts +6 -0
- package/dist/src/contracts/crud-business-module.d.ts.map +1 -1
- package/dist/src/contracts/glue-contract-v1.d.ts +98 -0
- package/dist/src/contracts/glue-contract-v1.d.ts.map +1 -0
- package/dist/src/contracts/glue-contract-v1.js +9 -0
- package/dist/src/contracts/inference-trace.d.ts +223 -0
- package/dist/src/contracts/inference-trace.d.ts.map +1 -0
- package/dist/src/contracts/inference-trace.js +12 -0
- package/dist/src/contracts/observable-chain.d.ts +4 -0
- package/dist/src/contracts/observable-chain.d.ts.map +1 -1
- package/dist/src/extractors/code-list-json5.d.ts +192 -0
- package/dist/src/extractors/code-list-json5.d.ts.map +1 -0
- package/dist/src/extractors/code-list-json5.js +91 -0
- package/dist/src/extractors/code-list.d.ts +3 -3
- package/dist/src/extractors/code-list.d.ts.map +1 -1
- package/dist/src/extractors/code-list.js +46 -11
- package/dist/src/extractors/html-page.d.ts +9 -0
- package/dist/src/extractors/html-page.d.ts.map +1 -1
- package/dist/src/extractors/html-page.js +26 -2
- package/dist/src/extractors/prd-extractor.d.ts +70 -0
- package/dist/src/extractors/prd-extractor.d.ts.map +1 -0
- package/dist/src/extractors/prd-extractor.js +248 -0
- package/dist/src/generators/agent-inferred-workflow-script.js +66 -66
- package/dist/src/generators/stage-skeleton-script.d.ts +87 -20
- package/dist/src/generators/stage-skeleton-script.d.ts.map +1 -1
- package/dist/src/generators/stage-skeleton-script.js +656 -238
- package/dist/src/index.d.ts +7 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -1
- package/dist/src/inference/module-hints-inference.d.ts +30 -0
- package/dist/src/inference/module-hints-inference.d.ts.map +1 -0
- package/dist/src/inference/module-hints-inference.js +363 -0
- package/dist/src/inference/source-analysis.d.ts +26 -0
- package/dist/src/inference/source-analysis.d.ts.map +1 -0
- package/dist/src/inference/source-analysis.js +258 -0
- package/dist/src/normalizers/crud-contract.d.ts +3 -0
- package/dist/src/normalizers/crud-contract.d.ts.map +1 -1
- package/dist/src/normalizers/crud-contract.js +3 -0
- package/dist/src/runtime/ai-runtime-trace.d.ts +88 -0
- package/dist/src/runtime/ai-runtime-trace.d.ts.map +1 -0
- package/dist/src/runtime/ai-runtime-trace.js +119 -0
- package/dist/src/testcase/button-interaction-inference.d.ts +37 -0
- package/dist/src/testcase/button-interaction-inference.d.ts.map +1 -0
- package/dist/src/testcase/button-interaction-inference.js +385 -0
- package/dist/src/testcase/contract-testcase-assembler.d.ts +44 -0
- package/dist/src/testcase/contract-testcase-assembler.d.ts.map +1 -0
- package/dist/src/testcase/contract-testcase-assembler.js +806 -0
- package/dist/src/testcase/prd-button-mapper.d.ts +72 -0
- package/dist/src/testcase/prd-button-mapper.d.ts.map +1 -0
- package/dist/src/testcase/prd-button-mapper.js +148 -0
- package/dist/src/testcase/runtime-context.d.ts +9 -0
- package/dist/src/testcase/runtime-context.d.ts.map +1 -0
- package/dist/src/testcase/runtime-context.js +1 -0
- package/dist/src/testcase/test-access.d.ts +38 -0
- package/dist/src/testcase/test-access.d.ts.map +1 -0
- package/dist/src/testcase/test-access.js +106 -0
- package/dist/src/testcase/testcase-execution-fields.d.ts +10 -0
- package/dist/src/testcase/testcase-execution-fields.d.ts.map +1 -0
- package/dist/src/testcase/testcase-execution-fields.js +1 -0
- package/dist/src/testcase/testcase-generator.d.ts.map +1 -1
- package/dist/src/testcase/testcase-generator.js +6 -0
- package/dist/src/testcase/testcase-markdown.d.ts +11 -0
- package/dist/src/testcase/testcase-markdown.d.ts.map +1 -0
- package/dist/src/testcase/testcase-markdown.js +181 -0
- package/dist/src/testcase/testcase-skeleton.d.ts +4 -0
- package/dist/src/testcase/testcase-skeleton.d.ts.map +1 -1
- package/dist/src/testcase/testcase-spec-assembly.d.ts +4 -0
- package/dist/src/testcase/testcase-spec-assembly.d.ts.map +1 -1
- package/dist/src/testcase/testcase-v1-draft.d.ts +58 -0
- package/dist/src/testcase/testcase-v1-draft.d.ts.map +1 -0
- package/dist/src/testcase/testcase-v1-draft.js +13 -0
- package/dist/src/validation/contract.d.ts +14 -0
- package/dist/src/validation/contract.d.ts.map +1 -0
- package/dist/src/validation/contract.js +132 -0
- package/dist/src/validation/credentials.d.ts +7 -2
- package/dist/src/validation/credentials.d.ts.map +1 -1
- package/dist/src/validation/credentials.js +15 -4
- package/dist/src/validation/index.d.ts +2 -0
- package/dist/src/validation/index.d.ts.map +1 -1
- package/dist/src/validation/index.js +2 -0
- package/dist/src/validation/inference-trace.d.ts +13 -0
- package/dist/src/validation/inference-trace.d.ts.map +1 -0
- package/dist/src/validation/inference-trace.js +31 -0
- package/dist/src/validation/testcase.d.ts +9 -7
- package/dist/src/validation/testcase.d.ts.map +1 -1
- package/dist/src/validation/testcase.js +181 -34
- package/docs/README.md +124 -134
- package/docs/mvp-usage-guide.md +298 -298
- 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 +2 -2
- package/dist/test/cli/extract-contract.test.d.ts +0 -2
- package/dist/test/cli/extract-contract.test.d.ts.map +0 -1
- package/dist/test/cli/extract-contract.test.js +0 -32
- package/dist/test/cli/generate-testcase.test.d.ts +0 -2
- package/dist/test/cli/generate-testcase.test.d.ts.map +0 -1
- package/dist/test/cli/generate-testcase.test.js +0 -130
- package/dist/test/cli/index.test.d.ts +0 -2
- package/dist/test/cli/index.test.d.ts.map +0 -1
- package/dist/test/cli/index.test.js +0 -93
- package/dist/test/cli/run.test.d.ts +0 -2
- package/dist/test/cli/run.test.d.ts.map +0 -1
- package/dist/test/cli/run.test.js +0 -149
- package/dist/test/cli/spec.test.d.ts +0 -2
- package/dist/test/cli/spec.test.d.ts.map +0 -1
- package/dist/test/cli/spec.test.js +0 -196
- package/dist/test/crud-contract.test.d.ts +0 -2
- package/dist/test/crud-contract.test.d.ts.map +0 -1
- package/dist/test/crud-contract.test.js +0 -819
- package/dist/test/gap-inference.test.d.ts +0 -2
- package/dist/test/gap-inference.test.d.ts.map +0 -1
- package/dist/test/gap-inference.test.js +0 -597
- package/dist/test/generalization.test.d.ts +0 -2
- package/dist/test/generalization.test.d.ts.map +0 -1
- package/dist/test/generalization.test.js +0 -73
- package/dist/test/material-inventory.test.d.ts +0 -2
- package/dist/test/material-inventory.test.d.ts.map +0 -1
- package/dist/test/material-inventory.test.js +0 -141
- package/dist/test/observable-chain.test.d.ts +0 -2
- package/dist/test/observable-chain.test.d.ts.map +0 -1
- package/dist/test/observable-chain.test.js +0 -123
- package/dist/test/observable-pipeline.test.d.ts +0 -2
- package/dist/test/observable-pipeline.test.d.ts.map +0 -1
- package/dist/test/observable-pipeline.test.js +0 -461
- package/dist/test/page-structure.test.d.ts +0 -2
- package/dist/test/page-structure.test.d.ts.map +0 -1
- package/dist/test/page-structure.test.js +0 -45
- package/dist/test/scenario-inference.test.d.ts +0 -2
- package/dist/test/scenario-inference.test.d.ts.map +0 -1
- package/dist/test/scenario-inference.test.js +0 -73
- package/dist/test/stage-context.test.d.ts +0 -2
- package/dist/test/stage-context.test.d.ts.map +0 -1
- package/dist/test/stage-context.test.js +0 -395
- package/dist/test/testcase-generator.test.d.ts +0 -2
- package/dist/test/testcase-generator.test.d.ts.map +0 -1
- package/dist/test/testcase-generator.test.js +0 -276
- package/dist/test/testcase-skeleton.test.d.ts +0 -2
- package/dist/test/testcase-skeleton.test.d.ts.map +0 -1
- package/dist/test/testcase-skeleton.test.js +0 -185
- package/dist/test/testcase-spec-assembly.test.d.ts +0 -2
- package/dist/test/testcase-spec-assembly.test.d.ts.map +0 -1
- package/dist/test/testcase-spec-assembly.test.js +0 -105
- package/dist/test/util/credentials.test.d.ts +0 -2
- package/dist/test/util/credentials.test.d.ts.map +0 -1
- package/dist/test/util/credentials.test.js +0 -64
- package/dist/test/util/i18n-testcase.test.d.ts +0 -2
- package/dist/test/util/i18n-testcase.test.d.ts.map +0 -1
- package/dist/test/util/i18n-testcase.test.js +0 -119
- package/dist/test/util/softlink.test.d.ts +0 -2
- package/dist/test/util/softlink.test.d.ts.map +0 -1
- package/dist/test/util/softlink.test.js +0 -82
- package/dist/test/validation/credentials.test.d.ts +0 -2
- package/dist/test/validation/credentials.test.d.ts.map +0 -1
- package/dist/test/validation/credentials.test.js +0 -72
- package/dist/test/validation/projects-index.test.d.ts +0 -2
- package/dist/test/validation/projects-index.test.d.ts.map +0 -1
- package/dist/test/validation/projects-index.test.js +0 -48
- package/dist/test/validation/testcase.test.d.ts +0 -2
- package/dist/test/validation/testcase.test.d.ts.map +0 -1
- package/dist/test/validation/testcase.test.js +0 -129
- package/dist/vitest.config.d.ts +0 -3
- package/dist/vitest.config.d.ts.map +0 -1
- package/dist/vitest.config.js +0 -7
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRD 功能条目 → 自定义按钮 的确定性映射与模板渲染。
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* 1. 根据 code_list.implementedScope 把列表页 action='custom' 按钮映射到 PRD 功能条目。
|
|
6
|
+
* 2. 没有 implementedScope 时,使用 label↔featureName 精确/包含匹配作为兜底。
|
|
7
|
+
* 3. 将 PRD 字段渲染成中文用例 steps / assertion / businessIntent。
|
|
8
|
+
*
|
|
9
|
+
* 设计取舍:
|
|
10
|
+
* - 全程纯代码逻辑,不调用 LLM,保证幂等。
|
|
11
|
+
* - PRD 映射优先于 button-interaction-inference 的 assertion,因为 PRD 的 output/result
|
|
12
|
+
* 是业务期望的事实源;inference 的 steps 仍优先用于 UI 机械步骤。
|
|
13
|
+
* - 无法映射时返回 undefined,由调用方继续使用 inference 或通用兜底。
|
|
14
|
+
*/
|
|
15
|
+
import type { PrdFunctionEntry } from '../extractors/prd-extractor.js';
|
|
16
|
+
/** code_list 中 implementedScope 的单项。 */
|
|
17
|
+
export type ImplementedScopeItem = {
|
|
18
|
+
featureId: string;
|
|
19
|
+
featureName: string;
|
|
20
|
+
implementationStatus?: string;
|
|
21
|
+
implementedBy: string[];
|
|
22
|
+
note?: string;
|
|
23
|
+
};
|
|
24
|
+
/** 列表页自定义按钮的精简描述。 */
|
|
25
|
+
export type CustomButtonRef = {
|
|
26
|
+
label: string;
|
|
27
|
+
customAction?: string;
|
|
28
|
+
actionName?: string;
|
|
29
|
+
backendMethod?: string | null;
|
|
30
|
+
};
|
|
31
|
+
/** 映射结果。 */
|
|
32
|
+
export type PrdButtonMapping = {
|
|
33
|
+
featureId: string;
|
|
34
|
+
featureName: string;
|
|
35
|
+
matchedBy: 'implementedScope' | 'label-exact' | 'label-contains';
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* 把自定义按钮映射到 PRD 功能条目。
|
|
39
|
+
*
|
|
40
|
+
* 映射优先级:
|
|
41
|
+
* 1. implementedScope.implementedBy 命中按钮的 customAction / actionName / backendMethod。
|
|
42
|
+
* 2. 按钮 label 与 featureName 精确相等。
|
|
43
|
+
* 3. featureName 包含 label 或 label 包含 featureName(按 PRD 顺序取第一个)。
|
|
44
|
+
*
|
|
45
|
+
* @param button - 按钮元数据。
|
|
46
|
+
* @param prdFunctions - PRD 提取出的功能条目。
|
|
47
|
+
* @param implementedScope - code_list 中的实现范围(可选)。
|
|
48
|
+
* @returns 映射结果;未命中时返回 undefined。
|
|
49
|
+
*/
|
|
50
|
+
export declare function mapButtonToPrdFunction(button: CustomButtonRef, prdFunctions: PrdFunctionEntry[], implementedScope?: ImplementedScopeItem[]): PrdButtonMapping | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* 根据 PRD 功能条目渲染 businessIntent。
|
|
53
|
+
*/
|
|
54
|
+
export declare function renderPrdBusinessIntent(entry: PrdFunctionEntry, label: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* 根据 PRD 功能条目渲染执行步骤。
|
|
57
|
+
*
|
|
58
|
+
* 优先使用 entry.processingLogic;若为空则使用 entry.inputConditions。
|
|
59
|
+
*/
|
|
60
|
+
export declare function renderPrdSteps(entry: PrdFunctionEntry, label: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* 根据 PRD 功能条目渲染断言。
|
|
63
|
+
*
|
|
64
|
+
* 从 outputResult 中提取成功/失败结果,组合成选择型断言。
|
|
65
|
+
* 若 outputResult 为空,则使用 businessRules 兜底。
|
|
66
|
+
*/
|
|
67
|
+
export declare function renderPrdAssertion(entry: PrdFunctionEntry, label: string): string;
|
|
68
|
+
/**
|
|
69
|
+
* 获取 PRD 功能条目的输出结果文本(用于 trace / 测试断言)。
|
|
70
|
+
*/
|
|
71
|
+
export declare function getPrdOutputResultText(entry: PrdFunctionEntry): string;
|
|
72
|
+
//# sourceMappingURL=prd-button-mapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prd-button-mapper.d.ts","sourceRoot":"","sources":["../../../src/testcase/prd-button-mapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAEvE,wCAAwC;AACxC,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,qBAAqB;AACrB,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,YAAY;AACZ,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,kBAAkB,GAAG,aAAa,GAAG,gBAAgB,CAAC;CAClE,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,eAAe,EACvB,YAAY,EAAE,gBAAgB,EAAE,EAChC,gBAAgB,CAAC,EAAE,oBAAoB,EAAE,GACxC,gBAAgB,GAAG,SAAS,CAsB9B;AA+CD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMtF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAkB7E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBjF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAEtE"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PRD 功能条目 → 自定义按钮 的确定性映射与模板渲染。
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* 1. 根据 code_list.implementedScope 把列表页 action='custom' 按钮映射到 PRD 功能条目。
|
|
6
|
+
* 2. 没有 implementedScope 时,使用 label↔featureName 精确/包含匹配作为兜底。
|
|
7
|
+
* 3. 将 PRD 字段渲染成中文用例 steps / assertion / businessIntent。
|
|
8
|
+
*
|
|
9
|
+
* 设计取舍:
|
|
10
|
+
* - 全程纯代码逻辑,不调用 LLM,保证幂等。
|
|
11
|
+
* - PRD 映射优先于 button-interaction-inference 的 assertion,因为 PRD 的 output/result
|
|
12
|
+
* 是业务期望的事实源;inference 的 steps 仍优先用于 UI 机械步骤。
|
|
13
|
+
* - 无法映射时返回 undefined,由调用方继续使用 inference 或通用兜底。
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* 把自定义按钮映射到 PRD 功能条目。
|
|
17
|
+
*
|
|
18
|
+
* 映射优先级:
|
|
19
|
+
* 1. implementedScope.implementedBy 命中按钮的 customAction / actionName / backendMethod。
|
|
20
|
+
* 2. 按钮 label 与 featureName 精确相等。
|
|
21
|
+
* 3. featureName 包含 label 或 label 包含 featureName(按 PRD 顺序取第一个)。
|
|
22
|
+
*
|
|
23
|
+
* @param button - 按钮元数据。
|
|
24
|
+
* @param prdFunctions - PRD 提取出的功能条目。
|
|
25
|
+
* @param implementedScope - code_list 中的实现范围(可选)。
|
|
26
|
+
* @returns 映射结果;未命中时返回 undefined。
|
|
27
|
+
*/
|
|
28
|
+
export function mapButtonToPrdFunction(button, prdFunctions, implementedScope) {
|
|
29
|
+
// 1. Primary:implementedScope
|
|
30
|
+
if (implementedScope && implementedScope.length > 0) {
|
|
31
|
+
const actionKeys = collectButtonActionKeys(button);
|
|
32
|
+
for (const scope of implementedScope) {
|
|
33
|
+
for (const implementedBy of scope.implementedBy) {
|
|
34
|
+
const segments = implementedBy.split('.').map((s) => s.trim());
|
|
35
|
+
for (const segment of segments) {
|
|
36
|
+
if (segment && actionKeys.includes(segment)) {
|
|
37
|
+
return {
|
|
38
|
+
featureId: scope.featureId,
|
|
39
|
+
featureName: scope.featureName,
|
|
40
|
+
matchedBy: 'implementedScope',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// 2. Fallback:label 匹配
|
|
48
|
+
return mapByLabel(button.label, prdFunctions);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 收集可用于匹配 implementedScope 的按钮动作键。
|
|
52
|
+
*/
|
|
53
|
+
function collectButtonActionKeys(button) {
|
|
54
|
+
const keys = new Set();
|
|
55
|
+
if (button.customAction)
|
|
56
|
+
keys.add(button.customAction);
|
|
57
|
+
if (button.actionName)
|
|
58
|
+
keys.add(button.actionName);
|
|
59
|
+
if (button.backendMethod)
|
|
60
|
+
keys.add(button.backendMethod);
|
|
61
|
+
return Array.from(keys).filter(Boolean);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 按 label↔featureName 匹配。
|
|
65
|
+
*/
|
|
66
|
+
function mapByLabel(label, prdFunctions) {
|
|
67
|
+
const normalizedLabel = label.trim();
|
|
68
|
+
// 精确匹配
|
|
69
|
+
const exact = prdFunctions.find((f) => f.featureName.trim() === normalizedLabel);
|
|
70
|
+
if (exact) {
|
|
71
|
+
return { featureId: exact.featureId, featureName: exact.featureName, matchedBy: 'label-exact' };
|
|
72
|
+
}
|
|
73
|
+
// 包含匹配:featureName 包含 label
|
|
74
|
+
const contains = prdFunctions.find((f) => f.featureName.includes(normalizedLabel));
|
|
75
|
+
if (contains) {
|
|
76
|
+
return { featureId: contains.featureId, featureName: contains.featureName, matchedBy: 'label-contains' };
|
|
77
|
+
}
|
|
78
|
+
// 包含匹配:label 包含 featureName
|
|
79
|
+
const containedBy = prdFunctions.find((f) => normalizedLabel.includes(f.featureName));
|
|
80
|
+
if (containedBy) {
|
|
81
|
+
return {
|
|
82
|
+
featureId: containedBy.featureId,
|
|
83
|
+
featureName: containedBy.featureName,
|
|
84
|
+
matchedBy: 'label-contains',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* 根据 PRD 功能条目渲染 businessIntent。
|
|
91
|
+
*/
|
|
92
|
+
export function renderPrdBusinessIntent(entry, label) {
|
|
93
|
+
const description = entry.description?.trim();
|
|
94
|
+
if (description) {
|
|
95
|
+
return `覆盖列表页「${label}」按钮的控件功能:${description}`;
|
|
96
|
+
}
|
|
97
|
+
return `覆盖列表页「${label}」按钮的控件功能(featureId=${entry.featureId})。`;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 根据 PRD 功能条目渲染执行步骤。
|
|
101
|
+
*
|
|
102
|
+
* 优先使用 entry.processingLogic;若为空则使用 entry.inputConditions。
|
|
103
|
+
*/
|
|
104
|
+
export function renderPrdSteps(entry, label) {
|
|
105
|
+
const steps = [];
|
|
106
|
+
steps.push(`1. 在目标页面定位并点击「${label}」按钮。`);
|
|
107
|
+
const logic = entry.processingLogic.filter(Boolean);
|
|
108
|
+
if (logic.length > 0) {
|
|
109
|
+
logic.forEach((item, index) => {
|
|
110
|
+
steps.push(`${index + 2}. ${item}。`);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
else if (entry.inputConditions) {
|
|
114
|
+
const input = entry.inputConditions.trim();
|
|
115
|
+
if (input && input !== '无') {
|
|
116
|
+
steps.push(`2. 确保满足输入条件:${input}。`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
steps.push(`${steps.length + 1}. 观察并记录页面反馈。`);
|
|
120
|
+
return steps.join(' ');
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* 根据 PRD 功能条目渲染断言。
|
|
124
|
+
*
|
|
125
|
+
* 从 outputResult 中提取成功/失败结果,组合成选择型断言。
|
|
126
|
+
* 若 outputResult 为空,则使用 businessRules 兜底。
|
|
127
|
+
*/
|
|
128
|
+
export function renderPrdAssertion(entry, label) {
|
|
129
|
+
const outputs = entry.outputResult.filter(Boolean);
|
|
130
|
+
if (outputs.length > 0) {
|
|
131
|
+
// 若 outputResult 有 2 条以上,通常对应成功/失败分支,用「;或」连接。
|
|
132
|
+
if (outputs.length >= 2) {
|
|
133
|
+
return `页面出现${outputs[0]};或出现${outputs[1]}。`;
|
|
134
|
+
}
|
|
135
|
+
return `页面出现${outputs[0]}。`;
|
|
136
|
+
}
|
|
137
|
+
const rules = entry.businessRules.filter(Boolean);
|
|
138
|
+
if (rules.length > 0) {
|
|
139
|
+
return `点击「${label}」按钮后,${rules.join(';')}。`;
|
|
140
|
+
}
|
|
141
|
+
return `点击「${label}」按钮后,页面应出现与该功能对应的可见响应(如提示信息、弹窗确认或数据刷新)。`;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 获取 PRD 功能条目的输出结果文本(用于 trace / 测试断言)。
|
|
145
|
+
*/
|
|
146
|
+
export function getPrdOutputResultText(entry) {
|
|
147
|
+
return renderPrdAssertion(entry, entry.featureName);
|
|
148
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-context.d.ts","sourceRoot":"","sources":["../../../src/testcase/runtime-context.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 开发阶段测试访问配置。
|
|
3
|
+
*/
|
|
4
|
+
export type TestAccessRole = {
|
|
5
|
+
role: string;
|
|
6
|
+
username?: string;
|
|
7
|
+
password?: string;
|
|
8
|
+
permissions: string[];
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* 单个业务系统的测试访问配置。
|
|
12
|
+
*/
|
|
13
|
+
export type TestAccessSystem = {
|
|
14
|
+
systemName: string;
|
|
15
|
+
loginUrl?: string;
|
|
16
|
+
roles: TestAccessRole[];
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* testcase / code_list 可复用的测试访问配置。
|
|
20
|
+
*/
|
|
21
|
+
export type TestAccessConfig = {
|
|
22
|
+
/** 运行访问数据文件引用;新链路默认指向同目录 credentials.json5。 */
|
|
23
|
+
credentialRef?: string;
|
|
24
|
+
/** 用例执行所需角色清单,供审阅与运行前门禁使用。 */
|
|
25
|
+
requiredRoles?: string[];
|
|
26
|
+
/** 兼容旧链路的内嵌系统账号结构;新链路不作为主路径。 */
|
|
27
|
+
systems?: TestAccessSystem[];
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* 归一化并校验测试访问配置。
|
|
31
|
+
*
|
|
32
|
+
* @param raw - 原始输入。
|
|
33
|
+
* @param fieldPrefix - 顶层字段前缀,用于生成清晰错误信息。
|
|
34
|
+
* @returns 归一化后的测试访问配置。
|
|
35
|
+
* @throws Error 结构非法时抛错。
|
|
36
|
+
*/
|
|
37
|
+
export declare function normalizeTestAccessConfig(raw: unknown, fieldPrefix?: string): TestAccessConfig;
|
|
38
|
+
//# sourceMappingURL=test-access.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-access.d.ts","sourceRoot":"","sources":["../../../src/testcase/test-access.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,+CAA+C;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,gCAAgC;IAChC,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC9B,CAAC;AAoDF;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,SAAe,GAAG,gBAAgB,CAyDpG"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 断言值为非空字符串。
|
|
3
|
+
*
|
|
4
|
+
* @param value - 待校验值。
|
|
5
|
+
* @param field - 错误提示字段名。
|
|
6
|
+
* @throws Error 字段不是非空字符串时抛错。
|
|
7
|
+
*/
|
|
8
|
+
function requireNonEmptyString(value, field) {
|
|
9
|
+
if (typeof value !== 'string' || !value) {
|
|
10
|
+
throw new Error(`${field} 必须为非空字符串`);
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 断言值若存在则必须为字符串。
|
|
16
|
+
*
|
|
17
|
+
* @param value - 待校验值。
|
|
18
|
+
* @param field - 错误提示字段名。
|
|
19
|
+
* @returns 合法字符串或 undefined。
|
|
20
|
+
* @throws Error 字段存在但不是字符串时抛错。
|
|
21
|
+
*/
|
|
22
|
+
function requireOptionalString(value, field) {
|
|
23
|
+
if (value === undefined) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
if (typeof value !== 'string') {
|
|
27
|
+
throw new Error(`${field} 必须为字符串`);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 断言权限列表为字符串数组;缺省时归一为空数组。
|
|
33
|
+
*
|
|
34
|
+
* @param value - 待校验值。
|
|
35
|
+
* @param field - 错误提示字段名。
|
|
36
|
+
* @returns 归一化后的权限数组。
|
|
37
|
+
* @throws Error 字段不是字符串数组时抛错。
|
|
38
|
+
*/
|
|
39
|
+
function normalizePermissions(value, field) {
|
|
40
|
+
if (value === undefined) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== 'string' || !item)) {
|
|
44
|
+
throw new Error(`${field} 必须为字符串数组`);
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 归一化并校验测试访问配置。
|
|
50
|
+
*
|
|
51
|
+
* @param raw - 原始输入。
|
|
52
|
+
* @param fieldPrefix - 顶层字段前缀,用于生成清晰错误信息。
|
|
53
|
+
* @returns 归一化后的测试访问配置。
|
|
54
|
+
* @throws Error 结构非法时抛错。
|
|
55
|
+
*/
|
|
56
|
+
export function normalizeTestAccessConfig(raw, fieldPrefix = 'testAccess') {
|
|
57
|
+
if (!raw || typeof raw !== 'object') {
|
|
58
|
+
throw new Error(`${fieldPrefix} 必须为对象`);
|
|
59
|
+
}
|
|
60
|
+
const input = raw;
|
|
61
|
+
const credentialRef = requireOptionalString(input.credentialRef, `${fieldPrefix}.credentialRef`);
|
|
62
|
+
const requiredRoles = normalizePermissions(input.requiredRoles, `${fieldPrefix}.requiredRoles`);
|
|
63
|
+
if (input.systems === undefined) {
|
|
64
|
+
if (!credentialRef) {
|
|
65
|
+
throw new Error(`${fieldPrefix}.credentialRef 或 ${fieldPrefix}.systems 必须至少提供一个`);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
credentialRef,
|
|
69
|
+
...(requiredRoles.length > 0 ? { requiredRoles } : {}),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (!Array.isArray(input.systems)) {
|
|
73
|
+
throw new Error(`${fieldPrefix}.systems 必须为数组`);
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
...(credentialRef ? { credentialRef } : {}),
|
|
77
|
+
...(requiredRoles.length > 0 ? { requiredRoles } : {}),
|
|
78
|
+
systems: input.systems.map((system, systemIndex) => {
|
|
79
|
+
const systemField = `${fieldPrefix}.systems[${systemIndex}]`;
|
|
80
|
+
if (!system || typeof system !== 'object') {
|
|
81
|
+
throw new Error(`${systemField} 必须为对象`);
|
|
82
|
+
}
|
|
83
|
+
const rawSystem = system;
|
|
84
|
+
if (!Array.isArray(rawSystem.roles)) {
|
|
85
|
+
throw new Error(`${systemField}.roles 必须为数组`);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
systemName: requireNonEmptyString(rawSystem.systemName, `${systemField}.systemName`),
|
|
89
|
+
loginUrl: requireOptionalString(rawSystem.loginUrl, `${systemField}.loginUrl`),
|
|
90
|
+
roles: rawSystem.roles.map((role, roleIndex) => {
|
|
91
|
+
const roleField = `${systemField}.roles[${roleIndex}]`;
|
|
92
|
+
if (!role || typeof role !== 'object') {
|
|
93
|
+
throw new Error(`${roleField} 必须为对象`);
|
|
94
|
+
}
|
|
95
|
+
const rawRole = role;
|
|
96
|
+
return {
|
|
97
|
+
role: requireNonEmptyString(rawRole.role, `${roleField}.role`),
|
|
98
|
+
username: requireOptionalString(rawRole.username, `${roleField}.username`),
|
|
99
|
+
password: requireOptionalString(rawRole.password, `${roleField}.password`),
|
|
100
|
+
permissions: normalizePermissions(rawRole.permissions, `${roleField}.permissions`),
|
|
101
|
+
};
|
|
102
|
+
}),
|
|
103
|
+
};
|
|
104
|
+
}),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testcase-execution-fields.d.ts","sourceRoot":"","sources":["../../../src/testcase/testcase-execution-fields.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testcase-generator.d.ts","sourceRoot":"","sources":["../../../src/testcase/testcase-generator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"testcase-generator.d.ts","sourceRoot":"","sources":["../../../src/testcase/testcase-generator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EAEb,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAOvF;;;;;;;;;GASG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,0BAA0B,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,kBAAkB,CAAC,EAAE,YAAY,EAAE,CAAC;IACpC,YAAY,EAAE,iBAAiB,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;GAUG;AAKH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,2BAA2B,CA2GpG"}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated 旧 v2 协议 testcase 生成器(generateGlueTestcases)。
|
|
3
|
+
* 新 glue 链路已改用 `assembleJson5ContractAndTestcase`(见
|
|
4
|
+
* `src/testcase/contract-testcase-assembler.ts`);本文件仍被 gap-pipeline
|
|
5
|
+
* (run-gap-pipeline)使用,故保留,待该子系统迁移后整体清理。
|
|
6
|
+
*/
|
|
1
7
|
import { readFileSync } from 'node:fs';
|
|
2
8
|
import { mapContractToCrudTestcaseSlots, renderGlueTestcaseSkeleton, selectTestcaseSkeletonByScenario, } from './testcase-skeleton.js';
|
|
3
9
|
/**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EpStageTestcaseV1Draft } from './testcase-v1-draft.js';
|
|
2
|
+
import type { EpStageContractV1Draft } from '../contracts/glue-contract-v1.js';
|
|
3
|
+
/**
|
|
4
|
+
* 把 testcase.json5 渲染成统一单表的 testcase.md 审阅视图(含头部槽位映射信息)。
|
|
5
|
+
*
|
|
6
|
+
* @param testcase - testcase.json5 事实源。
|
|
7
|
+
* @param contract - contract.json5 事实源(可选,用于渲染占位符映射表)。
|
|
8
|
+
* @returns 完整的 Markdown 文本。
|
|
9
|
+
*/
|
|
10
|
+
export declare function renderUnifiedTestcaseMarkdown(testcase: EpStageTestcaseV1Draft, contract?: EpStageContractV1Draft): string;
|
|
11
|
+
//# sourceMappingURL=testcase-markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testcase-markdown.d.ts","sourceRoot":"","sources":["../../../src/testcase/testcase-markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAkD/E;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,sBAAsB,EAChC,QAAQ,CAAC,EAAE,sBAAsB,GAChC,MAAM,CA2JR"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 把生成策略翻译成审阅表格中的中文文案。
|
|
3
|
+
*
|
|
4
|
+
* @param strategy - testcase.json5 中的生成策略。
|
|
5
|
+
* @returns 中文生成策略标签。
|
|
6
|
+
*/
|
|
7
|
+
function strategyLabel(strategy) {
|
|
8
|
+
return strategy === 'glue' ? '胶水组装' : 'AI 推理';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 规整 Markdown 表格单元格,避免空值和换行破坏表格结构。
|
|
12
|
+
*
|
|
13
|
+
* @param value - 原始字段值。
|
|
14
|
+
* @returns 可直接写入 Markdown 表格的文本。
|
|
15
|
+
*/
|
|
16
|
+
function escapeCell(value) {
|
|
17
|
+
return (value || '待补充').replaceAll('|', '\\|').replace(/\s+/g, ' ').trim();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 从 locator xpath 表达式中提取按钮文本。
|
|
21
|
+
*
|
|
22
|
+
* @param locator - Playwright locator 表达式。
|
|
23
|
+
* @returns 按钮中文文本或占位符。
|
|
24
|
+
*/
|
|
25
|
+
function extractButtonLabel(locator) {
|
|
26
|
+
if (!locator)
|
|
27
|
+
return '待运行态确认';
|
|
28
|
+
// xpath=//a[contains(@class,'mini-button')][normalize-space()='新增场所信息管理']
|
|
29
|
+
const normalizeSpaceMatch = locator.match(/normalize-space\(\)='([^']+)'/);
|
|
30
|
+
if (normalizeSpaceMatch)
|
|
31
|
+
return normalizeSpaceMatch[1];
|
|
32
|
+
// xpath=//span[text()='搜索']
|
|
33
|
+
const textMatch = locator.match(/text\(\)='([^']+)'/);
|
|
34
|
+
if (textMatch)
|
|
35
|
+
return textMatch[1];
|
|
36
|
+
// text=确定
|
|
37
|
+
const simpleTextMatch = locator.match(/^text=(.+)$/);
|
|
38
|
+
if (simpleTextMatch)
|
|
39
|
+
return simpleTextMatch[1];
|
|
40
|
+
// xpath=//*[@data-tooltip='修改']
|
|
41
|
+
const tooltipMatch = locator.match(/@data-tooltip='([^']+)'/);
|
|
42
|
+
if (tooltipMatch)
|
|
43
|
+
return tooltipMatch[1];
|
|
44
|
+
return '待运行态确认';
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 把 testcase.json5 渲染成统一单表的 testcase.md 审阅视图(含头部槽位映射信息)。
|
|
48
|
+
*
|
|
49
|
+
* @param testcase - testcase.json5 事实源。
|
|
50
|
+
* @param contract - contract.json5 事实源(可选,用于渲染占位符映射表)。
|
|
51
|
+
* @returns 完整的 Markdown 文本。
|
|
52
|
+
*/
|
|
53
|
+
export function renderUnifiedTestcaseMarkdown(testcase, contract) {
|
|
54
|
+
const rows = [];
|
|
55
|
+
// 标题
|
|
56
|
+
rows.push(`# ${testcase.moduleName} 胶水测试用例`);
|
|
57
|
+
rows.push('');
|
|
58
|
+
rows.push('用例文件事实源:testcase.json5');
|
|
59
|
+
rows.push('');
|
|
60
|
+
// 环境信息(从 contract 和 testcase 提取)
|
|
61
|
+
if (contract) {
|
|
62
|
+
rows.push('## 环境信息');
|
|
63
|
+
rows.push('');
|
|
64
|
+
rows.push(`- **直达链接**:${contract.base.navigation.directUrl || '待补充'}`);
|
|
65
|
+
rows.push(`- **凭据文件**:${testcase.testAccess?.credentialRef || 'credentials.json5'}(含登录地址、账号、密码)`);
|
|
66
|
+
const roles = testcase.testAccess?.requiredRoles || [];
|
|
67
|
+
if (roles.length > 0) {
|
|
68
|
+
rows.push(`- **测试角色**:${roles.join('、')}`);
|
|
69
|
+
}
|
|
70
|
+
const crud = contract.scenario.skeletonSlots?.crud;
|
|
71
|
+
const frames = crud?.frames;
|
|
72
|
+
if (frames?.listIframeSrcKeyword) {
|
|
73
|
+
rows.push(`- **列表页关键字**:${frames.listIframeSrcKeyword}`);
|
|
74
|
+
}
|
|
75
|
+
rows.push('');
|
|
76
|
+
}
|
|
77
|
+
// 查询字段清单(从 testcase cases 和 contract searchConditions 提取)
|
|
78
|
+
if (contract) {
|
|
79
|
+
rows.push('## 查询字段');
|
|
80
|
+
rows.push('');
|
|
81
|
+
const queryKeys = [...new Set(testcase.cases.map((c) => c.queryKey).filter(Boolean))];
|
|
82
|
+
const crud = contract.scenario.skeletonSlots?.crud;
|
|
83
|
+
const searchConditions = (crud?.searchConditions || []);
|
|
84
|
+
queryKeys.forEach((key, idx) => {
|
|
85
|
+
const condition = searchConditions.find((c) => c.label === key);
|
|
86
|
+
const component = condition?.component || 'unknown';
|
|
87
|
+
rows.push(`${idx + 1}. ${key}(${component})`);
|
|
88
|
+
});
|
|
89
|
+
rows.push('');
|
|
90
|
+
}
|
|
91
|
+
// 动作关联按钮(占位符映射表)
|
|
92
|
+
if (contract) {
|
|
93
|
+
rows.push('## 动作关联按钮(占位符映射表)');
|
|
94
|
+
rows.push('');
|
|
95
|
+
rows.push('### Glue 胶水组装命中的按钮');
|
|
96
|
+
rows.push('');
|
|
97
|
+
const crud = contract.scenario.skeletonSlots?.crud;
|
|
98
|
+
const locators = (crud?.locators || {});
|
|
99
|
+
rows.push(`- \`{{新增按钮}}\` = "${extractButtonLabel(locators.createButton)}"`);
|
|
100
|
+
rows.push(`- \`{{删除按钮}}\` = "${extractButtonLabel(locators.listDeleteButton)}"`);
|
|
101
|
+
rows.push(`- \`{{保存按钮}}\` = "${extractButtonLabel(locators.addSaveButton)}"`);
|
|
102
|
+
rows.push(`- \`{{查询按钮}}\` = "${extractButtonLabel(locators.listSearchSubmit)}"`);
|
|
103
|
+
rows.push(`- \`{{修改按钮}}\` = "${extractButtonLabel(locators.listEditActionLocatorTemplate)}"`);
|
|
104
|
+
rows.push(`- \`{{确认按钮}}\` = "${extractButtonLabel(locators.deleteConfirmButton)}"`);
|
|
105
|
+
// 导出按钮(从 export slots 或 gapCandidates 提取)
|
|
106
|
+
const exportSlots = contract.scenario.skeletonSlots?.export;
|
|
107
|
+
if (exportSlots) {
|
|
108
|
+
rows.push(`- \`{{导出按钮}}\` = "${exportSlots.label || '导出'}"`);
|
|
109
|
+
rows.push(`- \`{{导出确认按钮}}\` = "导出"(弹窗内确认按钮)`);
|
|
110
|
+
}
|
|
111
|
+
const frames = crud?.frames;
|
|
112
|
+
const listKeyword = frames?.listIframeSrcKeyword || '待补充';
|
|
113
|
+
rows.push(`- \`{{列表页}}\` = 返回列表页(iframe: ${listKeyword})`);
|
|
114
|
+
rows.push('');
|
|
115
|
+
// AI 推理候选按钮
|
|
116
|
+
const aiCandidates = testcase.uncoveredCandidates.filter((c) => c.generationStrategy === 'ai' && c.coverageStatus !== 'blocked');
|
|
117
|
+
if (aiCandidates.length > 0) {
|
|
118
|
+
rows.push('### AI 推理候选按钮');
|
|
119
|
+
rows.push('');
|
|
120
|
+
aiCandidates.forEach((c) => {
|
|
121
|
+
rows.push(`- ${c.label}`);
|
|
122
|
+
});
|
|
123
|
+
rows.push('');
|
|
124
|
+
}
|
|
125
|
+
// Blocked 候选(PRD 提到但未实现)
|
|
126
|
+
const blockedCandidates = testcase.uncoveredCandidates.filter((c) => c.coverageStatus === 'blocked');
|
|
127
|
+
if (blockedCandidates.length > 0) {
|
|
128
|
+
rows.push('### 已阻断候选(PRD 提到但未实现)');
|
|
129
|
+
rows.push('');
|
|
130
|
+
blockedCandidates.forEach((c) => {
|
|
131
|
+
rows.push(`- ${c.label}`);
|
|
132
|
+
});
|
|
133
|
+
rows.push('');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// 用例清单表格
|
|
137
|
+
rows.push('## 用例清单');
|
|
138
|
+
rows.push('');
|
|
139
|
+
rows.push('| 用例 ID | 生成策略 | 用例标题 | 查询主键 | 前置条件 | 执行步骤 | 执行断言 |');
|
|
140
|
+
rows.push('|---|---|---|---|---|---|---|');
|
|
141
|
+
for (const item of testcase.cases) {
|
|
142
|
+
rows.push([
|
|
143
|
+
item.caseId,
|
|
144
|
+
strategyLabel(item.generationStrategy),
|
|
145
|
+
item.title,
|
|
146
|
+
item.queryKey,
|
|
147
|
+
item.precondition,
|
|
148
|
+
item.steps,
|
|
149
|
+
item.assertion,
|
|
150
|
+
]
|
|
151
|
+
.map(escapeCell)
|
|
152
|
+
.join(' | ')
|
|
153
|
+
.replace(/^/, '| ')
|
|
154
|
+
.replace(/$/, ' |'));
|
|
155
|
+
}
|
|
156
|
+
for (const item of testcase.uncoveredCandidates) {
|
|
157
|
+
rows.push([
|
|
158
|
+
item.caseId,
|
|
159
|
+
strategyLabel(item.generationStrategy),
|
|
160
|
+
item.label,
|
|
161
|
+
item.queryKey,
|
|
162
|
+
item.precondition,
|
|
163
|
+
item.steps,
|
|
164
|
+
item.assertion,
|
|
165
|
+
]
|
|
166
|
+
.map(escapeCell)
|
|
167
|
+
.join(' | ')
|
|
168
|
+
.replace(/^/, '| ')
|
|
169
|
+
.replace(/$/, ' |'));
|
|
170
|
+
}
|
|
171
|
+
rows.push('');
|
|
172
|
+
// 审阅引导
|
|
173
|
+
rows.push('## 审阅引导');
|
|
174
|
+
rows.push('');
|
|
175
|
+
rows.push('1. **占位符含义**:表格中 `{{xxx}}` 占位符的实际值见上方"动作关联按钮"映射表。');
|
|
176
|
+
rows.push('2. **调整方式**:需修改时请告知要改哪条用例,由 Agent 更新 testcase.json5 后重新渲染。');
|
|
177
|
+
rows.push('3. **凭据信息**:登录账号密码存储在 credentials.json5,不显示在本审阅视图。');
|
|
178
|
+
rows.push('4. **证据追溯**:具体定位器表达式、iframe 关键字、业务规则详见 contract.json5。');
|
|
179
|
+
rows.push('5. **Blocked 候选**:标记为 blocked 的用例不会生成 spec 代码,仅记录在 trace 中供人工确认。');
|
|
180
|
+
return `${rows.join('\n')}\n`;
|
|
181
|
+
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated 旧 v2 协议 testcase skeleton 渲染(mapContractToCrudTestcaseSlots 等)。
|
|
3
|
+
* 新 glue 链路不引用;仅被旧 testcase-generator(gap-pipeline 子系统)使用,故保留。
|
|
4
|
+
*/
|
|
1
5
|
import type { CrudBusinessModuleContract } from '../contracts/crud-business-module.js';
|
|
2
6
|
import type { PageScenario } from '../contracts/observable-chain.js';
|
|
3
7
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testcase-skeleton.d.ts","sourceRoot":"","sources":["../../../src/testcase/testcase-skeleton.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AACvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAOrE;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,UAAU,EAAE,2BAA2B,CAAC;IACxC,YAAY,EAAE,2BAA2B,CAAC;IAC1C,QAAQ,EAAE,kBAAkB,GAAG,aAAa,CAAC;CAC9C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,0BAA0B,CAAC,iBAAiB,CAAC,CAAC;IAC/D,QAAQ,EAAE,YAAY,CAAC;IACvB,YAAY,EAAE,WAAW,GAAG,cAAc,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,mCAAmC,GAAG;IAChD,QAAQ,EAAE,0BAA0B,CAAC;IACrC,QAAQ,EAAE,YAAY,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,YAAY,GAAG,yBAAyB,CAUlG;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,mCAAmC,GAAG,iBAAiB,CA4E5G;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAS7F"}
|
|
1
|
+
{"version":3,"file":"testcase-skeleton.d.ts","sourceRoot":"","sources":["../../../src/testcase/testcase-skeleton.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AACvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAOrE;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,UAAU,EAAE,2BAA2B,CAAC;IACxC,YAAY,EAAE,2BAA2B,CAAC;IAC1C,QAAQ,EAAE,kBAAkB,GAAG,aAAa,CAAC;CAC9C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,0BAA0B,CAAC,iBAAiB,CAAC,CAAC;IAC/D,QAAQ,EAAE,YAAY,CAAC;IACvB,YAAY,EAAE,WAAW,GAAG,cAAc,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,mCAAmC,GAAG;IAChD,QAAQ,EAAE,0BAA0B,CAAC;IACrC,QAAQ,EAAE,YAAY,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,YAAY,GAAG,yBAAyB,CAUlG;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,mCAAmC,GAAG,iBAAiB,CA4E5G;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAS7F"}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated 旧 v2 协议 testcase → spec 装配(validateGlueTestcaseDocumentForSpecAssembly)。
|
|
3
|
+
* 新 glue 链路不引用;保留作历史参考与 gap-pipeline 兼容。
|
|
4
|
+
*/
|
|
1
5
|
import type { CrudBusinessModuleContract } from '../contracts/crud-business-module.js';
|
|
2
6
|
import type { GlueTestcaseDocument } from '../contracts/observable-chain.js';
|
|
3
7
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testcase-spec-assembly.d.ts","sourceRoot":"","sources":["../../../src/testcase/testcase-spec-assembly.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AACvF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAI7E;;;;;;GAMG;AACH,wBAAgB,2CAA2C,CACzD,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,0BAA0B,GACnC,IAAI,CAkBN"}
|
|
1
|
+
{"version":3,"file":"testcase-spec-assembly.d.ts","sourceRoot":"","sources":["../../../src/testcase/testcase-spec-assembly.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AACvF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAI7E;;;;;;GAMG;AACH,wBAAgB,2CAA2C,CACzD,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,0BAA0B,GACnC,IAAI,CAkBN"}
|