@epoint-testtech/ep-stage-skill 0.0.3-alpha.2 → 0.0.4-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 +2 -2
- package/codex-skill/ep-stage/.omc/state/sessions/d636ad35-5fc9-4d5e-85b0-2bcad7d86ee5/pre-tool-advisory-throttle.json +10 -0
- package/codex-skill/ep-stage/glue-create-project/SKILL.md +206 -0
- package/codex-skill/ep-stage/glue-generate-testcase/SKILL.md +274 -0
- package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-schema.md +112 -0
- package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-tpl.md +72 -0
- package/codex-skill/ep-stage/glue-run-test/SKILL.md +289 -0
- package/codex-skill/ep-stage/glue-run-test/references/crud-pipeline.md +145 -0
- package/codex-skill/ep-stage/{glue-test → glue-run-test}/scripts/generate-crud-spec.mjs +3 -3
- package/codex-skill/ep-stage/recording-to-glue/SKILL.md +1 -0
- package/codex-skill/ep-stage/scripts/validate-skill.mjs +29 -7
- package/dist/src/cli/dev/extract-contract.d.ts +14 -0
- package/dist/src/cli/dev/extract-contract.d.ts.map +1 -0
- package/dist/src/cli/dev/extract-contract.js +114 -0
- package/dist/src/cli/generate-crud-contract.js +7 -77
- package/dist/src/cli/generate-playwright-tests.d.ts +0 -28
- package/dist/src/cli/generate-playwright-tests.d.ts.map +1 -1
- package/dist/src/cli/generate-playwright-tests.js +4 -81
- package/dist/src/cli/generate-testcase.d.ts +84 -0
- package/dist/src/cli/generate-testcase.d.ts.map +1 -0
- package/dist/src/cli/generate-testcase.js +300 -0
- package/dist/src/cli/index.d.ts +26 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +75 -0
- package/dist/src/cli/probe.d.ts +46 -0
- package/dist/src/cli/probe.d.ts.map +1 -0
- package/dist/src/cli/probe.js +447 -0
- package/dist/src/cli/run-gap-pipeline.js +4 -0
- package/dist/src/cli/run.d.ts +63 -0
- package/dist/src/cli/run.d.ts.map +1 -0
- package/dist/src/cli/run.js +116 -0
- package/dist/src/cli/spec.d.ts +48 -0
- package/dist/src/cli/spec.d.ts.map +1 -0
- package/dist/src/cli/spec.js +102 -0
- package/dist/src/context/stage-context.d.ts +72 -8
- package/dist/src/context/stage-context.d.ts.map +1 -1
- package/dist/src/context/stage-context.js +61 -15
- 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/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/generators/stage-skeleton-script.d.ts.map +1 -1
- package/dist/src/generators/stage-skeleton-script.js +9 -0
- 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/testcase/testcase-generator.d.ts.map +1 -1
- package/dist/src/testcase/testcase-generator.js +4 -0
- package/dist/src/testcase/testcase-v2.d.ts +50 -0
- package/dist/src/testcase/testcase-v2.d.ts.map +1 -0
- package/dist/src/testcase/testcase-v2.js +1 -0
- package/dist/src/util/credentials.d.ts +12 -0
- package/dist/src/util/credentials.d.ts.map +1 -0
- package/dist/src/util/credentials.js +19 -0
- package/dist/src/util/i18n-testcase.d.ts +8 -0
- package/dist/src/util/i18n-testcase.d.ts.map +1 -0
- package/dist/src/util/i18n-testcase.js +55 -0
- package/dist/src/util/softlink.d.ts +33 -0
- package/dist/src/util/softlink.d.ts.map +1 -0
- package/dist/src/util/softlink.js +43 -0
- package/dist/src/validation/credentials.d.ts +19 -0
- package/dist/src/validation/credentials.d.ts.map +1 -0
- package/dist/src/validation/credentials.js +38 -0
- package/dist/src/validation/index.d.ts +6 -0
- package/dist/src/validation/index.d.ts.map +1 -0
- package/dist/src/validation/index.js +4 -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/projects-index.d.ts +13 -0
- package/dist/src/validation/projects-index.d.ts.map +1 -0
- package/dist/src/validation/projects-index.js +37 -0
- package/dist/src/validation/testcase.d.ts +13 -0
- package/dist/src/validation/testcase.d.ts.map +1 -0
- package/dist/src/validation/testcase.js +53 -0
- package/dist/test/cli/extract-contract.test.d.ts +2 -0
- package/dist/test/cli/extract-contract.test.d.ts.map +1 -0
- package/dist/test/cli/extract-contract.test.js +32 -0
- package/dist/test/cli/generate-testcase-inference.test.d.ts +2 -0
- package/dist/test/cli/generate-testcase-inference.test.d.ts.map +1 -0
- package/dist/test/cli/generate-testcase-inference.test.js +63 -0
- package/dist/test/cli/generate-testcase-paths.test.d.ts +2 -0
- package/dist/test/cli/generate-testcase-paths.test.d.ts.map +1 -0
- package/dist/test/cli/generate-testcase-paths.test.js +165 -0
- package/dist/test/cli/generate-testcase-requirements.test.d.ts +2 -0
- package/dist/test/cli/generate-testcase-requirements.test.d.ts.map +1 -0
- package/dist/test/cli/generate-testcase-requirements.test.js +229 -0
- package/dist/test/cli/generate-testcase.test.d.ts +2 -0
- package/dist/test/cli/generate-testcase.test.d.ts.map +1 -0
- package/dist/test/cli/generate-testcase.test.js +130 -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 +113 -0
- package/dist/test/cli/probe.test.d.ts +2 -0
- package/dist/test/cli/probe.test.d.ts.map +1 -0
- package/dist/test/cli/probe.test.js +31 -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 +149 -0
- package/dist/test/cli/spec-boundary.test.d.ts +2 -0
- package/dist/test/cli/spec-boundary.test.d.ts.map +1 -0
- package/dist/test/cli/spec-boundary.test.js +198 -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 +196 -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/crud-contract.test.js +12 -32
- package/dist/test/inference/module-hints-inference.test.d.ts +2 -0
- package/dist/test/inference/module-hints-inference.test.d.ts.map +1 -0
- package/dist/test/inference/module-hints-inference.test.js +25 -0
- package/dist/test/inference/source-analysis.test.d.ts +2 -0
- package/dist/test/inference/source-analysis.test.d.ts.map +1 -0
- package/dist/test/inference/source-analysis.test.js +23 -0
- package/dist/test/material-inventory.test.js +13 -0
- package/dist/test/observable-pipeline.test.js +12 -3
- package/dist/test/stage-context.test.js +145 -13
- 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/credentials.test.d.ts +2 -0
- package/dist/test/validation/credentials.test.d.ts.map +1 -0
- package/dist/test/validation/credentials.test.js +72 -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.test.d.ts +2 -0
- package/dist/test/validation/testcase.test.d.ts.map +1 -0
- package/dist/test/validation/testcase.test.js +129 -0
- package/docs/README.md +6 -6
- package/docs/mvp-usage-guide.md +3 -3
- package/package.json +9 -4
- package/codex-skill/ep-stage/create-project/SKILL.md +0 -59
- package/codex-skill/ep-stage/glue-test/SKILL.md +0 -258
- package/codex-skill/ep-stage/glue-test/references/crud-pipeline.md +0 -139
- package/codex-skill/ep-stage/glue-testcase/SKILL.md +0 -31
- package/codex-skill/ep-stage/glue-testcase/references/testcase-schema.md +0 -67
- /package/codex-skill/ep-stage/{glue-testcase → glue-generate-testcase}/examples/observable-testcase.json +0 -0
- /package/codex-skill/ep-stage/{glue-test → glue-run-test}/references/gap-review-protocol.md +0 -0
- /package/codex-skill/ep-stage/{glue-test → glue-run-test}/references/harness-principles.md +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
testcase.md 渲染模板(AI 用)。
|
|
3
|
+
|
|
4
|
+
用法(由 glue-generate-testcase SKILL Step 6 驱动):
|
|
5
|
+
- 数据源:`<需求名>/testcase.json5`(cases 结构 = 事实源) + `<需求名>/.ep-stage/contract.json`(业务槽位)+ `<需求名>/credentials.json5`(环境信息)
|
|
6
|
+
- AI 按本模板渲染 testcase.md,重跑覆盖
|
|
7
|
+
- 忠实性约束:cases 数量 / caseId / title / reviewStatus / requiredRole 必须**逐字**取自 testcase.json5;步骤/断言/前置条件中的具体槽位标签(菜单/列表页/按钮文字/查询字段)必须**逐字**取自 contract.json,AI 只组织自然语言句式,不得新增/捏造 contract 没有的槽位
|
|
8
|
+
|
|
9
|
+
下面是格式骨架。`{{...}}` 是占位,AI 渲染时全部替换;不要把这段说明注释写进产出的 testcase.md。
|
|
10
|
+
-->
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
schemaVersion: {{testcase.schemaVersion}} # 取自 testcase.json5 顶层
|
|
14
|
+
scenario: {{testcase.scenario}} # 取自 testcase.json5 顶层
|
|
15
|
+
moduleId: {{testcase.moduleId}} # 取自 testcase.json5 顶层
|
|
16
|
+
contractVersion: {{contract.contractVersion}} # 取自 contract.json
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# {{testcase.moduleName}}胶水测试用例
|
|
20
|
+
|
|
21
|
+
## 环境信息
|
|
22
|
+
|
|
23
|
+
- 登录地址:{{credentials[0].url}}
|
|
24
|
+
- 登录账号:{{credentials[0].username}}
|
|
25
|
+
- 登录密码:(已配置在 credentials.json5,不在此展示)
|
|
26
|
+
- 菜单路由:{{testcase.menuPath || "(未填写,待 testcase.json5 审阅补齐)"}}
|
|
27
|
+
- 列表页:{{contract.pages.list.title}}
|
|
28
|
+
- 查询字段:{{contract.flows.search.searchField.label}}
|
|
29
|
+
- 操作角色:{{testcase.requiredRoles[0].role}}
|
|
30
|
+
- 未解析槽位:{{contract.unresolvedSlots.length > 0 ? contract.unresolvedSlots.map(s => s.slotId).join("、") : "无"}}
|
|
31
|
+
|
|
32
|
+
## CRUD 冒烟用例
|
|
33
|
+
|
|
34
|
+
| 用例 ID | 标题 | 前置条件 | 步骤 | 断言 |
|
|
35
|
+
|---|---|---|---|---|
|
|
36
|
+
| {{case.caseId}} | {{case.title}} | {{case.preconditions}} | {{case.steps_joined}} | {{case.assertions_joined}} |
|
|
37
|
+
|
|
38
|
+
<!--
|
|
39
|
+
说明(仅供 AI 渲染时参考,不写入产出 md):
|
|
40
|
+
|
|
41
|
+
「步骤」「断言」「前置条件」列的填充规则——
|
|
42
|
+
|
|
43
|
+
cases[].steps / cases[].assertions 当前若为占位(如「执行 create 主流程(详见 .spec.ts)」、「验证 create 完成后业务断言」),
|
|
44
|
+
AI 应该**基于 contract.json 槽位组装具体的自然语言表述**:
|
|
45
|
+
|
|
46
|
+
- create 步骤(caseId 含 .create):`进入菜单 {testcase.menuPath || "<未填写菜单>"};确认列表页 {contract.pages.list.title};点击 {contract.flows.create.entryButton.label};填写 {contract.dataKey.label} 等字段;点击 {contract.flows.create.saveButton.label}`
|
|
47
|
+
- create 断言:`新增后可在 {contract.pages.list.title} 按 {contract.flows.search.searchField.label} 查询到记录`
|
|
48
|
+
- create 前置条件:`已登录并进入 {testcase.menuPath || "<未填写菜单>"}`
|
|
49
|
+
|
|
50
|
+
- read 步骤(caseId 含 .read):`进入菜单 {testcase.menuPath || "<未填写菜单>"};按查询字段:{contract.flows.search.searchField.label} 输入测试值;点击 {contract.flows.search.submitControl.label}`
|
|
51
|
+
- read 断言:`查询结果包含 {contract.flows.search.searchField.label} 对应的测试记录`
|
|
52
|
+
- read 前置条件:`已存在可查询测试数据`
|
|
53
|
+
|
|
54
|
+
- update 步骤(caseId 含 .update):`在 {contract.pages.list.title} 中定位测试记录;点击 {contract.flows.update.entryAction.label};修改 {contract.dataKey.label} 字段;点击 {contract.flows.update.saveButton.label}`
|
|
55
|
+
- update 断言:`修改后可按 {contract.flows.search.searchField.label} 查询到更新后的记录`
|
|
56
|
+
- update 前置条件:`已定位到待修改记录`
|
|
57
|
+
|
|
58
|
+
- delete 步骤(caseId 含 .delete):`在 {contract.pages.list.title} 中定位测试记录;点击 {contract.flows.delete.entryButton.label};按契约策略 {contract.flows.delete.expectedOutcome} 确认删除结果`
|
|
59
|
+
- delete 断言:`删除结果符合 contract.flows.delete.expectedOutcome={contract.flows.delete.expectedOutcome} 的预期`
|
|
60
|
+
- delete 前置条件:`已定位到待删除记录`
|
|
61
|
+
|
|
62
|
+
- 非 CRUD 用例(uncoveredCandidates 转入的 candidate / planned 用例):AI 按 testcase.json5 cases[].businessIntent + assertionExpectation 直译;缺时填「待人工补充」,不发挥
|
|
63
|
+
|
|
64
|
+
testcase.json5 的 cases[].evidence 不展示在 md 表格(人审不需要技术性溯源);
|
|
65
|
+
若 testcase.json5 cases 数量为 0,testcase.md 表格只保留表头,下方追加一段说明「本需求未生成任何用例,请审阅 .ep-stage/inference-trace.json 排查」。
|
|
66
|
+
-->
|
|
67
|
+
|
|
68
|
+
## 审阅引导
|
|
69
|
+
|
|
70
|
+
1. 表格中**步骤、断言列若仍含占位**(如「执行 X 主流程」),表示 contract.json 对应槽位证据不足;请检查 `.ep-stage/inference-trace.json` 的 `moduleHintsInference.unresolved` 与 `contract.json.unresolvedSlots`,补齐后重跑。
|
|
71
|
+
2. 如对推断步骤/断言不认可,**修改 `testcase.json5` 的 cases[] 字段**(非本 md 文件),重跑 `/glue-generate-testcase` 重新渲染本 md。
|
|
72
|
+
3. 凭据信息在 `credentials.json5`;登录密码不显示在本审阅视图。
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: glue-run-test
|
|
3
|
+
description: 使用时机:当用户对某需求要「跑测试 / 验证 / 端到端执行 / 跑胶水测试 / 看报告」或显式触发 `/glue-run-test <需求目录>` 时使用。本 skill 包装 `@epoint-testtech/ep-stage-skill` CLI 的 `spec` + `run` 双子命令,做 spec 组装与 Playwright 执行 + AI 钻探追加。
|
|
4
|
+
version: 2026-06-25+6e9e357
|
|
5
|
+
argument-hint: "<需求目录绝对/相对路径>"
|
|
6
|
+
compatibility: 需要由 stage-create 生成的 glue 模式 Playwright 项目 + 已审过的 `<需求名>/testcase.json5` (v2) + 填好的 `<需求名>/credentials.json5`。
|
|
7
|
+
metadata:
|
|
8
|
+
source-package: "@epoint-testtech/ep-stage-skill"
|
|
9
|
+
contract: "crud-business-module/v1"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ep-stage: glue-run-test
|
|
13
|
+
|
|
14
|
+
本 skill 引导 Agent **包装** `@epoint-testtech/ep-stage-skill` CLI 的 `spec` + `run` 双子命令,不重新实现 spec 组装、Playwright 执行或 AI 推理。CLI 内部完成:v1 初始版 spec 生成(spec 子命令)+ 凭据数组加载 + 多角色登录切换 + Playwright 执行 + uncoveredCandidates 中 `planned` 项 AI 钻探追加 + v2 追加版 spec 写回 + 报告生成 + 项目索引回写(run 子命令)。skill 负责:路径解析、反查项目脚手架、缺前置数据引导、任务清单打印、报告路径回显。用户视角全中文,不暴露 CLI 名称。
|
|
15
|
+
|
|
16
|
+
> **钻探已前置**:上游钻探 + contract 提取 + coverage 分析 + uncoveredCandidates 推断由 `/glue-generate-testcase` 阶段完成(REQ-CHAIN-01)。本 skill 只消费 testcase v2 + contract.json,不再自行钻探(REQ-CHAIN-02)。
|
|
17
|
+
>
|
|
18
|
+
> **spec 双生命周期**(REQ-SPEC-01):spec 子命令只产 **v1 初始版**(只含 coveredActions 胶水部分,顶部 `// === 胶水模式生成 ===`),不执行;run 子命令跑 v1 + AI 钻探 uncoveredCandidates → 追加 **v2 追加版**(底部 `// === AI 钻探追加 ===`)。重跑 run 检测已有追加段则**覆盖**,不保留旧追加。
|
|
19
|
+
|
|
20
|
+
## 1. 自然语言映射
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
/glue-run-test /abs/path/.../014-测试需求10-20260622 # 绝对路径
|
|
24
|
+
/glue-run-test ../knowledge-project/.../014-测试需求10-20260622 # 相对路径
|
|
25
|
+
/glue-run-test # 无参数 → 问需求目录
|
|
26
|
+
/glue-run-test /abs/path/.../014-测试需求10-20260622 --debug # 调试模式 → 跑本地 ep-stage-skill dist
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
> **`--debug` flag**:正常调用走远程 `npx @epoint-testtech/ep-stage-skill@latest`。开发期想测试未发布的本地代码时传 `--debug`,skill 在 Step 4(spec)+ Step 5(run)都改用 `node <repo>/packages/ep-stage-skill/dist/src/cli/index.js`。前提:cwd 在 ep-stage monorepo 内(向上能找到 `pnpm-workspace.yaml`);找不到时直接 exit 1 报错,不静默 fallback 到 npx。
|
|
30
|
+
|
|
31
|
+
skill 把入参 `path.resolve` 绝对化,`path.basename` 取「需求名」。projectDir 不接受参数,全部从 `~/.ep-stage/projects.index.json5` 反查最新 `mode: 'glue'` 条目(多条 AskUserQuestion 让用户挑);索引缺失走引导场景 A。
|
|
32
|
+
|
|
33
|
+
## 2. Agent 执行剧本(8 步任务清单协议)
|
|
34
|
+
|
|
35
|
+
**任务清单协议(REQ-UX-01)**:load 后**立即**打印初始任务清单(Step 0);每完成一步输出单行 `✅ 第 N 步 — <中文名> 已完成,进入第 N+1 步 — <下一中文名>...`;Step 8 输出全勾选终态清单 + 摘要(spec 路径 + 报告路径 + lastRunStatus)。
|
|
36
|
+
|
|
37
|
+
### Step 0 — 打印任务清单
|
|
38
|
+
|
|
39
|
+
```markdown
|
|
40
|
+
📋 **执行胶水测试任务清单**
|
|
41
|
+
|
|
42
|
+
- [ ] 第 1 步 — 解析需求路径并反查项目脚手架
|
|
43
|
+
- [ ] 第 2 步 — 校验 testcase.json5 输入
|
|
44
|
+
- [ ] 第 3 步 — 校验 credentials.json5 凭据
|
|
45
|
+
- [ ] 第 4 步 — 调用 spec 组装 CLI(只生成 v1 不执行)
|
|
46
|
+
- [ ] 第 5 步 — 调用 run 执行与 AI 推理 CLI(产 v2)
|
|
47
|
+
- [ ] 第 6 步 — 收集报告产物
|
|
48
|
+
- [ ] 第 7 步 — 写入运行状态到项目索引
|
|
49
|
+
- [ ] 第 8 步 — 输出执行摘要
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Step 1 — 解析需求路径并反查项目脚手架(缺 → 引导 A)
|
|
53
|
+
|
|
54
|
+
- 从用户消息 / args 抽取需求目录路径,绝对化 + basename。入参缺失 → free text 询问"请给我需求目录的绝对或相对路径"。
|
|
55
|
+
- 读 `~/.ep-stage/projects.index.json5`:文件不存在 / 无 glue 条目 → **引导场景 A**:中文提示"未发现胶水测试脚手架。请先跑 `/glue-create-project <项目名>` 创建脚手架后再回来",退出不调 CLI。多条匹配 → AskUserQuestion 挑 projectDir。
|
|
56
|
+
- 中文复述识别结果:"已识别:需求目录 = `<abs>`,需求名 = `<basename>`,项目目录 = `<projectDir>`"。
|
|
57
|
+
|
|
58
|
+
### Step 2 — 校验 testcase.json5 输入(缺 → 引导 B)
|
|
59
|
+
|
|
60
|
+
校验 `<projectDir>/src/tests/<需求名>/testcase.json5` 是否存在 + `schemaVersion: "v2"`。
|
|
61
|
+
|
|
62
|
+
缺失 / schema 非 v2 → **引导场景 B**,AskUserQuestion 三选一:
|
|
63
|
+
|
|
64
|
+
1. **跑 `/glue-generate-testcase <需求目录>`**:先生成用例,回头再跑 run。
|
|
65
|
+
2. **跳过用例直接组装**(不推荐):警告"无 testcase 时 spec 只能生成空胶水骨架,钻探追加无法启动",确认后继续。
|
|
66
|
+
3. **取消**:直接退出,不调 CLI。
|
|
67
|
+
|
|
68
|
+
### Step 3 — 校验 credentials.json5 凭据(缺 → 引导 C)
|
|
69
|
+
|
|
70
|
+
校验 `<projectDir>/src/tests/<需求名>/credentials.json5` 存在 + 数组至少 1 条 + 每条 `url / username / password` 非空(参 §4.3)。
|
|
71
|
+
|
|
72
|
+
缺失 / 字段空 → **引导场景 C**,先读 `~/.ep-stage/projects.index.json5` 的 `systems[].credentialsCache`,按 testcase.json5 `requiredSystems[].url` 比对算 `cachedSystemsHit`。AskUserQuestion 三选一:
|
|
73
|
+
|
|
74
|
+
1. **从全局缓存复用**(仅 `cachedSystemsHit` 非空时显示):列出命中的 url + 已缓存角色,自动拼出 credentials.json5(缺失角色用占位符保留)。
|
|
75
|
+
2. **现在补**:skill 顺序问每条角色的 URL / username / password / systemName(free text),直接写 `<需求名>/credentials.json5`,**回写**全局 `systems[].credentialsCache`(按 url 归并,跨需求复用)。
|
|
76
|
+
3. **取消**:直接退出,不调 CLI。
|
|
77
|
+
|
|
78
|
+
### Step 4 — 调用 spec 组装 CLI(只生成 v1 不执行)
|
|
79
|
+
|
|
80
|
+
固定流程(不 which、不扫 monorepo、不 cd、**不喂 stdin**)。
|
|
81
|
+
|
|
82
|
+
**正常模式**(用户 args 无 `--debug`):
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx -y @epoint-testtech/ep-stage-skill@latest spec \
|
|
86
|
+
--testcase <projectDir>/src/tests/<需求名>/testcase.json5 \
|
|
87
|
+
--out <projectDir>/src/tests/<需求名>/crud.spec.ts
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**调试模式**(用户 args 含 `--debug`,要求 cwd 在 ep-stage monorepo 内;详见 §1 `--debug` flag 说明):
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
D="$PWD"; REPO=""
|
|
94
|
+
while [ "$D" != "/" ]; do
|
|
95
|
+
if [ -f "$D/pnpm-workspace.yaml" ] && [ -f "$D/packages/ep-stage-skill/dist/src/cli/index.js" ]; then
|
|
96
|
+
REPO="$D"; break
|
|
97
|
+
fi
|
|
98
|
+
D="$(dirname "$D")"
|
|
99
|
+
done
|
|
100
|
+
[ -z "$REPO" ] && { echo "❌ --debug 但找不到本地 monorepo"; exit 1; }
|
|
101
|
+
echo "▸ 调试模式:本地 $REPO/packages/ep-stage-skill/dist/src/cli/index.js"
|
|
102
|
+
node "$REPO/packages/ep-stage-skill/dist/src/cli/index.js" spec \
|
|
103
|
+
--testcase <projectDir>/src/tests/<需求名>/testcase.json5 \
|
|
104
|
+
--out <projectDir>/src/tests/<需求名>/crud.spec.ts
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
CLI 内部:读 testcase v2 + `.ep-stage/contract.json` → 基于 `coveredActions` + `cases[]` 生成 **v1 初始版** `crud.spec.ts`(顶部注释 `// === 胶水模式生成 ===`,引用 `../../skeletons` 相对路径,只覆盖 coveredActions 胶水部分,不执行 Playwright)。CLI 非 0 退出 → 复述 stderr,不进 Step 5。
|
|
108
|
+
|
|
109
|
+
### Step 5 — 调用 run 执行与 AI 推理 CLI(产 v2)
|
|
110
|
+
|
|
111
|
+
**正常模式**(用户 args 无 `--debug`):
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npx -y @epoint-testtech/ep-stage-skill@latest run \
|
|
115
|
+
--spec <projectDir>/src/tests/<需求名>/crud.spec.ts \
|
|
116
|
+
--testcase <projectDir>/src/tests/<需求名>/testcase.json5 \
|
|
117
|
+
--headless false
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**调试模式**(用户 args 含 `--debug`,要求 cwd 在 ep-stage monorepo 内;详见 §1 `--debug` flag 说明):
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
D="$PWD"; REPO=""
|
|
124
|
+
while [ "$D" != "/" ]; do
|
|
125
|
+
if [ -f "$D/pnpm-workspace.yaml" ] && [ -f "$D/packages/ep-stage-skill/dist/src/cli/index.js" ]; then
|
|
126
|
+
REPO="$D"; break
|
|
127
|
+
fi
|
|
128
|
+
D="$(dirname "$D")"
|
|
129
|
+
done
|
|
130
|
+
[ -z "$REPO" ] && { echo "❌ --debug 但找不到本地 monorepo"; exit 1; }
|
|
131
|
+
echo "▸ 调试模式:本地 $REPO/packages/ep-stage-skill/dist/src/cli/index.js"
|
|
132
|
+
node "$REPO/packages/ep-stage-skill/dist/src/cli/index.js" run \
|
|
133
|
+
--spec <projectDir>/src/tests/<需求名>/crud.spec.ts \
|
|
134
|
+
--testcase <projectDir>/src/tests/<需求名>/testcase.json5 \
|
|
135
|
+
--headless false
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
CLI 内部全链路(详见 references/crud-pipeline.md「执行 + AI 推理追加」):
|
|
139
|
+
|
|
140
|
+
1. 读 `<需求名>/credentials.json5`(数组形态多角色)+ testcase.json5(v2)。
|
|
141
|
+
2. **多角色登录**:通过 `@epoint-testtech/stage-core.loadCredentials(path, role?)` 按 `cases[].requiredRole` / `uncoveredCandidates[].requiredRole` 检索对应角色,启动 `LoginPage`。
|
|
142
|
+
3. **跑 v1 胶水部分**:执行 spec 顶部 `// === 胶水模式生成 ===` 区段所有 `test(...)` block。
|
|
143
|
+
4. **AI 钻探追加**:对 `uncoveredCandidates[].status === 'planned'` 项,按 `requiredRole` 切登录 → 页面浏览补 spec(`businessIntent` / `assertionExpectation` / `label` 驱动 test 标题与断言)→ **追加到 spec 底部**产 v2(注释 `// === AI 钻探追加 ===`)。
|
|
144
|
+
5. **重跑覆盖**:检测到分段标记则**覆盖**追加段(v2 → v2'),不保留旧追加。
|
|
145
|
+
6. 写报告到 `e2e/glue-code-mvp/glue-report/<run-timestamp>/`。
|
|
146
|
+
|
|
147
|
+
CLI 非 0 退出 → 复述 stderr,按 §4 报告。
|
|
148
|
+
|
|
149
|
+
> **凭据通路**:skill 自身**不调** `loadCredentials`;该工具由 CLI run 子命令生成的 Playwright spec 内部 import 自 `@epoint-testtech/stage-core`。skill 只校验 credentials.json5 + 引导补齐,不解析凭据明文。
|
|
150
|
+
|
|
151
|
+
### Step 6 — 收集报告产物
|
|
152
|
+
|
|
153
|
+
CLI 退出后用 `ls` / `Read` 校验 §3 验证清单。报告目录缺失视为整体失败,按 §4 报告。
|
|
154
|
+
|
|
155
|
+
### Step 7 — 写入运行状态到项目索引
|
|
156
|
+
|
|
157
|
+
CLI run 子命令已在 `~/.ep-stage/projects.index.json5` 回写:
|
|
158
|
+
|
|
159
|
+
- `projects[].requirements[<i>].lastRunAt` / `lastRunStatus`(`passed` / `failed` / `partial`)
|
|
160
|
+
- `systems[].credentialsCache`(按 url 归并本次 credentials.json5 用到的角色凭据)
|
|
161
|
+
|
|
162
|
+
skill 校验:本需求条目存在;`lastRunAt` 是本次时间戳;`lastRunStatus` 非 `never`。缺时由 skill 补写。
|
|
163
|
+
|
|
164
|
+
### Step 8 — 输出执行摘要
|
|
165
|
+
|
|
166
|
+
打印全勾选终态清单 + §5 完成提示(含 spec 路径 + 报告路径 + `lastRunStatus` + Playwright HTML 报告打开指令 + 重跑提示)。
|
|
167
|
+
|
|
168
|
+
## 3. 验证清单
|
|
169
|
+
|
|
170
|
+
| 路径 | 说明 |
|
|
171
|
+
|-----------------------------------------------------------------|-----------------------------------------------------|
|
|
172
|
+
| `<projectDir>/src/tests/<需求名>/crud.spec.ts` | v1 或 v2(含 `// === 胶水模式生成 ===` + 可选 `// === AI 钻探追加 ===` 分段)|
|
|
173
|
+
| `<projectDir>/src/tests/<需求名>/testcase.json5` | 必须存在 + `schemaVersion: "v2"` |
|
|
174
|
+
| `<projectDir>/src/tests/<需求名>/credentials.json5` | 必须存在 + 数组 ≥ 1 条 + 必填字段非空 |
|
|
175
|
+
| `<projectDir>/src/tests/<需求名>/.ep-stage/contract.json` | 由 testcase 阶段产,run 阶段只读不写 |
|
|
176
|
+
| `e2e/glue-code-mvp/glue-report/<run-timestamp>/` | run 子命令产;含执行摘要 + Playwright trace 引用 |
|
|
177
|
+
| `~/.ep-stage/projects.index.json5` | 已更新 `lastRunAt / lastRunStatus + systems[].credentialsCache` |
|
|
178
|
+
|
|
179
|
+
## 4. 错误处理
|
|
180
|
+
|
|
181
|
+
失败归类与详细排障路径见 `references/crud-pipeline.md`「失败归类」节。本 skill 高频呈现:
|
|
182
|
+
|
|
183
|
+
| 触发时机 | Agent 中文呈现 + 建议 |
|
|
184
|
+
|---------------------------|-----------------------------------------------------------|
|
|
185
|
+
| 引导 A(缺脚手架) | 中文提示+退出,引导先跑 `/glue-create-project <项目名>` |
|
|
186
|
+
| 引导 B(缺 testcase) | AskUserQuestion 三选一:跑 testcase / 强组装 / 取消 |
|
|
187
|
+
| 引导 C(缺凭据) | AskUserQuestion 三选一:缓存复用 / 现在补 / 取消 |
|
|
188
|
+
| spec 子命令 exit ≠ 0 | 复述 stderr 中文转述,不进 Step 5 |
|
|
189
|
+
| run 子命令 exit ≠ 0 | 复述 stderr;归类参 references/crud-pipeline.md 失败归类 |
|
|
190
|
+
| Playwright 业务失败 | 报告 `lastRunStatus: failed`,提示打开 HTML 报告人审 |
|
|
191
|
+
| `coveredActions` 为空 | 阻断 spec 子命令;提示用户回 `/glue-generate-testcase` 补齐 |
|
|
192
|
+
| 用户中途取消 | AskUserQuestion 阶段说「算了/取消」立即终止;CLI 启动后等其退出再复述 |
|
|
193
|
+
|
|
194
|
+
## 5. 完成提示
|
|
195
|
+
|
|
196
|
+
Step 8 全部通过后,按以下中文格式呈现(不要用 success/done/passed):
|
|
197
|
+
|
|
198
|
+
> ✅ 胶水测试执行完成
|
|
199
|
+
>
|
|
200
|
+
> 需求:`<需求名>`/项目:`<projectDir>`
|
|
201
|
+
> Spec:`<projectDir>/src/tests/<需求名>/crud.spec.ts`(v2 — 胶水 + AI 钻探追加)
|
|
202
|
+
> 报告目录:`e2e/glue-code-mvp/glue-report/<run-timestamp>/`
|
|
203
|
+
> Playwright HTML:`npx playwright show-report --config <projectDir>/playwright.config.ts`
|
|
204
|
+
> 运行状态:`lastRunStatus = <passed/failed/partial>`(已回写 `~/.ep-stage/projects.index.json5`)
|
|
205
|
+
>
|
|
206
|
+
> 下一步:① 失败用例打开 HTML 报告看 trace;② 调整 testcase.json5 / ModuleHints 后重跑 `/glue-run-test <需求目录>`;③ 研发回归独立执行 `cd <projectDir> && npx playwright test src/tests/<需求名>/crud.spec.ts`(**跑文件当前内容**,v1/v2 行为见 §6)
|
|
207
|
+
|
|
208
|
+
**失败时不要发完成提示**:§3 验证项任一缺失或 CLI 非 0 退出 → 按 §4 报告,不允许混合"执行完成"字样。
|
|
209
|
+
|
|
210
|
+
## 6. spec 双向执行路径与生命周期(REQ-SPEC-01)
|
|
211
|
+
|
|
212
|
+
生成的 `crud.spec.ts` 是 standard Playwright spec,可被 2 条路径执行;文件有 v1 / v2 两个生命周期:
|
|
213
|
+
|
|
214
|
+
| 状态 | 文件内容 | 由谁产 | 何时存在 |
|
|
215
|
+
|---|---|---|---|
|
|
216
|
+
| **v1 初始版** | 仅 coveredActions 胶水部分(顶部 `// === 胶水模式生成 ===`) | `spec` 子命令 | 跑过 spec 但未跑过 run |
|
|
217
|
+
| **v2 追加版** | v1 + AI 钻探追加(底部 `// === AI 钻探追加 ===`) | `run` 子命令 | 跑过 run 后;重跑覆盖追加段 |
|
|
218
|
+
|
|
219
|
+
- **路径 ①(本 skill 标准链路)**:spec → run 子命令 → AI 推理追加产 v2。
|
|
220
|
+
- **路径 ②(研发回归独立执行)**:`cd <projectDir> && npx playwright test src/tests/<需求名>/crud.spec.ts` — 纯文件 IO 跑当前内容,不区分 v1/v2,不修改文件。
|
|
221
|
+
|
|
222
|
+
**测试资产自包含原则**(v1/v2 都成立):spec 用相对路径 `import { CrudPage } from '../../skeletons/...'`;凭据由同需求目录 `credentials.json5` 加载(通过 stage-core `loadCredentials`),不依赖任何 ep-stage runtime 状态;研发人员把整个 `<需求名>/` 目录拷到任意环境都能跑(含填好的 credentials.json5)。
|
|
223
|
+
|
|
224
|
+
## 7. 真实系统 E2E 运行约束
|
|
225
|
+
|
|
226
|
+
- **禁止** `--headless true` 跑真实业务系统 E2E。本 skill 调用 run 子命令固定 `--headless false`。
|
|
227
|
+
- 真实系统验证**必须有人在场观察**页面行为、弹窗、登录状态和业务副作用;无人工观察的无头回放不算当前阶段验收证据。
|
|
228
|
+
- `--headless true` 仅允许用于本地结构调试,不作为真实系统验收方式。
|
|
229
|
+
- 观察结论须与 `glue-report/<run-timestamp>/` + Playwright HTML 报告交叉核对后才作为验收证据。
|
|
230
|
+
|
|
231
|
+
## 8. 内嵌数据契约范例
|
|
232
|
+
|
|
233
|
+
`<需求名>/credentials.json5`(数组形态,REQ-ENV-02;详见 spec §4.3):
|
|
234
|
+
|
|
235
|
+
```json5
|
|
236
|
+
// 单系统单角色(zwplace 最常见)
|
|
237
|
+
[ { url: "http://192.168.x.x:8100/.../login", username: "admin", password: "..." } ]
|
|
238
|
+
|
|
239
|
+
// 多系统多角色(StageZhgd 类业务)
|
|
240
|
+
[
|
|
241
|
+
{ url: "http://sys1/login", username: "atjsdw...", password: "Epoint@2021", role: "建设单位", systemName: "建设单位办公系统" },
|
|
242
|
+
{ url: "http://sys1/login", username: "atsgdw...", password: "Epoint@2021", role: "施工单位", systemName: "建设单位办公系统" },
|
|
243
|
+
{ url: "http://sys2/login", username: "...", password: "...", role: "审批员", systemName: "审批端" }
|
|
244
|
+
]
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
字段约束:必填 `url / username / password`;可选 `role / systemName`;数组至少 1 条;同 `(url, role)` 组合唯一;文件 gitignore。
|
|
248
|
+
|
|
249
|
+
`~/.ep-stage/projects.index.json5`(run 阶段回写 `requirements[].lastRun*` + `systems[].credentialsCache`):
|
|
250
|
+
|
|
251
|
+
```json5
|
|
252
|
+
{
|
|
253
|
+
projects: [{
|
|
254
|
+
projectName: "...", projectDir: "...", mode: "glue", stageCreateVersion: "0.0.4-alpha.0",
|
|
255
|
+
requirements: [{
|
|
256
|
+
name: "014-测试需求10-20260622",
|
|
257
|
+
requirementDir: "/abs/.../014-测试需求10-20260622",
|
|
258
|
+
testsDir: "<projectDir>/src/tests/014-测试需求10-20260622",
|
|
259
|
+
lastRunAt: "2026-06-25T10:00:00Z", // run 回写
|
|
260
|
+
lastRunStatus: "passed" // passed | failed | partial | never;run 回写
|
|
261
|
+
}]
|
|
262
|
+
}],
|
|
263
|
+
systems: [{
|
|
264
|
+
url: "http://192.168.x.x:8100/.../login",
|
|
265
|
+
knownRoles: ["建设单位", "施工单位"],
|
|
266
|
+
credentialsCache: { "建设单位": { username: "...", password: "...", lastUsed: "..." } } // run 回写
|
|
267
|
+
}]
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## 9. 引导场景
|
|
272
|
+
|
|
273
|
+
- **A(缺脚手架)**:Step 1 反查 projects.index.json5 无 glue 条目 → 提示先跑 `/glue-create-project` 退出。
|
|
274
|
+
- **B(缺 testcase)**:Step 2 缺失 / schema 非 v2 → AskUserQuestion 三选一(跑 testcase / 强组装 / 取消)。
|
|
275
|
+
- **C(缺凭据)**:Step 3 字段空 → AskUserQuestion 三选一(缓存复用 / 现在补 / 取消,含 `cachedSystemsHit` 命中提示)。
|
|
276
|
+
- **D(缺 code_list.md)**:归 `/glue-generate-testcase` 处理(钻探前置)。
|
|
277
|
+
|
|
278
|
+
## 10. 按需加载
|
|
279
|
+
|
|
280
|
+
- CRUD 组装 pipeline + 验证命令 + **失败归类**:`references/crud-pipeline.md`
|
|
281
|
+
- harness 治理原则:`references/harness-principles.md`;gap 评审协议:`references/gap-review-protocol.md`
|
|
282
|
+
|
|
283
|
+
## 11. 关键约束
|
|
284
|
+
|
|
285
|
+
- 钻探前置:本 skill 不再实现 contract 提取 / coverage 分析 / uncoveredCandidates 推断;这些在 `/glue-generate-testcase` 完成。
|
|
286
|
+
- spec 子命令**只生成不执行**(REQ-SPEC-01);Playwright 执行只走 run 子命令 ① 或研发独立 ②。
|
|
287
|
+
- run 子命令仅对 `uncoveredCandidates[].status === 'planned'` 项 AI 推理;`candidate` / `unresolved` 项不得盲目追加 spec(gap 评审协议)。
|
|
288
|
+
- AI 推理阶段不修改 `cases[].businessIntent` / `assertionExpectation`,也不改 `src/skeletons/*.ts`。
|
|
289
|
+
- 用户视角始终全中文;业务断言、selector、iframe 关键字、按钮文案均由骨架 + `ModuleHints` 决策,skill / spec 不发明。
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# CRUD Pipeline 参考(CRUD Pipeline Reference)
|
|
2
|
+
|
|
3
|
+
> 本文为 `glue-run-test` skill 的参考文档。所有 CLI 调用统一使用 `npx -y @epoint-testtech/ep-stage-skill@latest <subcommand>` 形态(REQ-CLI-01 / REQ-CLI-02)。
|
|
4
|
+
|
|
5
|
+
## 子命令一览
|
|
6
|
+
|
|
7
|
+
| 子命令 | 入口 | 说明 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `testcase` | `glue-generate-testcase` skill 包装 | 上游钻探 + contract 提取 + coverage 分析 + testcase v2 生成 + credentials.template 生成 + code_list 软链 |
|
|
10
|
+
| `spec` | `glue-run-test` skill 包装(**只生成不执行**) | 读 testcase + contract → 生成 v1 初始版 `crud.spec.ts`(只覆盖 coveredActions 胶水部分) |
|
|
11
|
+
| `run` | `glue-run-test` skill 包装 | 读 spec + testcase + credentials → 启动 Playwright + AI 推理 uncoveredCandidates → 追加产 v2 + 报告 |
|
|
12
|
+
|
|
13
|
+
> **contract 提取已内嵌**到 `testcase` 子命令(REQ-CLI-02),不暴露为独立 CLI 命令。开发期可用 `pnpm generate:crud-contract`(路由到 `src/cli/dev/extract-contract.ts`),仅作调试入口,**不进 SKILL.md**。
|
|
14
|
+
|
|
15
|
+
## ModuleHints 指引(ModuleHints Guidance)
|
|
16
|
+
|
|
17
|
+
`ModuleHints` 是"已提取事实"与"测试人员策略"之间的边界。
|
|
18
|
+
|
|
19
|
+
以下内容应放入 hints,而不是由 Agent 自行推断:
|
|
20
|
+
|
|
21
|
+
- `dataKey.selectedField` 和 `dataKey.generationStrategy`
|
|
22
|
+
- `searchConditions[]`,尤其是 listbox 的 `value`
|
|
23
|
+
- `buttonAliases`
|
|
24
|
+
- `autofill.overrideFields`
|
|
25
|
+
- `deletePolicy`
|
|
26
|
+
- 断言策略覆盖
|
|
27
|
+
|
|
28
|
+
对于多条件 CRUD 冒烟测试,推荐:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"dataKey": {
|
|
33
|
+
"selectedField": "placename",
|
|
34
|
+
"generationStrategy": "timestamp_prefix"
|
|
35
|
+
},
|
|
36
|
+
"searchConditions": [
|
|
37
|
+
{
|
|
38
|
+
"field": "placename",
|
|
39
|
+
"component": "input",
|
|
40
|
+
"seedValue": "自动化测试名称",
|
|
41
|
+
"strategy": "timestamp_prefix",
|
|
42
|
+
"updatePrefix": "修改"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"field": "placecategory",
|
|
46
|
+
"component": "listbox",
|
|
47
|
+
"value": "条线大厅",
|
|
48
|
+
"createSelections": [
|
|
49
|
+
{ "field": "placecategory", "value": "条线大厅" }
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`searchConditions` 驱动生成的 `CrudFlowSlots`。`dataKey` 仍作为兼容性和策略锚点保留,但不应与独立的 `searchField` 概念重复。
|
|
57
|
+
|
|
58
|
+
## 检查 testcase 闸门(Inspect Testcase Gate)
|
|
59
|
+
|
|
60
|
+
testcase 子命令产出后,由 skill 校验 `testcase.json5` 顶层结构(schemaVersion=v2 + scenario + menuPath + requiredSystems + requiredRoles + coveredActions + uncoveredCandidates + cases + reasoningSummary)。`coveredActions` 为空 → 阻断 spec 子命令;`uncoveredCandidates[].status === 'unresolved'` → 提示用户补 `ModuleHints` 或拒签该 case。
|
|
61
|
+
|
|
62
|
+
详细 schema 见 [glue-generate-testcase/references/testcase-schema.md](../../glue-generate-testcase/references/testcase-schema.md)。
|
|
63
|
+
|
|
64
|
+
## 生成 Spec(Generate Spec)
|
|
65
|
+
|
|
66
|
+
任意 cwd 执行(**只生成不执行**,REQ-SPEC-01):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx -y @epoint-testtech/ep-stage-skill@latest spec \
|
|
70
|
+
--testcase <projectDir>/src/tests/<需求名>/testcase.json5 \
|
|
71
|
+
--out <projectDir>/src/tests/<需求名>/crud.spec.ts
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
生成的 spec(v1 初始版)应当:
|
|
75
|
+
|
|
76
|
+
- 从 `../../skeletons` 导入 `CrudPage`、`LoginPage`、`MenuPage` 以及 `type CrudFlowSlots`
|
|
77
|
+
- 定义 `const crudSlots: CrudFlowSlots`
|
|
78
|
+
- 调用 `await crudPage.runCrudFlow()`
|
|
79
|
+
- 仅覆盖 `coveredActions`(胶水部分);`uncoveredCandidates` 留给 `run` 子命令 AI 推理追加
|
|
80
|
+
- 避免内联 Playwright 操作流程
|
|
81
|
+
|
|
82
|
+
## 执行 + AI 推理追加(Run + AI Inference Append)
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx -y @epoint-testtech/ep-stage-skill@latest run \
|
|
86
|
+
--spec <projectDir>/src/tests/<需求名>/crud.spec.ts \
|
|
87
|
+
--testcase <projectDir>/src/tests/<需求名>/testcase.json5 \
|
|
88
|
+
--headless false
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
run 子命令内部:
|
|
92
|
+
|
|
93
|
+
1. 读 `<需求名>/credentials.json5`(数组形态多角色,REQ-ENV-02)
|
|
94
|
+
2. 启动 Playwright → 按 `cases[].requiredRole` 切换登录
|
|
95
|
+
3. 跑 v1 胶水部分 spec
|
|
96
|
+
4. 对 `uncoveredCandidates[].status === 'planned'` 项 AI 实时推理(页面浏览 + 补 spec),用 `uncoveredCandidates[].requiredRole` 切换登录
|
|
97
|
+
5. 追加到 spec 底部产 v2(带 `// === AI 钻探追加 ===` 分段注释)
|
|
98
|
+
6. 报告落 `e2e/glue-code-mvp/glue-report/<run-timestamp>/`
|
|
99
|
+
7. 回写 `~/.ep-stage/projects.index.json5` 的 `systems[].credentialsCache` + `requirements[].lastRun*`
|
|
100
|
+
|
|
101
|
+
**重跑 run**:检测到 spec 已含 `// === AI 钻探追加 ===` 分段时**覆盖追加段**,不保留旧追加(REQ-SPEC-01 v1/v2 生命周期)。
|
|
102
|
+
|
|
103
|
+
## 凭据配置(Credentials Config)
|
|
104
|
+
|
|
105
|
+
凭据下沉到**需求级** `credentials.json5` 数组形态(REQ-ENV-02 / REQ-ENV-03),**不再用 `.env`**:
|
|
106
|
+
|
|
107
|
+
```json5
|
|
108
|
+
// <projectDir>/src/tests/<需求名>/credentials.json5(gitignore)
|
|
109
|
+
[
|
|
110
|
+
{
|
|
111
|
+
url: "http://192.168.x.x:8100/.../login",
|
|
112
|
+
username: "admin",
|
|
113
|
+
password: "***",
|
|
114
|
+
role: "admin", // 可选,多角色场景必填
|
|
115
|
+
systemName: "主业务系统" // 可选,便于审阅与报告
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
字段约束(Task 1.1 `validateCredentials` 强制):
|
|
121
|
+
|
|
122
|
+
- 必填:`url` / `username` / `password`
|
|
123
|
+
- 可选:`role`(用于 Playwright 多角色登录切换)/ `systemName`
|
|
124
|
+
- `(url, role)` 组合唯一
|
|
125
|
+
|
|
126
|
+
完整流程参考 [glue-generate-testcase SKILL.md](../../glue-generate-testcase/SKILL.md) 第 5 步「与用户交互确认推断结果」。
|
|
127
|
+
|
|
128
|
+
## 在消费方 glue 项目中本地编译验证(Optional Local Check)
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
cd <projectDir>
|
|
132
|
+
pnpm --ignore-workspace exec tsc --noEmit
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
仅校验 v1 spec 编译通过;不执行测试。Playwright 真实执行走 `run` 子命令。
|
|
136
|
+
|
|
137
|
+
## 失败归类(Failure Triage)
|
|
138
|
+
|
|
139
|
+
若运行时失败,将失败原因归类为:
|
|
140
|
+
|
|
141
|
+
- **系统可达性或登录配置** — credentials.json5 缺字段 / URL 不通 / 角色凭据错;引导场景 C
|
|
142
|
+
- **上游物料不匹配** — code_list.md 缺字段;引导场景 D
|
|
143
|
+
- **`ModuleHints` 缺失或错误** — testcase.json5 `coveredActions` 为空 / `uncoveredCandidates` 长期 `unresolved`
|
|
144
|
+
- **骨架/组件行为** — `CrudPage` / `LoginPage` 实现 bug;本地修复后回归
|
|
145
|
+
- **包生成器缺陷** — `spec` / `run` 子命令产物错误;走 ep-stage-skill 仓库 issue
|
|
@@ -24,7 +24,7 @@ function help() {
|
|
|
24
24
|
|
|
25
25
|
Notes:
|
|
26
26
|
- Relative upstream paths are resolved from the repository root.
|
|
27
|
-
- The contract path is passed to @epoint/ep-stage-skill relative to its package root.
|
|
27
|
+
- The contract path is passed to @epoint-testtech/ep-stage-skill relative to its package root.
|
|
28
28
|
- The script fails when unresolvedSlots is non-empty.
|
|
29
29
|
`);
|
|
30
30
|
}
|
|
@@ -88,7 +88,7 @@ mkdirSync(path.dirname(contractAbs), { recursive: true });
|
|
|
88
88
|
|
|
89
89
|
run([
|
|
90
90
|
'--filter',
|
|
91
|
-
'@epoint/ep-stage-skill',
|
|
91
|
+
'@epoint-testtech/ep-stage-skill',
|
|
92
92
|
'generate:crud-contract',
|
|
93
93
|
'--',
|
|
94
94
|
'--module-id',
|
|
@@ -125,7 +125,7 @@ mkdirSync(path.dirname(specAbs), { recursive: true });
|
|
|
125
125
|
|
|
126
126
|
run([
|
|
127
127
|
'--filter',
|
|
128
|
-
'@epoint/ep-stage-skill',
|
|
128
|
+
'@epoint-testtech/ep-stage-skill',
|
|
129
129
|
'generate:playwright-tests',
|
|
130
130
|
'--',
|
|
131
131
|
'--contract',
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// validate-skill.mjs — 校验 ep-stage scope 下所有 skill 的 SKILL.md frontmatter
|
|
3
|
+
// + 双发现目录(.claude/skills / .agents/skills)symlink 指向。
|
|
4
|
+
//
|
|
5
|
+
// version 字段维护契约(REQ-VER-003):
|
|
6
|
+
// 当 SKILL.md 内容(frontmatter 或正文)发生变更时,维护者必须手动把
|
|
7
|
+
// `version:` 更新为 `YYYY-MM-DD+<7位短SHA>`,其中 <短SHA> 取当前 HEAD。
|
|
8
|
+
// 快捷生成命令:date +"%Y-%m-%d"+$(git rev-parse --short HEAD)
|
|
9
|
+
// pre-commit hook(.husky/pre-commit → check-skill-version-bump.mjs)会
|
|
10
|
+
// 自动校验:改了 SKILL.md 但没改 version 行 → 阻塞 commit。
|
|
2
11
|
import { lstatSync, readFileSync, realpathSync } from 'node:fs';
|
|
3
12
|
import path from 'node:path';
|
|
4
13
|
import { fileURLToPath } from 'node:url';
|
|
@@ -8,6 +17,9 @@ const epStageRoot = path.resolve(__dirname, '..');
|
|
|
8
17
|
const packageRoot = path.resolve(epStageRoot, '../..');
|
|
9
18
|
const repoRoot = path.resolve(packageRoot, '../..');
|
|
10
19
|
|
|
20
|
+
// version 值正则:YYYY-MM-DD+<7位十六进制短SHA>
|
|
21
|
+
const VERSION_PATTERN = /^\d{4}-\d{2}-\d{2}\+[0-9a-f]{7}$/;
|
|
22
|
+
|
|
11
23
|
function readFrontmatter(skillRoot) {
|
|
12
24
|
const skillPath = path.join(skillRoot, 'SKILL.md');
|
|
13
25
|
const text = readFileSync(skillPath, 'utf8');
|
|
@@ -18,17 +30,20 @@ function readFrontmatter(skillRoot) {
|
|
|
18
30
|
return {
|
|
19
31
|
name: frontmatter.match(/^name:\s*(.+)$/m)?.[1]?.trim(),
|
|
20
32
|
description: frontmatter.match(/^description:\s*(.+)$/m)?.[1]?.trim(),
|
|
33
|
+
version: frontmatter.match(/^version:\s*(.+)$/m)?.[1]?.trim(),
|
|
21
34
|
skillPath,
|
|
22
35
|
};
|
|
23
36
|
}
|
|
24
37
|
|
|
25
38
|
function assertSkill(skillRoot, expectedName) {
|
|
26
|
-
const { name, description, skillPath } = readFrontmatter(skillRoot);
|
|
39
|
+
const { name, description, version, skillPath } = readFrontmatter(skillRoot);
|
|
27
40
|
if (name !== expectedName) throw new Error(`Expected name ${expectedName} in ${skillPath}, got ${name ?? 'missing'}`);
|
|
28
41
|
if (!description?.startsWith('使用时机') && !description?.startsWith('Use when')) throw new Error(`${skillPath} description 必须以「使用时机」或「Use when」开头`);
|
|
29
42
|
if (description.length > 1024) throw new Error(`${skillPath} description exceeds 1024 characters`);
|
|
30
43
|
if (path.basename(skillRoot) !== name) throw new Error(`${skillPath} directory basename must match name`);
|
|
31
|
-
|
|
44
|
+
if (!version) throw new Error(`${skillPath} version 字段缺失;维护者改 SKILL.md 时必须同步更新 version(格式 YYYY-MM-DD+<7位短SHA>)`);
|
|
45
|
+
if (!VERSION_PATTERN.test(version)) throw new Error(`${skillPath} version 格式非法 "${version}";应为 YYYY-MM-DD+<7位短SHA>,如 2026-06-23+a9e7576`);
|
|
46
|
+
return { name, descriptionLength: description.length, version, skillRoot };
|
|
32
47
|
}
|
|
33
48
|
|
|
34
49
|
function assertSymlink(linkPath, targetPath) {
|
|
@@ -40,10 +55,14 @@ function assertSymlink(linkPath, targetPath) {
|
|
|
40
55
|
}
|
|
41
56
|
|
|
42
57
|
// ep-stage scope 下四个 skill 的包内真相源目录。
|
|
58
|
+
// 注:Task 0.2 已 git mv 改名 3 个 skill 目录(glue-* 前缀化)。
|
|
59
|
+
// - glue-create-project:Task 2.1 已同步 SKILL.md frontmatter name → 'glue-create-project'。
|
|
60
|
+
// - glue-generate-testcase:Task 3.2 已同步 SKILL.md frontmatter name → 'glue-generate-testcase'。
|
|
61
|
+
// - glue-run-test:Task 4.3 已同步 SKILL.md frontmatter name → 'glue-run-test'。
|
|
43
62
|
const skillSources = [
|
|
44
|
-
{ name: 'create-project', source: path.join(epStageRoot, 'create-project') },
|
|
45
|
-
{ name: 'glue-
|
|
46
|
-
{ name: 'glue-
|
|
63
|
+
{ name: 'glue-create-project', source: path.join(epStageRoot, 'glue-create-project') },
|
|
64
|
+
{ name: 'glue-generate-testcase', source: path.join(epStageRoot, 'glue-generate-testcase') },
|
|
65
|
+
{ name: 'glue-run-test', source: path.join(epStageRoot, 'glue-run-test') },
|
|
47
66
|
{ name: 'recording-to-glue', source: path.join(epStageRoot, 'recording-to-glue') },
|
|
48
67
|
];
|
|
49
68
|
|
|
@@ -57,11 +76,14 @@ const discoveryDirs = [
|
|
|
57
76
|
path.join(repoRoot, '.agents/skills'),
|
|
58
77
|
];
|
|
59
78
|
|
|
79
|
+
// 注:symlink 名总是与包内目录 basename 一致(Phase 0 重建后)。
|
|
80
|
+
// Task 4.3 收敛后,3 个 glue-* skill 的 SKILL.md `name` 与目录 basename 完全一致;symlink 仍以 basename 为准。
|
|
60
81
|
const symlinks = [];
|
|
61
82
|
for (const dir of discoveryDirs) {
|
|
62
83
|
for (const { name, source } of skillSources) {
|
|
63
|
-
|
|
64
|
-
|
|
84
|
+
const linkName = path.basename(source);
|
|
85
|
+
assertSymlink(path.join(dir, linkName), source);
|
|
86
|
+
symlinks.push({ dir: path.relative(repoRoot, dir), name, linkName, pointsTo: path.relative(repoRoot, source) });
|
|
65
87
|
}
|
|
66
88
|
}
|
|
67
89
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 开发期 contract 提取入口(REQ-CLI-03)。被 generate:crud-contract pnpm script 调用,
|
|
3
|
+
* 不在 index.ts 注册为子命令,不出现在任何 SKILL.md。
|
|
4
|
+
*
|
|
5
|
+
* 行为与 0.0.3-alpha.2 的 generate-crud-contract.ts 完全等价;本次重构仅把执行
|
|
6
|
+
* 入口包装成可被 pnpm script 与 generate-testcase 子命令复用的纯函数。
|
|
7
|
+
*
|
|
8
|
+
* @param argv - 命令行参数(--module-id/--docs/--code-list/--webapp/--java-actions/--hints/--out)。
|
|
9
|
+
* @returns 写入的 contract.json 绝对路径。
|
|
10
|
+
*/
|
|
11
|
+
export declare function runExtractContract(argv: string[]): {
|
|
12
|
+
contractPath: string;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=extract-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-contract.d.ts","sourceRoot":"","sources":["../../../../src/cli/dev/extract-contract.ts"],"names":[],"mappings":"AA+GA;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CAkB3E"}
|