@epoint-testtech/ep-stage-skill 0.0.3-alpha.1
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 -0
- package/codex-skill/ep-stage/create-project/SKILL.md +59 -0
- package/codex-skill/ep-stage/glue-test/SKILL.md +258 -0
- package/codex-skill/ep-stage/glue-test/references/crud-pipeline.md +139 -0
- package/codex-skill/ep-stage/glue-test/references/gap-review-protocol.md +43 -0
- package/codex-skill/ep-stage/glue-test/references/harness-principles.md +46 -0
- package/codex-skill/ep-stage/glue-test/scripts/generate-crud-spec.mjs +149 -0
- package/codex-skill/ep-stage/glue-testcase/SKILL.md +31 -0
- package/codex-skill/ep-stage/glue-testcase/examples/observable-testcase.json +40 -0
- package/codex-skill/ep-stage/glue-testcase/references/testcase-schema.md +67 -0
- package/codex-skill/ep-stage/recording-to-glue/SKILL.md +27 -0
- package/codex-skill/ep-stage/scripts/validate-skill.mjs +73 -0
- package/dist/src/capability/coverage-diff.d.ts +34 -0
- package/dist/src/capability/coverage-diff.d.ts.map +1 -0
- package/dist/src/capability/coverage-diff.js +91 -0
- package/dist/src/capability/page-structure.d.ts +31 -0
- package/dist/src/capability/page-structure.d.ts.map +1 -0
- package/dist/src/capability/page-structure.js +50 -0
- package/dist/src/capability/scenario-inference.d.ts +36 -0
- package/dist/src/capability/scenario-inference.d.ts.map +1 -0
- package/dist/src/capability/scenario-inference.js +114 -0
- package/dist/src/cli/generate-crud-contract.d.ts +2 -0
- package/dist/src/cli/generate-crud-contract.d.ts.map +1 -0
- package/dist/src/cli/generate-crud-contract.js +77 -0
- package/dist/src/cli/generate-playwright-tests.d.ts +30 -0
- package/dist/src/cli/generate-playwright-tests.d.ts.map +1 -0
- package/dist/src/cli/generate-playwright-tests.js +81 -0
- package/dist/src/cli/run-gap-pipeline.d.ts +256 -0
- package/dist/src/cli/run-gap-pipeline.d.ts.map +1 -0
- package/dist/src/cli/run-gap-pipeline.js +1468 -0
- package/dist/src/context/stage-context.d.ts +63 -0
- package/dist/src/context/stage-context.d.ts.map +1 -0
- package/dist/src/context/stage-context.js +297 -0
- package/dist/src/contracts/crud-business-module.d.ts +645 -0
- package/dist/src/contracts/crud-business-module.d.ts.map +1 -0
- package/dist/src/contracts/crud-business-module.js +1 -0
- package/dist/src/contracts/gap-inference.d.ts +213 -0
- package/dist/src/contracts/gap-inference.d.ts.map +1 -0
- package/dist/src/contracts/gap-inference.js +11 -0
- package/dist/src/contracts/observable-chain.d.ts +250 -0
- package/dist/src/contracts/observable-chain.d.ts.map +1 -0
- package/dist/src/contracts/observable-chain.js +1 -0
- package/dist/src/extractors/code-list.d.ts +40 -0
- package/dist/src/extractors/code-list.d.ts.map +1 -0
- package/dist/src/extractors/code-list.js +225 -0
- package/dist/src/extractors/html-page.d.ts +67 -0
- package/dist/src/extractors/html-page.d.ts.map +1 -0
- package/dist/src/extractors/html-page.js +195 -0
- package/dist/src/extractors/java-action.d.ts +8 -0
- package/dist/src/extractors/java-action.d.ts.map +1 -0
- package/dist/src/extractors/java-action.js +53 -0
- package/dist/src/extractors/spec-yaml.d.ts +28 -0
- package/dist/src/extractors/spec-yaml.d.ts.map +1 -0
- package/dist/src/extractors/spec-yaml.js +29 -0
- package/dist/src/gap-planner/action-candidates.d.ts +9 -0
- package/dist/src/gap-planner/action-candidates.d.ts.map +1 -0
- package/dist/src/gap-planner/action-candidates.js +66 -0
- package/dist/src/gap-planner/list-gap-workflows.d.ts +17 -0
- package/dist/src/gap-planner/list-gap-workflows.d.ts.map +1 -0
- package/dist/src/gap-planner/list-gap-workflows.js +47 -0
- package/dist/src/gap-planner/plan-agent-workflows.d.ts +26 -0
- package/dist/src/gap-planner/plan-agent-workflows.d.ts.map +1 -0
- package/dist/src/gap-planner/plan-agent-workflows.js +116 -0
- package/dist/src/gap-planner/skeleton-coverage.d.ts +9 -0
- package/dist/src/gap-planner/skeleton-coverage.d.ts.map +1 -0
- package/dist/src/gap-planner/skeleton-coverage.js +41 -0
- package/dist/src/gap-planner/stable-id.d.ts +16 -0
- package/dist/src/gap-planner/stable-id.d.ts.map +1 -0
- package/dist/src/gap-planner/stable-id.js +19 -0
- package/dist/src/generalization/generalization-eval.d.ts +71 -0
- package/dist/src/generalization/generalization-eval.d.ts.map +1 -0
- package/dist/src/generalization/generalization-eval.js +53 -0
- package/dist/src/generators/agent-inferred-workflow-script.d.ts +22 -0
- package/dist/src/generators/agent-inferred-workflow-script.d.ts.map +1 -0
- package/dist/src/generators/agent-inferred-workflow-script.js +230 -0
- package/dist/src/generators/stage-skeleton-script.d.ts +107 -0
- package/dist/src/generators/stage-skeleton-script.d.ts.map +1 -0
- package/dist/src/generators/stage-skeleton-script.js +607 -0
- package/dist/src/index.d.ts +52 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +26 -0
- package/dist/src/material/material-inventory.d.ts +92 -0
- package/dist/src/material/material-inventory.d.ts.map +1 -0
- package/dist/src/material/material-inventory.js +191 -0
- package/dist/src/normalizers/crud-contract.d.ts +107 -0
- package/dist/src/normalizers/crud-contract.d.ts.map +1 -0
- package/dist/src/normalizers/crud-contract.js +1068 -0
- package/dist/src/testcase/testcase-generator.d.ts +43 -0
- package/dist/src/testcase/testcase-generator.d.ts.map +1 -0
- package/dist/src/testcase/testcase-generator.js +152 -0
- package/dist/src/testcase/testcase-skeleton.d.ts +91 -0
- package/dist/src/testcase/testcase-skeleton.d.ts.map +1 -0
- package/dist/src/testcase/testcase-skeleton.js +121 -0
- package/dist/src/testcase/testcase-spec-assembly.d.ts +11 -0
- package/dist/src/testcase/testcase-spec-assembly.d.ts.map +1 -0
- package/dist/src/testcase/testcase-spec-assembly.js +24 -0
- package/dist/src/trace/review-summary.d.ts +17 -0
- package/dist/src/trace/review-summary.d.ts.map +1 -0
- package/dist/src/trace/review-summary.js +34 -0
- package/dist/src/trace/trace-writer.d.ts +17 -0
- package/dist/src/trace/trace-writer.d.ts.map +1 -0
- package/dist/src/trace/trace-writer.js +81 -0
- package/dist/test/crud-contract.test.d.ts +2 -0
- package/dist/test/crud-contract.test.d.ts.map +1 -0
- package/dist/test/crud-contract.test.js +819 -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 +141 -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/observable-pipeline.test.d.ts +2 -0
- package/dist/test/observable-pipeline.test.d.ts.map +1 -0
- package/dist/test/observable-pipeline.test.js +461 -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/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 +263 -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/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 +134 -0
- package/docs/mvp-usage-guide.md +298 -0
- package/examples/schemeresource-observable-docs/schemeresource.context.md +20 -0
- package/examples/schemeresource.module-hints.json +38 -0
- package/examples/schemeresource.observable.code_list.md +37 -0
- package/examples/zwplace-observable-docs/zwplace.context.md +16 -0
- package/examples/zwplace-placecategory-validation.json +29 -0
- package/examples/zwplace.module-hints.json +69 -0
- package/examples/zwplace.observable.code_list.md +37 -0
- package/package.json +38 -0
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
import { mkdirSync, 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 { applyCodeListWriteBack, collectCodeListMaterialRefsFromPath, extractLoginSystemUrlFromEnvFile, extractCodeListSummary, createRuntimeFailureSummary, isProtectedKnowledgeProjectPath, parseRuntimeSummaryOutput, parseObservablePipelineArgs, resolveMenuRouteForPipeline, runtimeSummaryToTrace, glueSpecAssemblyToTrace, playwrightExecutionToTrace, glueReportToTrace, } from '../src/index.js';
|
|
6
|
+
function createMenuContract() {
|
|
7
|
+
return {
|
|
8
|
+
contractVersion: 'crud-business-module/v1',
|
|
9
|
+
module: {
|
|
10
|
+
id: 'zwplace',
|
|
11
|
+
label: '场所窗口信息管理',
|
|
12
|
+
},
|
|
13
|
+
pages: {
|
|
14
|
+
list: {
|
|
15
|
+
role: 'list',
|
|
16
|
+
pageId: 'gxhzwplacelist',
|
|
17
|
+
title: '场所窗口信息列表',
|
|
18
|
+
iframeSrcKeyword: 'gxhzwplacelist',
|
|
19
|
+
fields: [],
|
|
20
|
+
buttons: [],
|
|
21
|
+
grids: [],
|
|
22
|
+
dialogs: [],
|
|
23
|
+
sources: [],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
dataKey: {
|
|
27
|
+
field: 'windowname',
|
|
28
|
+
label: '窗口名称',
|
|
29
|
+
availability: 'derivable',
|
|
30
|
+
generationStrategy: 'timestamp_prefix',
|
|
31
|
+
sources: [],
|
|
32
|
+
},
|
|
33
|
+
flows: {},
|
|
34
|
+
assertions: [],
|
|
35
|
+
businessRules: [],
|
|
36
|
+
unresolvedSlots: [],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
describe('observable pipeline material inventory', () => {
|
|
40
|
+
it('extracts material refs from code_list markdown', () => {
|
|
41
|
+
const root = mkdtempSync(path.join(os.tmpdir(), 'ep-stage-code-list-'));
|
|
42
|
+
const codeListPath = path.join(root, 'code_list.md');
|
|
43
|
+
mkdirSync(path.join(root, 'src', 'main', 'webapp', 'perpage', 'zwplace'), { recursive: true });
|
|
44
|
+
writeFileSync(codeListPath, [
|
|
45
|
+
'# 场所窗口信息管理代码清单',
|
|
46
|
+
'',
|
|
47
|
+
'| 类型 | 路径 |',
|
|
48
|
+
'| --- | --- |',
|
|
49
|
+
'| HTML | `src/main/webapp/perpage/zwplace/gxhzwplacelist.html` |',
|
|
50
|
+
'| Action | `src/main/java/com/epoint/zwplace/action/GxhZwPlaceListAction.java` |',
|
|
51
|
+
'| 文档 | `_docs/003-场所窗口信息管理-20260518/spec.yaml` |',
|
|
52
|
+
'',
|
|
53
|
+
].join('\n'), 'utf8');
|
|
54
|
+
const summary = extractCodeListSummary(codeListPath);
|
|
55
|
+
// 【opus-4.8修订 N1】extractMaterialRefs 现返回 exists 字段;toEqual 为严格深比较,期望值必须带 exists,否则用例无法转绿。
|
|
56
|
+
// 本用例中三条物料文件均未实际写入(仅建了 webapp 目录),故 exists 均为 false。
|
|
57
|
+
expect(summary.materialRefs).toEqual([
|
|
58
|
+
{
|
|
59
|
+
kind: 'html',
|
|
60
|
+
rawPath: 'src/main/webapp/perpage/zwplace/gxhzwplacelist.html',
|
|
61
|
+
path: path.join(root, 'src/main/webapp/perpage/zwplace/gxhzwplacelist.html'),
|
|
62
|
+
exists: false,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
kind: 'java_action',
|
|
66
|
+
rawPath: 'src/main/java/com/epoint/zwplace/action/GxhZwPlaceListAction.java',
|
|
67
|
+
path: path.join(root, 'src/main/java/com/epoint/zwplace/action/GxhZwPlaceListAction.java'),
|
|
68
|
+
exists: false,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
kind: 'spec',
|
|
72
|
+
rawPath: '_docs/003-场所窗口信息管理-20260518/spec.yaml',
|
|
73
|
+
path: path.join(root, '_docs/003-场所窗口信息管理-20260518/spec.yaml'),
|
|
74
|
+
exists: false,
|
|
75
|
+
},
|
|
76
|
+
]);
|
|
77
|
+
});
|
|
78
|
+
it('resolves paths relative to knowledge root when code_list is under _docs', () => {
|
|
79
|
+
const root = mkdtempSync(path.join(os.tmpdir(), 'ep-stage-knowledge-root-'));
|
|
80
|
+
const docsDir = path.join(root, '_docs');
|
|
81
|
+
const htmlPath = path.join(root, 'src', 'main', 'webapp', 'perpage', 'zwplace', 'gxhzwplacelist.html');
|
|
82
|
+
const codeListPath = path.join(docsDir, 'code_list.md');
|
|
83
|
+
mkdirSync(path.dirname(htmlPath), { recursive: true });
|
|
84
|
+
mkdirSync(docsDir, { recursive: true });
|
|
85
|
+
writeFileSync(htmlPath, '<html></html>');
|
|
86
|
+
writeFileSync(codeListPath, '| HTML | `src/main/webapp/perpage/zwplace/gxhzwplacelist.html` |', 'utf8');
|
|
87
|
+
const summary = extractCodeListSummary(codeListPath);
|
|
88
|
+
expect(summary.materialRefs[0]).toMatchObject({
|
|
89
|
+
kind: 'html',
|
|
90
|
+
path: htmlPath,
|
|
91
|
+
exists: true,
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
it('uses explicit knowledgeRoot when code_list is outside the knowledge project', () => {
|
|
95
|
+
const root = mkdtempSync(path.join(os.tmpdir(), 'ep-stage-explicit-knowledge-root-'));
|
|
96
|
+
const knowledgeRoot = path.join(root, 'knowledge-project', 'zwplace');
|
|
97
|
+
const externalDir = path.join(root, 'agent-input');
|
|
98
|
+
const htmlPath = path.join(knowledgeRoot, 'src', 'main', 'webapp', 'perpage', 'zwplace', 'gxhzwplacelist.html');
|
|
99
|
+
const codeListPath = path.join(externalDir, 'code_list.md');
|
|
100
|
+
mkdirSync(path.dirname(htmlPath), { recursive: true });
|
|
101
|
+
mkdirSync(externalDir, { recursive: true });
|
|
102
|
+
writeFileSync(htmlPath, '<html></html>');
|
|
103
|
+
writeFileSync(codeListPath, '| HTML | `src/main/webapp/perpage/zwplace/gxhzwplacelist.html` |', 'utf8');
|
|
104
|
+
const summary = extractCodeListSummary(codeListPath, { knowledgeRoot });
|
|
105
|
+
expect(summary.materialRefs[0]).toMatchObject({
|
|
106
|
+
kind: 'html',
|
|
107
|
+
path: htmlPath,
|
|
108
|
+
exists: true,
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
it('uses knowledgeRoot declared inside an external code_list as the material root', () => {
|
|
112
|
+
const root = mkdtempSync(path.join(os.tmpdir(), 'ep-stage-code-list-declared-root-'));
|
|
113
|
+
const knowledgeRoot = path.join(root, 'knowledge-project', 'zwplace');
|
|
114
|
+
const externalDir = path.join(root, 'agent-input');
|
|
115
|
+
const htmlPath = path.join(knowledgeRoot, 'src', 'main', 'webapp', 'perpage', 'zwplace', 'gxhzwplacelist.html');
|
|
116
|
+
const codeListPath = path.join(externalDir, 'code_list.md');
|
|
117
|
+
mkdirSync(path.dirname(htmlPath), { recursive: true });
|
|
118
|
+
mkdirSync(externalDir, { recursive: true });
|
|
119
|
+
writeFileSync(htmlPath, '<html></html>');
|
|
120
|
+
writeFileSync(codeListPath, [
|
|
121
|
+
'# 场所窗口信息管理代码清单',
|
|
122
|
+
'',
|
|
123
|
+
`knowledgeRoot: ${knowledgeRoot}`,
|
|
124
|
+
'',
|
|
125
|
+
'| HTML | `src/main/webapp/perpage/zwplace/gxhzwplacelist.html` |',
|
|
126
|
+
'',
|
|
127
|
+
].join('\n'), 'utf8');
|
|
128
|
+
const summary = extractCodeListSummary(codeListPath);
|
|
129
|
+
expect(summary.knowledgeRoot).toBe(knowledgeRoot);
|
|
130
|
+
expect(summary.knowledgeRootSource).toBe('code_list');
|
|
131
|
+
expect(summary.materialRefs[0]).toMatchObject({
|
|
132
|
+
kind: 'html',
|
|
133
|
+
path: htmlPath,
|
|
134
|
+
exists: true,
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
it('fails fast when code_list declares a missing knowledgeRoot', () => {
|
|
138
|
+
const root = mkdtempSync(path.join(os.tmpdir(), 'ep-stage-code-list-missing-root-'));
|
|
139
|
+
const codeListPath = path.join(root, 'code_list.md');
|
|
140
|
+
const missingKnowledgeRoot = path.join(root, 'missing-knowledge');
|
|
141
|
+
writeFileSync(codeListPath, [
|
|
142
|
+
'# 场所窗口信息管理代码清单',
|
|
143
|
+
'',
|
|
144
|
+
`knowledgeRoot: ${missingKnowledgeRoot}`,
|
|
145
|
+
'',
|
|
146
|
+
'| HTML | `src/main/webapp/perpage/zwplace/gxhzwplacelist.html` |',
|
|
147
|
+
'',
|
|
148
|
+
].join('\n'), 'utf8');
|
|
149
|
+
expect(() => extractCodeListSummary(codeListPath)).toThrow('code_list.md 声明的 knowledgeRoot 不存在');
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
/**
|
|
153
|
+
* 可观测 pipeline 参数解析测试。
|
|
154
|
+
*
|
|
155
|
+
* 验证默认入口只需 --code-list,--scenario 为场景 gate 人工确认值,
|
|
156
|
+
* 调试覆盖参数可并存。
|
|
157
|
+
*/
|
|
158
|
+
describe('observable pipeline args', () => {
|
|
159
|
+
it('accepts code-list as the only required user entry', () => {
|
|
160
|
+
const args = parseObservablePipelineArgs([
|
|
161
|
+
'--code-list', '/repo/knowledge/_docs/code_list.md',
|
|
162
|
+
]);
|
|
163
|
+
expect(args.codeList).toBe('/repo/knowledge/_docs/code_list.md');
|
|
164
|
+
expect(args.debugOverrides).toEqual({});
|
|
165
|
+
expect(args.scenario).toBeUndefined();
|
|
166
|
+
});
|
|
167
|
+
it('accepts --scenario as the human confirmation for the scenario gate', () => {
|
|
168
|
+
const args = parseObservablePipelineArgs([
|
|
169
|
+
'--code-list', '/repo/knowledge/_docs/code_list.md',
|
|
170
|
+
'--scenario', 'crud.single-page',
|
|
171
|
+
]);
|
|
172
|
+
expect(args.scenario).toBe('crud.single-page');
|
|
173
|
+
});
|
|
174
|
+
it('accepts --confirm-testcases as the human confirmation for testcase gate', () => {
|
|
175
|
+
const args = parseObservablePipelineArgs([
|
|
176
|
+
'--code-list', '/repo/knowledge/_docs/code_list.md',
|
|
177
|
+
'--scenario', 'crud.nested',
|
|
178
|
+
'--confirm-testcases', 'true',
|
|
179
|
+
]);
|
|
180
|
+
expect(args.confirmTestcases).toBe(true);
|
|
181
|
+
});
|
|
182
|
+
it('accepts code-list as default entry and keeps debug overrides', () => {
|
|
183
|
+
const args = parseObservablePipelineArgs([
|
|
184
|
+
'--code-list', '/repo/knowledge/_docs/code_list.md',
|
|
185
|
+
'--docs', '/repo/knowledge/_docs/003',
|
|
186
|
+
'--webapp', '/repo/knowledge/src/main/webapp/perpage/zwplace',
|
|
187
|
+
'--java-actions', '/repo/knowledge/src/main/java/com/epoint/zwplace/action',
|
|
188
|
+
'--module-id', 'zwplace',
|
|
189
|
+
'--menu', '场所窗口信息管理>场所窗口信息列表',
|
|
190
|
+
'--headless', 'false',
|
|
191
|
+
]);
|
|
192
|
+
expect(args.codeList).toBe('/repo/knowledge/_docs/code_list.md');
|
|
193
|
+
expect(args.debugOverrides.docs).toBe('/repo/knowledge/_docs/003');
|
|
194
|
+
expect(args.debugOverrides.webapp).toBe('/repo/knowledge/src/main/webapp/perpage/zwplace');
|
|
195
|
+
expect(args.headless).toBe(false);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
describe('interactive code_list write-back helpers', () => {
|
|
199
|
+
it('writes knowledgeRoot/menu and appends missing material refs into code_list markdown', () => {
|
|
200
|
+
const nextContent = applyCodeListWriteBack([
|
|
201
|
+
'# 场所窗口信息管理代码清单',
|
|
202
|
+
'',
|
|
203
|
+
'## 前端文件',
|
|
204
|
+
'',
|
|
205
|
+
'- `src/main/webapp/perpage/zwplace/gxhzwplacelist.html`',
|
|
206
|
+
'',
|
|
207
|
+
].join('\n'), {
|
|
208
|
+
knowledgeRoot: '../knowledge-project/epoint-web-v9.5.2',
|
|
209
|
+
menu: '场所窗口信息管理>场所窗口信息列表',
|
|
210
|
+
materialRefs: [
|
|
211
|
+
'./zwplace.module-hints.json',
|
|
212
|
+
'src/main/java/com/epoint/zwplace/action/GxhZwPlaceListAction.java',
|
|
213
|
+
],
|
|
214
|
+
});
|
|
215
|
+
expect(nextContent).toContain('knowledgeRoot: "../knowledge-project/epoint-web-v9.5.2"');
|
|
216
|
+
expect(nextContent).toContain('menu: "场所窗口信息管理>场所窗口信息列表"');
|
|
217
|
+
expect(nextContent).toContain('## 交互补齐物料');
|
|
218
|
+
expect(nextContent).toContain('`./zwplace.module-hints.json`');
|
|
219
|
+
expect(nextContent).toContain('`src/main/java/com/epoint/zwplace/action/GxhZwPlaceListAction.java`');
|
|
220
|
+
});
|
|
221
|
+
it('expands a directory into sorted html and action refs for code_list write-back', () => {
|
|
222
|
+
const root = mkdtempSync(path.join(os.tmpdir(), 'ep-stage-writeback-refs-'));
|
|
223
|
+
const codeListPath = path.join(root, 'agent-input', 'code_list.md');
|
|
224
|
+
const htmlDir = path.join(root, 'knowledge', 'src', 'main', 'webapp', 'perpage', 'zwplace');
|
|
225
|
+
const actionDir = path.join(root, 'knowledge', 'src', 'main', 'java', 'com', 'epoint', 'zwplace', 'action');
|
|
226
|
+
mkdirSync(path.dirname(codeListPath), { recursive: true });
|
|
227
|
+
mkdirSync(htmlDir, { recursive: true });
|
|
228
|
+
mkdirSync(actionDir, { recursive: true });
|
|
229
|
+
writeFileSync(codeListPath, '# 场所窗口信息管理代码清单\n');
|
|
230
|
+
writeFileSync(path.join(htmlDir, 'b.html'), '<html></html>');
|
|
231
|
+
writeFileSync(path.join(htmlDir, 'a.html'), '<html></html>');
|
|
232
|
+
writeFileSync(path.join(actionDir, 'BAction.java'), 'class BAction {}');
|
|
233
|
+
writeFileSync(path.join(actionDir, 'AAction.java'), 'class AAction {}');
|
|
234
|
+
expect(collectCodeListMaterialRefsFromPath(codeListPath, htmlDir, 'html')).toEqual([
|
|
235
|
+
'../knowledge/src/main/webapp/perpage/zwplace/a.html',
|
|
236
|
+
'../knowledge/src/main/webapp/perpage/zwplace/b.html',
|
|
237
|
+
]);
|
|
238
|
+
expect(collectCodeListMaterialRefsFromPath(codeListPath, actionDir, 'java_action')).toEqual([
|
|
239
|
+
'../knowledge/src/main/java/com/epoint/zwplace/action/AAction.java',
|
|
240
|
+
'../knowledge/src/main/java/com/epoint/zwplace/action/BAction.java',
|
|
241
|
+
]);
|
|
242
|
+
});
|
|
243
|
+
it('marks main-repo knowledge-project paths as protected for write-back', () => {
|
|
244
|
+
expect(isProtectedKnowledgeProjectPath('/Users/taurus/0-ws/01-work-repo/eai-method-qa/ep-stage/knowledge-project/epoint-web-v9.5.2/_docs/code_list.md')).toBe(true);
|
|
245
|
+
expect(isProtectedKnowledgeProjectPath('/Users/taurus/0-ws/01-work-repo/eai-method-qa/ep-stage/packages/ep-stage-skill/examples/zwplace.observable.code_list.md')).toBe(false);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
describe('pipeline menu resolution', () => {
|
|
249
|
+
it('prefers resolved menu inputs over inferred candidates', () => {
|
|
250
|
+
const resolution = resolveMenuRouteForPipeline({
|
|
251
|
+
menu: { status: 'resolved', value: '显式菜单>目标页', evidence: 'menu 来自 code_list.md 扎口字段' },
|
|
252
|
+
contract: createMenuContract(),
|
|
253
|
+
});
|
|
254
|
+
expect(resolution).toMatchObject({
|
|
255
|
+
status: 'resolved',
|
|
256
|
+
value: '显式菜单>目标页',
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
it('infers a reviewable menu candidate from contract module label and list page title', () => {
|
|
260
|
+
const resolution = resolveMenuRouteForPipeline({
|
|
261
|
+
menu: { status: 'missing', reason: '未提供 menu' },
|
|
262
|
+
contract: createMenuContract(),
|
|
263
|
+
});
|
|
264
|
+
expect(resolution.status).toBe('candidate');
|
|
265
|
+
if (resolution.status !== 'candidate') {
|
|
266
|
+
throw new Error('expected candidate menu resolution');
|
|
267
|
+
}
|
|
268
|
+
expect(resolution.candidates).toContain('场所窗口信息管理>场所窗口信息列表');
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
/**
|
|
272
|
+
* runtime gap 推理 trace 转换测试。
|
|
273
|
+
*
|
|
274
|
+
* 验证 runtimeSummaryToTrace 把 runtime 摘要转为可审阅 trace payload,
|
|
275
|
+
* 存在 needsReview workflow 时 gate 为 needs_review。
|
|
276
|
+
*/
|
|
277
|
+
describe('runtime gap trace', () => {
|
|
278
|
+
it('parses sentinel runtime JSON without being confused by nested objects', () => {
|
|
279
|
+
const summary = parseRuntimeSummaryOutput([
|
|
280
|
+
'runtime log before json',
|
|
281
|
+
'@@EP_STAGE_RUNTIME_SUMMARY@@',
|
|
282
|
+
JSON.stringify({
|
|
283
|
+
specPath: '/repo/e2e/glue-code-mvp/src/tests/generated-zwplace.gap.spec.ts',
|
|
284
|
+
resolvedWorkflows: ['zwplace.syncWindow'],
|
|
285
|
+
skippedWorkflows: [],
|
|
286
|
+
needsReviewWorkflows: [],
|
|
287
|
+
pageStructureSignals: [
|
|
288
|
+
{
|
|
289
|
+
selector: "div[class*='mini-datagrid']",
|
|
290
|
+
staticPresent: true,
|
|
291
|
+
runtimeVisible: true,
|
|
292
|
+
runtimeMismatch: false,
|
|
293
|
+
evidenceText: '列表可见',
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
}, null, 2),
|
|
297
|
+
].join('\n'));
|
|
298
|
+
expect(summary.pageStructureSignals?.[0]).toMatchObject({
|
|
299
|
+
selector: "div[class*='mini-datagrid']",
|
|
300
|
+
runtimeVisible: true,
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
it('converts runtime summary into reviewable trace payload', () => {
|
|
304
|
+
const trace = runtimeSummaryToTrace({
|
|
305
|
+
specPath: '/repo/e2e/glue-code-mvp/src/tests/generated-zwplace.gap.spec.ts',
|
|
306
|
+
resolvedWorkflows: ['zwplace.syncWindow'],
|
|
307
|
+
skippedWorkflows: ['zwplace.export'],
|
|
308
|
+
needsReviewWorkflows: ['zwplace.addWindow'],
|
|
309
|
+
});
|
|
310
|
+
expect(trace.stage).toBe('runtime-gap-inference');
|
|
311
|
+
expect(trace.reasoningSummary).toMatchObject({
|
|
312
|
+
conclusion: '运行时已解决 1 个 workflow,1 个仍需人工审阅。',
|
|
313
|
+
needsHumanReview: true,
|
|
314
|
+
});
|
|
315
|
+
expect(trace.gate.status).toBe('needs_review');
|
|
316
|
+
});
|
|
317
|
+
it('records per-workflow runtime failure reasons in reviewable trace payload', () => {
|
|
318
|
+
const trace = runtimeSummaryToTrace({
|
|
319
|
+
specPath: '/repo/e2e/glue-code-mvp/src/tests/generated-zwplace.gap.spec.ts',
|
|
320
|
+
resolvedWorkflows: [],
|
|
321
|
+
skippedWorkflows: [],
|
|
322
|
+
needsReviewWorkflows: ['zwplace.addWindow'],
|
|
323
|
+
needsReviewWorkflowReasons: [
|
|
324
|
+
{
|
|
325
|
+
workflowId: 'zwplace.addWindow',
|
|
326
|
+
reason: '定位器未在业务 iframe 中可见',
|
|
327
|
+
},
|
|
328
|
+
],
|
|
329
|
+
});
|
|
330
|
+
expect(trace.reasoningSummary?.evidenceChain).toContainEqual({
|
|
331
|
+
source: 'runtime',
|
|
332
|
+
path: '/repo/e2e/glue-code-mvp/src/tests/generated-zwplace.gap.spec.ts',
|
|
333
|
+
text: 'needs_review=zwplace.addWindow reason=定位器未在业务 iframe 中可见',
|
|
334
|
+
});
|
|
335
|
+
expect(trace.reasoningSummary?.risks).toContain('zwplace.addWindow 仍需人工审阅:定位器未在业务 iframe 中可见');
|
|
336
|
+
});
|
|
337
|
+
it('records runtime page structure signals in trace outputs', () => {
|
|
338
|
+
const trace = runtimeSummaryToTrace({
|
|
339
|
+
specPath: '/repo/e2e/glue-code-mvp/src/tests/generated-zwplace.gap.spec.ts',
|
|
340
|
+
resolvedWorkflows: ['zwplace.syncWindow'],
|
|
341
|
+
skippedWorkflows: [],
|
|
342
|
+
needsReviewWorkflows: [],
|
|
343
|
+
pageStructureSignals: [
|
|
344
|
+
{
|
|
345
|
+
selector: "div[class*='mini-datagrid']",
|
|
346
|
+
staticPresent: true,
|
|
347
|
+
runtimeVisible: true,
|
|
348
|
+
runtimeMismatch: false,
|
|
349
|
+
evidenceText: '列表 iframe 中 mini-datagrid 可见',
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
});
|
|
353
|
+
expect(trace.outputs).toContainEqual({
|
|
354
|
+
kind: 'runtime_page_structure',
|
|
355
|
+
path: "div[class*='mini-datagrid']",
|
|
356
|
+
description: 'visible',
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
it('keeps runtime implementation failures observable instead of classifying all failures as environment issues', () => {
|
|
360
|
+
const runtimeSummary = createRuntimeFailureSummary({
|
|
361
|
+
plannedWorkflows: ['zwplace.addWindow'],
|
|
362
|
+
specPath: '/repo/e2e/glue-code-mvp/src/tests/generated-zwplace.gap.spec.ts',
|
|
363
|
+
status: 1,
|
|
364
|
+
stdout: '',
|
|
365
|
+
stderr: 'SyntaxError: Unexpected token in runtime-runner.ts',
|
|
366
|
+
});
|
|
367
|
+
const trace = runtimeSummaryToTrace(runtimeSummary);
|
|
368
|
+
expect(runtimeSummary.runtimeStatus).toBe('failed');
|
|
369
|
+
expect(runtimeSummary.runtimePageStructureStatus).toBe('failed');
|
|
370
|
+
expect(trace.gate.status).toBe('needs_review');
|
|
371
|
+
expect(trace.reasoningSummary?.risks[0]).toContain('SyntaxError');
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
/**
|
|
375
|
+
* 尾段(spec assembly / playwright execution / glue-report)trace 转换测试。
|
|
376
|
+
*/
|
|
377
|
+
describe('observable pipeline tail traces', () => {
|
|
378
|
+
it('extracts LOGIN_SYSTEM_URL from a project env file', () => {
|
|
379
|
+
const root = mkdtempSync(path.join(os.tmpdir(), 'ep-stage-login-env-'));
|
|
380
|
+
const envPath = path.join(root, '.env');
|
|
381
|
+
writeFileSync(envPath, [
|
|
382
|
+
'# Stage 环境变量配置',
|
|
383
|
+
'LOGIN_SYSTEM_URL=http://192.168.203.46:8100/epoint-web/frame/pages/login/login',
|
|
384
|
+
'LOGIN_USERNAME=yz',
|
|
385
|
+
].join('\n'), 'utf8');
|
|
386
|
+
expect(extractLoginSystemUrlFromEnvFile(envPath)).toBe('http://192.168.203.46:8100/epoint-web/frame/pages/login/login');
|
|
387
|
+
});
|
|
388
|
+
it('converts spec assembly, playwright execution and glue report into trace payloads', () => {
|
|
389
|
+
const specTrace = glueSpecAssemblyToTrace({
|
|
390
|
+
contractPath: '/repo/packages/ep-stage-skill/output/zwplace.crud.contract.json',
|
|
391
|
+
specPath: '/repo/e2e/glue-code-mvp/src/tests/generated-zwplace.gap.spec.ts',
|
|
392
|
+
menu: '场所窗口信息管理>场所窗口信息列表',
|
|
393
|
+
testcasePath: '/repo/e2e/glue-code-mvp/testcases/zwplace.glue-testcase.json',
|
|
394
|
+
generated: true,
|
|
395
|
+
});
|
|
396
|
+
expect(specTrace).toMatchObject({
|
|
397
|
+
stage: 'glue-spec-assembly',
|
|
398
|
+
gate: { status: 'confirmed' },
|
|
399
|
+
reasoningSummary: { needsHumanReview: false },
|
|
400
|
+
});
|
|
401
|
+
const playwrightTrace = playwrightExecutionToTrace({
|
|
402
|
+
specPath: '/repo/e2e/glue-code-mvp/src/tests/generated-zwplace.gap.spec.ts',
|
|
403
|
+
project: 'chromium',
|
|
404
|
+
status: 'environment_unavailable',
|
|
405
|
+
exitCode: 1,
|
|
406
|
+
stderr: 'page.goto: net::ERR_EMPTY_RESPONSE',
|
|
407
|
+
loginUrl: 'http://192.168.203.46:8100/epoint-web/frame/pages/login/login',
|
|
408
|
+
environmentEvidence: 'curl -I: Empty reply from server',
|
|
409
|
+
});
|
|
410
|
+
expect(playwrightTrace.gate.status).toBe('needs_review');
|
|
411
|
+
expect(playwrightTrace.reasoningSummary?.risks[0]).toContain('ERR_EMPTY_RESPONSE');
|
|
412
|
+
expect(playwrightTrace.reasoningSummary?.evidenceChain).toContainEqual({
|
|
413
|
+
source: 'runtime',
|
|
414
|
+
path: 'http://192.168.203.46:8100/epoint-web/frame/pages/login/login',
|
|
415
|
+
text: '环境预检:curl -I: Empty reply from server',
|
|
416
|
+
});
|
|
417
|
+
const reportTrace = glueReportToTrace({
|
|
418
|
+
projectDir: '/repo/e2e/glue-code-mvp',
|
|
419
|
+
runTimeStamp: '2026-06-16T12:00:00.000Z',
|
|
420
|
+
stageGlueReportJsonPath: '/repo/e2e/glue-code-mvp/glue-report/report.json',
|
|
421
|
+
stageGlueReportHtmlPath: '/repo/e2e/glue-code-mvp/glue-report/index.html',
|
|
422
|
+
});
|
|
423
|
+
expect(reportTrace.outputs).toContainEqual({
|
|
424
|
+
kind: 'glue_report_json',
|
|
425
|
+
path: '/repo/e2e/glue-code-mvp/glue-report/report.json',
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
it('surfaces feature_incomplete gap findings from glue-report in trace evidence', () => {
|
|
429
|
+
const reportTrace = glueReportToTrace({
|
|
430
|
+
projectDir: '/repo/e2e/glue-code-mvp',
|
|
431
|
+
stageGlueReportJsonPath: '/repo/e2e/glue-code-mvp/glue-report/report.json',
|
|
432
|
+
failedWorkflowSummaries: [
|
|
433
|
+
'zwplace.custom_da3d238e(同步窗口管理系统): 同步窗口管理系统未达成业务预期',
|
|
434
|
+
],
|
|
435
|
+
gapFindingSummaries: [
|
|
436
|
+
'zwplace.custom_da3d238e: feature_incomplete - 运行态推理发现「同步窗口管理系统」未满足业务预期。',
|
|
437
|
+
],
|
|
438
|
+
});
|
|
439
|
+
expect(reportTrace.reasoningSummary?.conclusion).toContain('业务缺口');
|
|
440
|
+
expect(reportTrace.reasoningSummary?.evidenceChain).toContainEqual({
|
|
441
|
+
source: 'runtime',
|
|
442
|
+
path: '/repo/e2e/glue-code-mvp/glue-report/report.json',
|
|
443
|
+
text: 'zwplace.custom_da3d238e: feature_incomplete - 运行态推理发现「同步窗口管理系统」未满足业务预期。',
|
|
444
|
+
});
|
|
445
|
+
expect(reportTrace.reasoningSummary?.risks).toContain('zwplace.custom_da3d238e: feature_incomplete - 运行态推理发现「同步窗口管理系统」未满足业务预期。');
|
|
446
|
+
});
|
|
447
|
+
it('records spec assembly failure as a reviewable trace payload', () => {
|
|
448
|
+
const trace = glueSpecAssemblyToTrace({
|
|
449
|
+
contractPath: '/repo/packages/ep-stage-skill/output/zwplace.crud.contract.json',
|
|
450
|
+
specPath: '/repo/e2e/glue-code-mvp/src/tests/generated-zwplace.gap.spec.ts',
|
|
451
|
+
menu: '场所窗口信息管理>场所窗口信息列表',
|
|
452
|
+
testcasePath: '/repo/e2e/glue-code-mvp/testcases/zwplace.glue-testcase.json',
|
|
453
|
+
generated: false,
|
|
454
|
+
exitCode: 1,
|
|
455
|
+
stderr: 'generate:playwright-tests failed',
|
|
456
|
+
});
|
|
457
|
+
expect(trace.outputs).toEqual([]);
|
|
458
|
+
expect(trace.gate.status).toBe('needs_review');
|
|
459
|
+
expect(trace.reasoningSummary?.risks[0]).toContain('generate:playwright-tests failed');
|
|
460
|
+
});
|
|
461
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-structure.test.d.ts","sourceRoot":"","sources":["../../test/page-structure.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,45 @@
|
|
|
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 { extractHtmlPage, summarizePageStructure } from '../src/index.js';
|
|
6
|
+
/**
|
|
7
|
+
* 页面结构信号抽取与场景归纳测试。
|
|
8
|
+
*
|
|
9
|
+
* 验证 extractHtmlPage 能从 HTML 静态结构钻探出 mini-datagrid / fui-condition /
|
|
10
|
+
* mini-grid-pager 等结构信号,且 summarizePageStructure 能据此识别嵌套 CRUD 候选。
|
|
11
|
+
*/
|
|
12
|
+
describe('page structure signals', () => {
|
|
13
|
+
it('extracts MiniUI grid, condition, pager and nested crud signals', () => {
|
|
14
|
+
const dir = mkdtempSync(path.join(os.tmpdir(), 'ep-stage-html-'));
|
|
15
|
+
const htmlPath = path.join(dir, 'gxhzwplaceadd.html');
|
|
16
|
+
writeFileSync(htmlPath, [
|
|
17
|
+
'<html><head><title>新增场所信息管理</title></head><body>',
|
|
18
|
+
'<div class="fui-condition"><input /></div>',
|
|
19
|
+
'<a class="mini-button" onclick="addWindow()">添加窗口</a>',
|
|
20
|
+
'<a class="mini-button" onclick="deleteWindow()">删除选中</a>',
|
|
21
|
+
'<div id="windowGrid" class="mini-datagrid">',
|
|
22
|
+
' <div property="columns">',
|
|
23
|
+
' <div type="checkcolumn"></div>',
|
|
24
|
+
' <div type="indexcolumn">序</div>',
|
|
25
|
+
' <div field="windowname">窗口名称</div>',
|
|
26
|
+
' <div field="windowtype">窗口类型</div>',
|
|
27
|
+
' <div field="windowaddr">窗口地址</div>',
|
|
28
|
+
' </div>',
|
|
29
|
+
'</div>',
|
|
30
|
+
'<div class="mini-grid-pager"></div>',
|
|
31
|
+
'<script>function openChild(){ epoint.openTopDialog("新增窗口", "gxhwindowadd.html"); }</script>',
|
|
32
|
+
'</body></html>',
|
|
33
|
+
].join('\n'), 'utf8');
|
|
34
|
+
const page = extractHtmlPage(htmlPath);
|
|
35
|
+
expect(page.pageStructureSignals).toMatchObject({
|
|
36
|
+
dataGrid: { staticPresent: true, selector: "div[class*='mini-datagrid']" },
|
|
37
|
+
searchCondition: { staticPresent: true, selector: "div[class*='fui-condition']" },
|
|
38
|
+
pager: { staticPresent: true, selector: "div[class*='mini-grid-pager']" },
|
|
39
|
+
});
|
|
40
|
+
const summary = summarizePageStructure(page);
|
|
41
|
+
expect(summary.nestedCrudCandidate).toBe(true);
|
|
42
|
+
expect(summary.evidenceLabels).toContain('可见 mini-datagrid 结构存在');
|
|
43
|
+
expect(summary.evidenceLabels).toContain('可见 mini-grid-pager 结构存在');
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenario-inference.test.d.ts","sourceRoot":"","sources":["../../test/scenario-inference.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { applyScenarioHumanCorrection, inferPageScenarios, } from '../src/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* 场景推理门禁测试。
|
|
5
|
+
*
|
|
6
|
+
* 验证 inferPageScenarios 能从页面结构候选推出嵌套 CRUD 候选并停在 needs_review gate,
|
|
7
|
+
* 且 applyScenarioHumanCorrection 能记录人工纠偏并要求重跑。
|
|
8
|
+
*/
|
|
9
|
+
describe('scenario inference gate', () => {
|
|
10
|
+
it('infers nested CRUD candidate and requires human confirmation', () => {
|
|
11
|
+
const pages = [
|
|
12
|
+
{
|
|
13
|
+
pageId: 'gxhzwplacelist',
|
|
14
|
+
readSearchCandidate: true,
|
|
15
|
+
createCandidate: true,
|
|
16
|
+
deleteCandidate: true,
|
|
17
|
+
updateOrDetailCandidate: true,
|
|
18
|
+
nestedCrudCandidate: false,
|
|
19
|
+
evidenceLabels: ['可见 mini-datagrid 结构存在', '可见 mini-grid-pager 结构存在'],
|
|
20
|
+
risks: [],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
pageId: 'gxhzwplaceadd',
|
|
24
|
+
readSearchCandidate: true,
|
|
25
|
+
createCandidate: true,
|
|
26
|
+
deleteCandidate: true,
|
|
27
|
+
updateOrDetailCandidate: false,
|
|
28
|
+
nestedCrudCandidate: true,
|
|
29
|
+
evidenceLabels: ['弹窗页面存在 mini-datagrid', '按钮 添加窗口', '按钮 删除选中'],
|
|
30
|
+
risks: ['嵌套 CRUD 断言和保存边界需要人工确认'],
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
const result = inferPageScenarios({
|
|
34
|
+
moduleId: 'zwplace',
|
|
35
|
+
pages,
|
|
36
|
+
skeletonCapabilities: ['crud.create', 'crud.read', 'crud.update', 'crud.delete', 'export.run'],
|
|
37
|
+
});
|
|
38
|
+
expect(result.candidates[0]).toMatchObject({
|
|
39
|
+
scenario: 'crud.nested',
|
|
40
|
+
confidence: 'medium',
|
|
41
|
+
matchedSkeletonCapabilities: ['crud.create', 'crud.read', 'crud.delete'],
|
|
42
|
+
unresolved: ['嵌套 CRUD 断言和保存边界需要人工确认'],
|
|
43
|
+
});
|
|
44
|
+
expect(result.gate.status).toBe('needs_review');
|
|
45
|
+
expect(result.reasoningSummary.needsHumanReview).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
it('records human correction and requires rerun', () => {
|
|
48
|
+
const corrected = applyScenarioHumanCorrection({
|
|
49
|
+
inference: {
|
|
50
|
+
moduleId: 'zwplace',
|
|
51
|
+
candidates: [],
|
|
52
|
+
gate: { status: 'needs_review', reason: '未识别到场景' },
|
|
53
|
+
reasoningSummary: {
|
|
54
|
+
conclusion: '未识别到稳定场景。',
|
|
55
|
+
evidenceChain: [],
|
|
56
|
+
alternatives: ['unknown'],
|
|
57
|
+
confidence: 'low',
|
|
58
|
+
risks: ['证据不足'],
|
|
59
|
+
needsHumanReview: true,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
correction: '弹窗 iframe 中存在窗口明细嵌套 CRUD,重点检查添加窗口和删除选中。',
|
|
63
|
+
});
|
|
64
|
+
expect(corrected.conversationReview).toMatchObject({
|
|
65
|
+
status: 'corrected',
|
|
66
|
+
rerunRequired: true,
|
|
67
|
+
});
|
|
68
|
+
expect(corrected.reasoningSummary.evidenceChain[0]).toMatchObject({
|
|
69
|
+
source: 'human',
|
|
70
|
+
text: '弹窗 iframe 中存在窗口明细嵌套 CRUD,重点检查添加窗口和删除选中。',
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stage-context.test.d.ts","sourceRoot":"","sources":["../../test/stage-context.test.ts"],"names":[],"mappings":""}
|