@epoint-testtech/ep-stage-skill 0.0.9 → 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 -316
- 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
package/dist/src/cli/probe.d.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import type { CrudBusinessModuleContract } from '../contracts/crud-business-module.js';
|
|
2
|
-
import type { CoverageDiffTrace } from '../contracts/observable-chain.js';
|
|
3
|
-
import type { TestcaseV2 } from '../testcase/testcase-v2.js';
|
|
4
|
-
import type { InferenceTraceV1 } from '../contracts/inference-trace.js';
|
|
5
|
-
/**
|
|
6
|
-
* probe 子模块入参(REQ-CHAIN-01)。
|
|
7
|
-
*
|
|
8
|
-
* 把 run-gap-pipeline.ts 与 generate-testcase.ts 共用的「物料解析 → contract 构建 →
|
|
9
|
-
* skeleton coverage → coverage diff」段抽成单一入口,避免两侧复制实现。
|
|
10
|
-
*/
|
|
11
|
-
export type ProbeAndCoverageArgs = {
|
|
12
|
-
moduleId: string;
|
|
13
|
-
codeListPath: string;
|
|
14
|
-
docsDir: string;
|
|
15
|
-
webappDir: string;
|
|
16
|
-
javaActionsDir: string;
|
|
17
|
-
hintsPath?: string;
|
|
18
|
-
scenario: string;
|
|
19
|
-
menuPath?: string;
|
|
20
|
-
};
|
|
21
|
-
/**
|
|
22
|
-
* probe 子模块产物。
|
|
23
|
-
*
|
|
24
|
-
* @property contract - 标准化后的 CRUD 业务模块契约。
|
|
25
|
-
* @property coverageDiff - 骨架 vs gap 覆盖度差异 trace。
|
|
26
|
-
* @property testcaseV2 - 装配后的 testcase v2 顶层结构(待 generate-testcase 写入 .json5)。
|
|
27
|
-
*/
|
|
28
|
-
export type ProbeAndCoverageResult = {
|
|
29
|
-
contract: CrudBusinessModuleContract;
|
|
30
|
-
coverageDiff: CoverageDiffTrace;
|
|
31
|
-
testcaseV2: TestcaseV2;
|
|
32
|
-
inferenceTrace: InferenceTraceV1;
|
|
33
|
-
};
|
|
34
|
-
/**
|
|
35
|
-
* probe + coverage + testcase v2 装配的统一入口(REQ-CHAIN-01)。
|
|
36
|
-
*
|
|
37
|
-
* 把 run-gap-pipeline.ts 与 generate-testcase.ts 共用的物料解析 → contract → skeleton coverage →
|
|
38
|
-
* coverage diff 段抽成单一函数,避免 1881 行 run-gap-pipeline.ts 与新 CLI 双份维护。
|
|
39
|
-
*
|
|
40
|
-
* 注意:本函数只做钻探与装配,不写文件;写盘由调用方负责(保持 I/O 边界清晰)。
|
|
41
|
-
*
|
|
42
|
-
* @param args - 物料路径 + 场景。
|
|
43
|
-
* @returns contract + coverageDiff + 装配好的 TestcaseV2。
|
|
44
|
-
*/
|
|
45
|
-
export declare function runProbeAndCoverage(args: ProbeAndCoverageArgs): ProbeAndCoverageResult;
|
|
46
|
-
//# sourceMappingURL=probe.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../../src/cli/probe.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,0BAA0B,EAAe,MAAM,sCAAsC,CAAC;AACpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAI7D,OAAO,KAAK,EAAE,gBAAgB,EAAoE,MAAM,iCAAiC,CAAC;AAI1I;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,0BAA0B,CAAC;IACrC,YAAY,EAAE,iBAAiB,CAAC;IAChC,UAAU,EAAE,UAAU,CAAC;IACvB,cAAc,EAAE,gBAAgB,CAAC;CAClC,CAAC;AAoYF;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,sBAAsB,CAuFtF"}
|
package/dist/src/cli/probe.js
DELETED
|
@@ -1,447 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { extractCodeListSummary } from '../extractors/code-list.js';
|
|
4
|
-
import { extractHtmlPage } from '../extractors/html-page.js';
|
|
5
|
-
import { extractJavaAction } from '../extractors/java-action.js';
|
|
6
|
-
import { extractSpecYaml } from '../extractors/spec-yaml.js';
|
|
7
|
-
import { buildCrudBusinessModuleContract } from '../normalizers/crud-contract.js';
|
|
8
|
-
import { collectActionCandidates } from '../gap-planner/action-candidates.js';
|
|
9
|
-
import { createSkeletonCoverage } from '../gap-planner/skeleton-coverage.js';
|
|
10
|
-
import { planAgentInferredWorkflows } from '../gap-planner/plan-agent-workflows.js';
|
|
11
|
-
import { summarizePageStructure } from '../capability/page-structure.js';
|
|
12
|
-
import { createCoverageDiff } from '../capability/coverage-diff.js';
|
|
13
|
-
import { buildSourceAnalysis } from '../inference/source-analysis.js';
|
|
14
|
-
import { inferModuleHintsFromMaterials } from '../inference/module-hints-inference.js';
|
|
15
|
-
/**
|
|
16
|
-
* 收集指定目录下的 HTML 物料文件。
|
|
17
|
-
*
|
|
18
|
-
* @param dir - 物料目录。
|
|
19
|
-
* @returns 排序后的绝对路径数组。
|
|
20
|
-
*/
|
|
21
|
-
function htmlFiles(dir) {
|
|
22
|
-
return readdirSync(dir)
|
|
23
|
-
.filter((fileName) => fileName.endsWith('.html'))
|
|
24
|
-
.sort()
|
|
25
|
-
.map((fileName) => path.join(dir, fileName));
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* 收集指定目录下的 *Action.java 物料文件。
|
|
29
|
-
*
|
|
30
|
-
* @param dir - 物料目录。
|
|
31
|
-
* @returns 排序后的绝对路径数组。
|
|
32
|
-
*/
|
|
33
|
-
function javaFiles(dir) {
|
|
34
|
-
return readdirSync(dir)
|
|
35
|
-
.filter((fileName) => fileName.endsWith('Action.java'))
|
|
36
|
-
.sort()
|
|
37
|
-
.map((fileName) => path.join(dir, fileName));
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* 根据页面 ID 推断 CRUD 页面角色。
|
|
41
|
-
*
|
|
42
|
-
* @param pageId - 页面标识。
|
|
43
|
-
* @returns CRUD 页面角色。
|
|
44
|
-
*/
|
|
45
|
-
function inferCrudPageRole(pageId) {
|
|
46
|
-
if (pageId.endsWith('add'))
|
|
47
|
-
return 'add';
|
|
48
|
-
if (pageId.endsWith('edit'))
|
|
49
|
-
return 'edit';
|
|
50
|
-
if (pageId.endsWith('detail'))
|
|
51
|
-
return 'detail';
|
|
52
|
-
return 'list';
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* 将 HTML 控件类型映射为 spec.yaml 兼容的组件类型。
|
|
56
|
-
*
|
|
57
|
-
* @param controlType - HTML extractor 提取到的控件类型。
|
|
58
|
-
* @returns spec 字段组件类型。
|
|
59
|
-
*/
|
|
60
|
-
function mapHtmlControlTypeToSpecComponent(controlType) {
|
|
61
|
-
if (controlType === 'mini-combobox') {
|
|
62
|
-
return 'listbox';
|
|
63
|
-
}
|
|
64
|
-
if (controlType === 'mini-textarea') {
|
|
65
|
-
return 'textarea';
|
|
66
|
-
}
|
|
67
|
-
return 'input';
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* 在缺少 spec.yaml 时,根据 HTML 页面结构合成最小可用的 spec。
|
|
71
|
-
*
|
|
72
|
-
* 设计取舍:
|
|
73
|
-
* - searchable: 仅由列表页搜索区字段决定;
|
|
74
|
-
* - formVisible: 由 add/edit 页面字段决定;
|
|
75
|
-
* - listVisible: 优先取列表页 datagrid 列定义;
|
|
76
|
-
* - businessRules: 当前无法可靠结构化抽取,保持空数组,交给 testcase 审阅阶段。
|
|
77
|
-
*
|
|
78
|
-
* @param docsDir - 需求文档目录。
|
|
79
|
-
* @param moduleId - 模块 ID。
|
|
80
|
-
* @param codeListSummary - code_list 摘要。
|
|
81
|
-
* @param pages - 已提取的 HTML 页面集合。
|
|
82
|
-
* @returns 供 contract 正常生成使用的推理 spec。
|
|
83
|
-
*/
|
|
84
|
-
function inferSpecFromPages(docsDir, moduleId, codeListSummary, pages) {
|
|
85
|
-
const fieldMap = new Map();
|
|
86
|
-
const listPage = pages.find((page) => inferCrudPageRole(page.pageId) === 'list');
|
|
87
|
-
/**
|
|
88
|
-
* 合并同名字段的页面证据,避免列表页 / 新增页重复字段互相覆盖。
|
|
89
|
-
*
|
|
90
|
-
* @param field - 当前页面字段。
|
|
91
|
-
* @param role - 当前页面角色。
|
|
92
|
-
* @returns 无返回值。
|
|
93
|
-
*/
|
|
94
|
-
function mergeField(field, role) {
|
|
95
|
-
const existing = fieldMap.get(field.name) ?? {
|
|
96
|
-
name: field.name,
|
|
97
|
-
label: field.label,
|
|
98
|
-
component: mapHtmlControlTypeToSpecComponent(field.controlType),
|
|
99
|
-
required: field.required,
|
|
100
|
-
searchable: false,
|
|
101
|
-
listVisible: false,
|
|
102
|
-
formVisible: false,
|
|
103
|
-
length: field.maxLength,
|
|
104
|
-
};
|
|
105
|
-
existing.label ||= field.label;
|
|
106
|
-
existing.component ||= mapHtmlControlTypeToSpecComponent(field.controlType);
|
|
107
|
-
existing.required ||= field.required;
|
|
108
|
-
existing.length ??= field.maxLength;
|
|
109
|
-
if (role === 'list') {
|
|
110
|
-
existing.searchable = true;
|
|
111
|
-
}
|
|
112
|
-
if (role === 'add' || role === 'edit') {
|
|
113
|
-
existing.formVisible = true;
|
|
114
|
-
}
|
|
115
|
-
fieldMap.set(field.name, existing);
|
|
116
|
-
}
|
|
117
|
-
for (const page of pages) {
|
|
118
|
-
const role = inferCrudPageRole(page.pageId);
|
|
119
|
-
for (const field of page.fields) {
|
|
120
|
-
mergeField(field, role);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
for (const column of listPage?.grids.flatMap((grid) => grid.dataColumns) ?? []) {
|
|
124
|
-
if (!column.field) {
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
const existing = fieldMap.get(column.field) ?? {
|
|
128
|
-
name: column.field,
|
|
129
|
-
label: column.headerText || column.field,
|
|
130
|
-
component: 'input',
|
|
131
|
-
required: false,
|
|
132
|
-
searchable: false,
|
|
133
|
-
listVisible: false,
|
|
134
|
-
formVisible: false,
|
|
135
|
-
};
|
|
136
|
-
existing.label ||= column.headerText || column.field;
|
|
137
|
-
existing.listVisible = true;
|
|
138
|
-
fieldMap.set(column.field, existing);
|
|
139
|
-
}
|
|
140
|
-
return {
|
|
141
|
-
path: path.join(docsDir, 'spec.inferred-from-html.yaml'),
|
|
142
|
-
module: {
|
|
143
|
-
id: moduleId,
|
|
144
|
-
label: codeListSummary.moduleLabel || moduleId,
|
|
145
|
-
},
|
|
146
|
-
fields: Array.from(fieldMap.values()),
|
|
147
|
-
businessRules: [],
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* 解析 docs 目录下的 spec.yaml;缺失时根据 HTML 页面结构合成 fallback spec。
|
|
152
|
-
*
|
|
153
|
-
* @param docsDir - 需求文档目录。
|
|
154
|
-
* @param moduleId - 模块 ID。
|
|
155
|
-
* @param codeListSummary - 已解析的 code_list 摘要。
|
|
156
|
-
* @param pages - 已提取的 HTML 页面集合。
|
|
157
|
-
* @returns ExtractedSpecYaml。
|
|
158
|
-
*/
|
|
159
|
-
function resolveSpec(docsDir, moduleId, codeListSummary, pages) {
|
|
160
|
-
const specPath = path.join(docsDir, 'spec.yaml');
|
|
161
|
-
if (existsSync(specPath))
|
|
162
|
-
return extractSpecYaml(specPath);
|
|
163
|
-
return inferSpecFromPages(docsDir, moduleId, codeListSummary, pages);
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* 推断 requiredRole 默认值。
|
|
167
|
-
*
|
|
168
|
-
* 设计:testcase v2 schema 要求 cases[].requiredRole / uncoveredCandidates[].requiredRole 必填。
|
|
169
|
-
* 当前 BusinessRuleContract 不带 role 字段,hints.scenarioMatrix 也未覆盖角色;
|
|
170
|
-
* 回落到 'admin',后续由 generate-testcase 子命令的 --required-role / hints 覆盖。
|
|
171
|
-
*
|
|
172
|
-
* @returns 默认 requiredRole 字面量。
|
|
173
|
-
*/
|
|
174
|
-
function deriveRequiredRole() {
|
|
175
|
-
return 'admin';
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* 从 contract 推导出 v2 schema 要求的 requiredRoles 集合。
|
|
179
|
-
*
|
|
180
|
-
* 目前只生成单一默认角色;后续可由 hints / scenario decision 注入更丰富的角色矩阵。
|
|
181
|
-
*
|
|
182
|
-
* @returns 至少包含一个默认 admin 的角色清单。
|
|
183
|
-
*/
|
|
184
|
-
function deriveRequiredRoles() {
|
|
185
|
-
return [{ role: 'admin', hint: undefined }];
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* 把 coverageDiff 中已覆盖动作映射成 testcase v2 coveredActions 段。
|
|
189
|
-
*
|
|
190
|
-
* @param contract - CRUD 契约(用于 evidence kind 提示)。
|
|
191
|
-
* @param coverageDiff - coverage diff trace。
|
|
192
|
-
* @returns coveredActions 数组(含 evidence <kind>=<ref>)。
|
|
193
|
-
*/
|
|
194
|
-
function mapCoveredActions(contract, coverageDiff) {
|
|
195
|
-
return coverageDiff.covered.map((item) => ({
|
|
196
|
-
actionId: item.actionId,
|
|
197
|
-
kind: item.label,
|
|
198
|
-
evidence: [
|
|
199
|
-
`contract=${contract.contractVersion}`,
|
|
200
|
-
`skeleton=${coverageDiff.skeletonCoverageId}`,
|
|
201
|
-
],
|
|
202
|
-
}));
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* 把 coverageDiff 中未覆盖动作映射成 v2 uncoveredCandidates 段。
|
|
206
|
-
*
|
|
207
|
-
* @param contract - CRUD 契约(用于 evidence kind 提示)。
|
|
208
|
-
* @param coverageDiff - coverage diff trace。
|
|
209
|
-
* @param requiredRole - 默认 role;后续 spec/run 子命令可改写。
|
|
210
|
-
* @returns uncoveredCandidates 数组(含 requiredRole/businessIntent/assertionExpectation/evidence)。
|
|
211
|
-
*/
|
|
212
|
-
function mapUncoveredCandidates(contract, coverageDiff, requiredRole) {
|
|
213
|
-
const status = (diffStatus) => {
|
|
214
|
-
if (diffStatus === 'gap-candidate')
|
|
215
|
-
return 'planned';
|
|
216
|
-
if (diffStatus === 'partially-covered')
|
|
217
|
-
return 'candidate';
|
|
218
|
-
return 'unresolved';
|
|
219
|
-
};
|
|
220
|
-
return [
|
|
221
|
-
...coverageDiff.gapCandidates,
|
|
222
|
-
...coverageDiff.partiallyCovered,
|
|
223
|
-
...coverageDiff.unresolved,
|
|
224
|
-
].map((item) => ({
|
|
225
|
-
actionId: item.actionId,
|
|
226
|
-
label: item.label,
|
|
227
|
-
status: status(item.status),
|
|
228
|
-
requiredRole,
|
|
229
|
-
businessIntent: item.reason,
|
|
230
|
-
assertionExpectation: `验证 ${item.label} 完成后业务断言(待 run 子命令推理细化)`,
|
|
231
|
-
evidence: [`contract=${contract.contractVersion}`, `skeleton=${coverageDiff.skeletonCoverageId}`],
|
|
232
|
-
}));
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* 装配 testcase v2 顶层结构。
|
|
236
|
-
*
|
|
237
|
-
* @param input - 装配所需要素。
|
|
238
|
-
* @returns TestcaseV2。
|
|
239
|
-
*/
|
|
240
|
-
function assembleTestcaseV2(input) {
|
|
241
|
-
const requiredRoles = deriveRequiredRoles();
|
|
242
|
-
const defaultRole = deriveRequiredRole();
|
|
243
|
-
const coveredActions = mapCoveredActions(input.contract, input.coverageDiff);
|
|
244
|
-
const uncoveredCandidates = mapUncoveredCandidates(input.contract, input.coverageDiff, defaultRole);
|
|
245
|
-
const caseEvidence = [
|
|
246
|
-
`contract=${input.contract.contractVersion}`,
|
|
247
|
-
`skeleton=${input.coverageDiff.skeletonCoverageId}`,
|
|
248
|
-
];
|
|
249
|
-
const cases = ['create', 'read', 'update', 'delete'].map((kind) => ({
|
|
250
|
-
caseId: `${input.contract.module.id}.${kind}`,
|
|
251
|
-
title: `${input.contract.module.label} ${kind}`,
|
|
252
|
-
scenario: input.scenario,
|
|
253
|
-
reviewStatus: 'needs_review',
|
|
254
|
-
requiredRole: defaultRole,
|
|
255
|
-
evidence: caseEvidence,
|
|
256
|
-
steps: [`执行 ${kind} 主流程(详见 .spec.ts)`],
|
|
257
|
-
assertions: [`验证 ${kind} 完成后业务断言`],
|
|
258
|
-
unresolved: [],
|
|
259
|
-
}));
|
|
260
|
-
return {
|
|
261
|
-
schemaVersion: 'v2',
|
|
262
|
-
scenario: input.scenario,
|
|
263
|
-
moduleId: input.contract.module.id,
|
|
264
|
-
moduleName: input.contract.module.label,
|
|
265
|
-
menuPath: input.menuPath,
|
|
266
|
-
requiredSystems: [{ url: '', systemName: undefined }],
|
|
267
|
-
requiredRoles,
|
|
268
|
-
coveredActions,
|
|
269
|
-
uncoveredCandidates,
|
|
270
|
-
cases,
|
|
271
|
-
reasoningSummary: {
|
|
272
|
-
conclusion: `基于 ${input.contract.contractVersion} 契约和 ${input.coverageDiff.skeletonCoverageId} 骨架覆盖度生成 ${cases.length} 条胶水用例。`,
|
|
273
|
-
evidenceChain: [
|
|
274
|
-
`contract=${input.contract.contractVersion}`,
|
|
275
|
-
`skeleton=${input.coverageDiff.skeletonCoverageId}`,
|
|
276
|
-
`codeList=${path.basename(input.codeListPath)}`,
|
|
277
|
-
],
|
|
278
|
-
alternatives: [],
|
|
279
|
-
confidence: input.coverageDiff.reasoningSummary.confidence,
|
|
280
|
-
risks: input.coverageDiff.reasoningSummary.risks,
|
|
281
|
-
needsHumanReview: true,
|
|
282
|
-
},
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* 组装 materialInventory 章节(spec §7)。
|
|
287
|
-
*
|
|
288
|
-
* 把 probe 已解析到的 codeList / pages / actions / args 登记成可审阅的物料清单,
|
|
289
|
-
* 缺失项进 `missing` 数组。保持私有,不做跨文件抽象(Task 3 范围)。
|
|
290
|
-
*
|
|
291
|
-
* @param args - probe 入参。
|
|
292
|
-
* @param codeList - 已解析的 code_list 摘要。
|
|
293
|
-
* @param pages - 已抽取的 HTML 页面。
|
|
294
|
-
* @param actions - 已抽取的 Java Action。
|
|
295
|
-
* @returns MaterialInventoryTrace。
|
|
296
|
-
*/
|
|
297
|
-
function buildMaterialInventory(args, codeList, pages, actions) {
|
|
298
|
-
const missing = [];
|
|
299
|
-
const moduleIdSlot = { status: 'resolved', value: args.moduleId };
|
|
300
|
-
const menuSlot = codeList.menu
|
|
301
|
-
? { status: 'resolved', value: codeList.menu }
|
|
302
|
-
: (() => {
|
|
303
|
-
missing.push('menu');
|
|
304
|
-
return { status: 'missing', reason: 'code_list.md 未提供 menu' };
|
|
305
|
-
})();
|
|
306
|
-
const htmlFiles = pages.map((page) => ({
|
|
307
|
-
status: 'resolved',
|
|
308
|
-
value: page.path,
|
|
309
|
-
}));
|
|
310
|
-
const javaActionFiles = actions.map((action) => ({
|
|
311
|
-
status: 'resolved',
|
|
312
|
-
value: action.path,
|
|
313
|
-
}));
|
|
314
|
-
const docsFiles = [];
|
|
315
|
-
if (args.docsDir && existsSync(args.docsDir)) {
|
|
316
|
-
docsFiles.push({ status: 'resolved', value: args.docsDir });
|
|
317
|
-
}
|
|
318
|
-
else {
|
|
319
|
-
missing.push('docsDir');
|
|
320
|
-
docsFiles.push({ status: 'missing', reason: '未提供 docsDir' });
|
|
321
|
-
}
|
|
322
|
-
return {
|
|
323
|
-
knowledgeRoot: codeList.knowledgeRoot ?? '',
|
|
324
|
-
moduleId: moduleIdSlot,
|
|
325
|
-
menu: menuSlot,
|
|
326
|
-
htmlFiles,
|
|
327
|
-
javaActionFiles,
|
|
328
|
-
docsFiles,
|
|
329
|
-
missing,
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* 把 coverageDiff(CoverageDiffTrace)映射为 InferenceTraceV1.coverageDiff 的内联形状。
|
|
334
|
-
*
|
|
335
|
-
* 映射规则(controller 裁定):
|
|
336
|
-
* - coveredActions ← coverageDiff.covered
|
|
337
|
-
* - uncoveredCandidates ← [...gapCandidates, ...partiallyCovered, ...unresolved]
|
|
338
|
-
* - needsReview ← unresolved.map(i => i.actionId)
|
|
339
|
-
*
|
|
340
|
-
* @param coverageDiff - probe 产出的 CoverageDiffTrace。
|
|
341
|
-
* @returns InferenceTraceV1.coverageDiff。
|
|
342
|
-
*/
|
|
343
|
-
function mapCoverageDiffForTrace(coverageDiff) {
|
|
344
|
-
return {
|
|
345
|
-
coveredActions: coverageDiff.covered,
|
|
346
|
-
uncoveredCandidates: [
|
|
347
|
-
...coverageDiff.gapCandidates,
|
|
348
|
-
...coverageDiff.partiallyCovered,
|
|
349
|
-
...coverageDiff.unresolved,
|
|
350
|
-
],
|
|
351
|
-
needsReview: coverageDiff.unresolved.map((item) => item.actionId),
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* probe + coverage + testcase v2 装配的统一入口(REQ-CHAIN-01)。
|
|
356
|
-
*
|
|
357
|
-
* 把 run-gap-pipeline.ts 与 generate-testcase.ts 共用的物料解析 → contract → skeleton coverage →
|
|
358
|
-
* coverage diff 段抽成单一函数,避免 1881 行 run-gap-pipeline.ts 与新 CLI 双份维护。
|
|
359
|
-
*
|
|
360
|
-
* 注意:本函数只做钻探与装配,不写文件;写盘由调用方负责(保持 I/O 边界清晰)。
|
|
361
|
-
*
|
|
362
|
-
* @param args - 物料路径 + 场景。
|
|
363
|
-
* @returns contract + coverageDiff + 装配好的 TestcaseV2。
|
|
364
|
-
*/
|
|
365
|
-
export function runProbeAndCoverage(args) {
|
|
366
|
-
const codeList = extractCodeListSummary(args.codeListPath);
|
|
367
|
-
const pages = htmlFiles(args.webappDir).map(extractHtmlPage);
|
|
368
|
-
const actions = javaFiles(args.javaActionsDir).map(extractJavaAction);
|
|
369
|
-
// 源分析快照(spec §7 sourceAnalysis)。
|
|
370
|
-
const sourceAnalysis = buildSourceAnalysis({
|
|
371
|
-
codeListPath: args.codeListPath,
|
|
372
|
-
docsDir: args.docsDir,
|
|
373
|
-
codeList,
|
|
374
|
-
pages,
|
|
375
|
-
actions,
|
|
376
|
-
});
|
|
377
|
-
// 显式 hints 文件 vs 推理 hints:
|
|
378
|
-
// - 有 hintsPath 时读取为 explicitHints,mode 标记 explicit_override,explicitHints 流入 builders。
|
|
379
|
-
// - 无 hintsPath 时调 inferModuleHintsFromMaterials,推理出的 hints 流入 builders(替换原 undefined)。
|
|
380
|
-
let hints;
|
|
381
|
-
let moduleHintsInference;
|
|
382
|
-
if (args.hintsPath) {
|
|
383
|
-
const explicitHints = JSON.parse(readFileSync(args.hintsPath, 'utf8'));
|
|
384
|
-
// 仍跑一次推理以记录推理过程,但 mode 标记 explicit_override,builders 用 explicitHints。
|
|
385
|
-
const inferred = inferModuleHintsFromMaterials({
|
|
386
|
-
sourceAnalysis,
|
|
387
|
-
pages,
|
|
388
|
-
actions,
|
|
389
|
-
explicitHints,
|
|
390
|
-
});
|
|
391
|
-
moduleHintsInference = { ...inferred.inference, mode: 'explicit_override' };
|
|
392
|
-
hints = explicitHints;
|
|
393
|
-
}
|
|
394
|
-
else {
|
|
395
|
-
const inferred = inferModuleHintsFromMaterials({ sourceAnalysis, pages, actions });
|
|
396
|
-
moduleHintsInference = inferred.inference;
|
|
397
|
-
hints = inferred.hints;
|
|
398
|
-
}
|
|
399
|
-
const pageStructures = pages.map(summarizePageStructure);
|
|
400
|
-
const contract = buildCrudBusinessModuleContract({
|
|
401
|
-
moduleId: args.moduleId,
|
|
402
|
-
spec: resolveSpec(args.docsDir, args.moduleId, codeList, pages),
|
|
403
|
-
codeList,
|
|
404
|
-
pages,
|
|
405
|
-
actions,
|
|
406
|
-
hints,
|
|
407
|
-
});
|
|
408
|
-
const skeletonCoverage = createSkeletonCoverage(contract, hints);
|
|
409
|
-
const actionCandidates = collectActionCandidates(contract);
|
|
410
|
-
const baselineWorkflows = planAgentInferredWorkflows({ contract, actions, hints });
|
|
411
|
-
const coverageDiff = createCoverageDiff({
|
|
412
|
-
moduleId: args.moduleId,
|
|
413
|
-
actionCandidates,
|
|
414
|
-
skeletonCoverage,
|
|
415
|
-
baselineWorkflows,
|
|
416
|
-
pageStructures,
|
|
417
|
-
});
|
|
418
|
-
const testcaseV2 = assembleTestcaseV2({
|
|
419
|
-
contract,
|
|
420
|
-
coverageDiff,
|
|
421
|
-
scenario: args.scenario,
|
|
422
|
-
menuPath: args.menuPath ?? codeList.menu ?? '',
|
|
423
|
-
codeListPath: args.codeListPath,
|
|
424
|
-
});
|
|
425
|
-
// 组装 inference trace(REQ-INFER-004 / REQ-INFER-006)。
|
|
426
|
-
const materialInventory = buildMaterialInventory(args, codeList, pages, actions);
|
|
427
|
-
const reasoningSummary = {
|
|
428
|
-
conclusion: `基于 ${sourceAnalysis.mode} 源分析(置信度 ${sourceAnalysis.confidence})与 ${moduleHintsInference.mode} 槽位推理生成契约;coverageDiff unresolved ${coverageDiff.unresolved.length} 项。`,
|
|
429
|
-
confidence: sourceAnalysis.confidence,
|
|
430
|
-
risks: [
|
|
431
|
-
...sourceAnalysis.unresolved,
|
|
432
|
-
...moduleHintsInference.unresolved.map((slot) => `未解析槽位:${slot}`),
|
|
433
|
-
],
|
|
434
|
-
needsHumanReview: moduleHintsInference.unresolved.length > 0 || sourceAnalysis.confidence === 'low',
|
|
435
|
-
};
|
|
436
|
-
const inferenceTrace = {
|
|
437
|
-
schemaVersion: 'inference-trace/v1',
|
|
438
|
-
codeListPath: args.codeListPath,
|
|
439
|
-
generatedAt: new Date().toISOString(),
|
|
440
|
-
materialInventory,
|
|
441
|
-
sourceAnalysis,
|
|
442
|
-
moduleHintsInference,
|
|
443
|
-
coverageDiff: mapCoverageDiffForTrace(coverageDiff),
|
|
444
|
-
reasoningSummary,
|
|
445
|
-
};
|
|
446
|
-
return { contract, coverageDiff, testcaseV2, inferenceTrace };
|
|
447
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* run-report.ts — run 子命令的综合报告渲染(glue 风格单文件 HTML)。
|
|
3
|
-
*
|
|
4
|
-
* 输入:Playwright JSON reporter 产物(PLAYWRIGHT_JSON_OUTPUT_NAME 指定落盘)。
|
|
5
|
-
* 输出:<reportDir>/index.html —— 与脚手架 stage-glue 报告同款样式
|
|
6
|
-
* (概览 + 统计卡片 + 状态色卡片 + 页脚原生报告入口),
|
|
7
|
-
* 但数据来自 Playwright 真实执行结果(覆盖全部用例,含 AI snippet)。
|
|
8
|
-
*/
|
|
9
|
-
export type RunReportTestItem = {
|
|
10
|
-
title: string;
|
|
11
|
-
suitePath: string;
|
|
12
|
-
projectName: string;
|
|
13
|
-
status: 'passed' | 'failed' | 'flaky' | 'skipped';
|
|
14
|
-
durationMs: number;
|
|
15
|
-
location?: string;
|
|
16
|
-
errorMessage?: string;
|
|
17
|
-
};
|
|
18
|
-
export type RunReportData = {
|
|
19
|
-
suiteTitle: string;
|
|
20
|
-
startedAt: string;
|
|
21
|
-
durationMs: number;
|
|
22
|
-
specFile?: string;
|
|
23
|
-
items: RunReportTestItem[];
|
|
24
|
-
/** AI 任务清单(标题 + 用例ID):命中者标记为 Agent 推理执行并回显用例ID。 */
|
|
25
|
-
aiTasks: Array<{
|
|
26
|
-
title: string;
|
|
27
|
-
caseId: string;
|
|
28
|
-
}>;
|
|
29
|
-
/** 相对 reportDir 的链接。 */
|
|
30
|
-
playwrightReportHref?: string;
|
|
31
|
-
stageReportHref?: string;
|
|
32
|
-
};
|
|
33
|
-
/**
|
|
34
|
-
* 把 Playwright JSON reporter 输出摊平为报告用例条目。
|
|
35
|
-
*/
|
|
36
|
-
export declare function parsePlaywrightJsonResults(jsonText: string): {
|
|
37
|
-
suiteTitle: string;
|
|
38
|
-
durationMs: number;
|
|
39
|
-
startedAt: string;
|
|
40
|
-
items: RunReportTestItem[];
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* 渲染 glue 风格综合报告(单文件、内联样式、无外部依赖)。
|
|
44
|
-
*/
|
|
45
|
-
export declare function renderRunReportHtml(data: RunReportData): string;
|
|
46
|
-
//# sourceMappingURL=run-report.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"run-report.d.ts","sourceRoot":"","sources":["../../../src/cli/run-report.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,mDAAmD;IACnD,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,wBAAwB;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAkDF;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC5B,CA0CA;AA4ED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAgL/D"}
|