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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/SKILL.md +27 -27
  2. package/codex-skill/ep-stage/glue-create-project/SKILL.md +223 -206
  3. package/codex-skill/ep-stage/glue-generate-testcase/SKILL.md +275 -274
  4. package/codex-skill/ep-stage/glue-generate-testcase/examples/observable-testcase.json +40 -40
  5. package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-schema.md +95 -112
  6. package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-tpl.md +72 -72
  7. package/codex-skill/ep-stage/glue-run-test/SKILL.md +292 -289
  8. package/codex-skill/ep-stage/glue-run-test/references/ai-runtime-loop.md +30 -0
  9. package/codex-skill/ep-stage/glue-run-test/references/crud-pipeline.md +132 -145
  10. package/codex-skill/ep-stage/glue-run-test/references/gap-review-protocol.md +44 -43
  11. package/codex-skill/ep-stage/glue-run-test/references/harness-principles.md +46 -46
  12. package/codex-skill/ep-stage/glue-run-test/references/playwright-cli-odav.md +166 -0
  13. package/codex-skill/ep-stage/glue-run-test/scripts/extract-page-elements-eval.mjs +233 -0
  14. package/codex-skill/ep-stage/glue-run-test/scripts/generate-crud-spec.mjs +149 -149
  15. package/codex-skill/ep-stage/page-element-scan/SKILL.md +91 -0
  16. package/codex-skill/ep-stage/page-element-scan/evals/evals.json +37 -0
  17. package/codex-skill/ep-stage/page-element-scan/evals/fixtures/sample-list-page.html +85 -0
  18. package/codex-skill/ep-stage/page-element-scan/references/pagination-controls.md +153 -0
  19. package/codex-skill/ep-stage/page-element-scan/scripts/extract-page-elements-eval.mjs +233 -0
  20. package/codex-skill/ep-stage/page-element-scan/scripts/extract-page-elements.mjs +298 -0
  21. package/codex-skill/ep-stage/page-element-scan/scripts/scan-page.mjs +749 -0
  22. package/codex-skill/ep-stage/recording-to-glue/SKILL.md +28 -28
  23. package/codex-skill/ep-stage/scripts/validate-skill.mjs +98 -95
  24. package/dist/src/cli/generate-testcase.d.ts +17 -51
  25. package/dist/src/cli/generate-testcase.d.ts.map +1 -1
  26. package/dist/src/cli/generate-testcase.js +74 -181
  27. package/dist/src/cli/index.d.ts +3 -2
  28. package/dist/src/cli/index.d.ts.map +1 -1
  29. package/dist/src/cli/index.js +28 -15
  30. package/dist/src/cli/run.d.ts +136 -29
  31. package/dist/src/cli/run.d.ts.map +1 -1
  32. package/dist/src/cli/run.js +494 -65
  33. package/dist/src/cli/spec.d.ts +17 -13
  34. package/dist/src/cli/spec.d.ts.map +1 -1
  35. package/dist/src/cli/spec.js +59 -33
  36. package/dist/src/contracts/crud-business-module.d.ts +6 -0
  37. package/dist/src/contracts/crud-business-module.d.ts.map +1 -1
  38. package/dist/src/contracts/glue-contract-v1.d.ts +98 -0
  39. package/dist/src/contracts/glue-contract-v1.d.ts.map +1 -0
  40. package/dist/src/contracts/glue-contract-v1.js +9 -0
  41. package/dist/src/contracts/observable-chain.d.ts +4 -0
  42. package/dist/src/contracts/observable-chain.d.ts.map +1 -1
  43. package/dist/src/extractors/code-list-json5.d.ts +192 -0
  44. package/dist/src/extractors/code-list-json5.d.ts.map +1 -0
  45. package/dist/src/extractors/code-list-json5.js +91 -0
  46. package/dist/src/extractors/html-page.d.ts +9 -0
  47. package/dist/src/extractors/html-page.d.ts.map +1 -1
  48. package/dist/src/extractors/html-page.js +26 -2
  49. package/dist/src/extractors/prd-extractor.d.ts +70 -0
  50. package/dist/src/extractors/prd-extractor.d.ts.map +1 -0
  51. package/dist/src/extractors/prd-extractor.js +248 -0
  52. package/dist/src/generators/agent-inferred-workflow-script.js +66 -66
  53. package/dist/src/generators/stage-skeleton-script.d.ts +87 -20
  54. package/dist/src/generators/stage-skeleton-script.d.ts.map +1 -1
  55. package/dist/src/generators/stage-skeleton-script.js +647 -238
  56. package/dist/src/index.d.ts +2 -2
  57. package/dist/src/index.d.ts.map +1 -1
  58. package/dist/src/index.js +1 -1
  59. package/dist/src/normalizers/crud-contract.d.ts +3 -0
  60. package/dist/src/normalizers/crud-contract.d.ts.map +1 -1
  61. package/dist/src/normalizers/crud-contract.js +3 -0
  62. package/dist/src/runtime/ai-runtime-trace.d.ts +88 -0
  63. package/dist/src/runtime/ai-runtime-trace.d.ts.map +1 -0
  64. package/dist/src/runtime/ai-runtime-trace.js +119 -0
  65. package/dist/src/testcase/button-interaction-inference.d.ts +37 -0
  66. package/dist/src/testcase/button-interaction-inference.d.ts.map +1 -0
  67. package/dist/src/testcase/button-interaction-inference.js +385 -0
  68. package/dist/src/testcase/contract-testcase-assembler.d.ts +44 -0
  69. package/dist/src/testcase/contract-testcase-assembler.d.ts.map +1 -0
  70. package/dist/src/testcase/contract-testcase-assembler.js +806 -0
  71. package/dist/src/testcase/prd-button-mapper.d.ts +72 -0
  72. package/dist/src/testcase/prd-button-mapper.d.ts.map +1 -0
  73. package/dist/src/testcase/prd-button-mapper.js +148 -0
  74. package/dist/src/testcase/runtime-context.d.ts +9 -0
  75. package/dist/src/testcase/runtime-context.d.ts.map +1 -0
  76. package/dist/src/testcase/runtime-context.js +1 -0
  77. package/dist/src/testcase/test-access.d.ts +38 -0
  78. package/dist/src/testcase/test-access.d.ts.map +1 -0
  79. package/dist/src/testcase/test-access.js +106 -0
  80. package/dist/src/testcase/testcase-execution-fields.d.ts +10 -0
  81. package/dist/src/testcase/testcase-execution-fields.d.ts.map +1 -0
  82. package/dist/src/testcase/testcase-execution-fields.js +1 -0
  83. package/dist/src/testcase/testcase-generator.d.ts.map +1 -1
  84. package/dist/src/testcase/testcase-generator.js +6 -0
  85. package/dist/src/testcase/testcase-markdown.d.ts +11 -0
  86. package/dist/src/testcase/testcase-markdown.d.ts.map +1 -0
  87. package/dist/src/testcase/testcase-markdown.js +181 -0
  88. package/dist/src/testcase/testcase-skeleton.d.ts +4 -0
  89. package/dist/src/testcase/testcase-skeleton.d.ts.map +1 -1
  90. package/dist/src/testcase/testcase-spec-assembly.d.ts +4 -0
  91. package/dist/src/testcase/testcase-spec-assembly.d.ts.map +1 -1
  92. package/dist/src/testcase/testcase-v1-draft.d.ts +58 -0
  93. package/dist/src/testcase/testcase-v1-draft.d.ts.map +1 -0
  94. package/dist/src/testcase/testcase-v1-draft.js +13 -0
  95. package/dist/src/validation/contract.d.ts +14 -0
  96. package/dist/src/validation/contract.d.ts.map +1 -0
  97. package/dist/src/validation/contract.js +132 -0
  98. package/dist/src/validation/credentials.d.ts +7 -2
  99. package/dist/src/validation/credentials.d.ts.map +1 -1
  100. package/dist/src/validation/credentials.js +15 -4
  101. package/dist/src/validation/index.d.ts +1 -0
  102. package/dist/src/validation/index.d.ts.map +1 -1
  103. package/dist/src/validation/index.js +1 -0
  104. package/dist/src/validation/testcase.d.ts +9 -7
  105. package/dist/src/validation/testcase.d.ts.map +1 -1
  106. package/dist/src/validation/testcase.js +181 -34
  107. package/docs/README.md +124 -134
  108. package/docs/mvp-usage-guide.md +298 -298
  109. package/examples/schemeresource-observable-docs/schemeresource.context.md +20 -20
  110. package/examples/schemeresource.module-hints.json +38 -38
  111. package/examples/schemeresource.observable.code_list.md +37 -37
  112. package/examples/zwplace-observable-docs/zwplace.context.md +16 -16
  113. package/examples/zwplace-placecategory-validation.json +29 -29
  114. package/examples/zwplace.module-hints.json +69 -69
  115. package/examples/zwplace.observable.code_list.md +37 -37
  116. package/package.json +2 -2
  117. package/codex-skill/ep-stage/.omc/state/sessions/d636ad35-5fc9-4d5e-85b0-2bcad7d86ee5/pre-tool-advisory-throttle.json +0 -10
  118. package/dist/test/cli/extract-contract.test.d.ts +0 -2
  119. package/dist/test/cli/extract-contract.test.d.ts.map +0 -1
  120. package/dist/test/cli/extract-contract.test.js +0 -32
  121. package/dist/test/cli/generate-testcase-inference.test.d.ts +0 -2
  122. package/dist/test/cli/generate-testcase-inference.test.d.ts.map +0 -1
  123. package/dist/test/cli/generate-testcase-inference.test.js +0 -63
  124. package/dist/test/cli/generate-testcase-paths.test.d.ts +0 -2
  125. package/dist/test/cli/generate-testcase-paths.test.d.ts.map +0 -1
  126. package/dist/test/cli/generate-testcase-paths.test.js +0 -165
  127. package/dist/test/cli/generate-testcase-requirements.test.d.ts +0 -2
  128. package/dist/test/cli/generate-testcase-requirements.test.d.ts.map +0 -1
  129. package/dist/test/cli/generate-testcase-requirements.test.js +0 -229
  130. package/dist/test/cli/generate-testcase.test.d.ts +0 -2
  131. package/dist/test/cli/generate-testcase.test.d.ts.map +0 -1
  132. package/dist/test/cli/generate-testcase.test.js +0 -130
  133. package/dist/test/cli/index.test.d.ts +0 -2
  134. package/dist/test/cli/index.test.d.ts.map +0 -1
  135. package/dist/test/cli/index.test.js +0 -113
  136. package/dist/test/cli/probe.test.d.ts +0 -2
  137. package/dist/test/cli/probe.test.d.ts.map +0 -1
  138. package/dist/test/cli/probe.test.js +0 -31
  139. package/dist/test/cli/run.test.d.ts +0 -2
  140. package/dist/test/cli/run.test.d.ts.map +0 -1
  141. package/dist/test/cli/run.test.js +0 -149
  142. package/dist/test/cli/spec-boundary.test.d.ts +0 -2
  143. package/dist/test/cli/spec-boundary.test.d.ts.map +0 -1
  144. package/dist/test/cli/spec-boundary.test.js +0 -198
  145. package/dist/test/cli/spec.test.d.ts +0 -2
  146. package/dist/test/cli/spec.test.d.ts.map +0 -1
  147. package/dist/test/cli/spec.test.js +0 -196
  148. package/dist/test/code-list.test.d.ts +0 -2
  149. package/dist/test/code-list.test.d.ts.map +0 -1
  150. package/dist/test/code-list.test.js +0 -39
  151. package/dist/test/crud-contract.test.d.ts +0 -2
  152. package/dist/test/crud-contract.test.d.ts.map +0 -1
  153. package/dist/test/crud-contract.test.js +0 -799
  154. package/dist/test/gap-inference.test.d.ts +0 -2
  155. package/dist/test/gap-inference.test.d.ts.map +0 -1
  156. package/dist/test/gap-inference.test.js +0 -597
  157. package/dist/test/generalization.test.d.ts +0 -2
  158. package/dist/test/generalization.test.d.ts.map +0 -1
  159. package/dist/test/generalization.test.js +0 -73
  160. package/dist/test/inference/module-hints-inference.test.d.ts +0 -2
  161. package/dist/test/inference/module-hints-inference.test.d.ts.map +0 -1
  162. package/dist/test/inference/module-hints-inference.test.js +0 -25
  163. package/dist/test/inference/source-analysis.test.d.ts +0 -2
  164. package/dist/test/inference/source-analysis.test.d.ts.map +0 -1
  165. package/dist/test/inference/source-analysis.test.js +0 -23
  166. package/dist/test/material-inventory.test.d.ts +0 -2
  167. package/dist/test/material-inventory.test.d.ts.map +0 -1
  168. package/dist/test/material-inventory.test.js +0 -154
  169. package/dist/test/observable-chain.test.d.ts +0 -2
  170. package/dist/test/observable-chain.test.d.ts.map +0 -1
  171. package/dist/test/observable-chain.test.js +0 -123
  172. package/dist/test/observable-pipeline.test.d.ts +0 -2
  173. package/dist/test/observable-pipeline.test.d.ts.map +0 -1
  174. package/dist/test/observable-pipeline.test.js +0 -470
  175. package/dist/test/page-structure.test.d.ts +0 -2
  176. package/dist/test/page-structure.test.d.ts.map +0 -1
  177. package/dist/test/page-structure.test.js +0 -45
  178. package/dist/test/scenario-inference.test.d.ts +0 -2
  179. package/dist/test/scenario-inference.test.d.ts.map +0 -1
  180. package/dist/test/scenario-inference.test.js +0 -73
  181. package/dist/test/stage-context.test.d.ts +0 -2
  182. package/dist/test/stage-context.test.d.ts.map +0 -1
  183. package/dist/test/stage-context.test.js +0 -395
  184. package/dist/test/testcase-generator.test.d.ts +0 -2
  185. package/dist/test/testcase-generator.test.d.ts.map +0 -1
  186. package/dist/test/testcase-generator.test.js +0 -276
  187. package/dist/test/testcase-skeleton.test.d.ts +0 -2
  188. package/dist/test/testcase-skeleton.test.d.ts.map +0 -1
  189. package/dist/test/testcase-skeleton.test.js +0 -185
  190. package/dist/test/testcase-spec-assembly.test.d.ts +0 -2
  191. package/dist/test/testcase-spec-assembly.test.d.ts.map +0 -1
  192. package/dist/test/testcase-spec-assembly.test.js +0 -105
  193. package/dist/test/util/credentials.test.d.ts +0 -2
  194. package/dist/test/util/credentials.test.d.ts.map +0 -1
  195. package/dist/test/util/credentials.test.js +0 -64
  196. package/dist/test/util/i18n-testcase.test.d.ts +0 -2
  197. package/dist/test/util/i18n-testcase.test.d.ts.map +0 -1
  198. package/dist/test/util/i18n-testcase.test.js +0 -119
  199. package/dist/test/util/softlink.test.d.ts +0 -2
  200. package/dist/test/util/softlink.test.d.ts.map +0 -1
  201. package/dist/test/util/softlink.test.js +0 -82
  202. package/dist/test/validation/credentials.test.d.ts +0 -2
  203. package/dist/test/validation/credentials.test.d.ts.map +0 -1
  204. package/dist/test/validation/credentials.test.js +0 -72
  205. package/dist/test/validation/projects-index.test.d.ts +0 -2
  206. package/dist/test/validation/projects-index.test.d.ts.map +0 -1
  207. package/dist/test/validation/projects-index.test.js +0 -48
  208. package/dist/test/validation/testcase.test.d.ts +0 -2
  209. package/dist/test/validation/testcase.test.d.ts.map +0 -1
  210. package/dist/test/validation/testcase.test.js +0 -129
  211. package/dist/vitest.config.d.ts +0 -3
  212. package/dist/vitest.config.d.ts.map +0 -1
  213. package/dist/vitest.config.js +0 -7
@@ -1 +1 @@
1
- {"version":3,"file":"html-page.d.ts","sourceRoot":"","sources":["../../../src/extractors/html-page.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC,WAAW,EAAE,uBAAuB,EAAE,CAAC;IACvC,eAAe,CAAC,EAAE,uBAAuB,CAAC;IAC1C,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC,UAAU,EAAE,sBAAsB,EAAE,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,QAAQ,EAAE,4BAA4B,CAAC;IACvC,eAAe,EAAE,4BAA4B,CAAC;IAC9C,KAAK,EAAE,4BAA4B,CAAC;IACpC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,eAAe,EAAE,OAAO,CAAC;IACzB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,oBAAoB,EAAE,6BAA6B,CAAC;CACrD,CAAC;AAgFF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CA0InE"}
1
+ {"version":3,"file":"html-page.d.ts","sourceRoot":"","sources":["../../../src/extractors/html-page.ts"],"names":[],"mappings":"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"}
@@ -38,6 +38,17 @@ function buildButtonLocator(label, tagName) {
38
38
  }
39
39
  return `xpath=//a[contains(@class,'mini-button')][normalize-space()='${label}'] | //button[contains(@class,'mini-button')][normalize-space()='${label}']`;
40
40
  }
41
+ function parseEditorAttribute(editorAttribute) {
42
+ const normalized = normalizeAttribute(editorAttribute);
43
+ if (!normalized)
44
+ return {};
45
+ const typeMatch = normalized.match(/type\s*:\s*['"]([^'"]+)['"]/i);
46
+ const actionMatch = normalized.match(/action\s*:\s*['"]([^'"]+)['"]/i);
47
+ return {
48
+ type: typeMatch?.[1],
49
+ action: actionMatch?.[1],
50
+ };
51
+ }
41
52
  function buildFieldLocator(label, controlType) {
42
53
  if (controlType === 'mini-textbox') {
43
54
  return `//div[@label='${label}']//input[@class='mini-textbox-input']`;
@@ -116,7 +127,14 @@ export function extractHtmlPage(filePath) {
116
127
  const field = column.attr('field') ?? '';
117
128
  const columnType = column.attr('type') ?? (field ? 'data' : 'unknown');
118
129
  const headerText = normalizeText(column.clone().children().remove().end().text());
119
- const extractedColumn = { field, headerText, columnType };
130
+ const editor = parseEditorAttribute(column.attr('editor'));
131
+ const extractedColumn = {
132
+ field,
133
+ headerText,
134
+ columnType,
135
+ editorType: editor.type,
136
+ editorAction: editor.action,
137
+ };
120
138
  if (columnType === 'checkcolumn') {
121
139
  selectionColumn = extractedColumn;
122
140
  return;
@@ -149,7 +167,13 @@ export function extractHtmlPage(filePath) {
149
167
  dataColumns,
150
168
  selectionColumn,
151
169
  indexColumn,
152
- rowActions
170
+ rowActions,
171
+ allowCellEdit: booleanAttribute(grid.attr('allowCellEdit')) || undefined,
172
+ cellEditAction: normalizeAttribute(grid.attr('cellEditAction')) || undefined,
173
+ allowCellSelect: booleanAttribute(grid.attr('allowCellSelect')) || undefined,
174
+ editNextOnEnterKey: booleanAttribute(grid.attr('editNextOnEnterKey')) || undefined,
175
+ oncellbeginedit: normalizeAttribute(grid.attr('oncellbeginedit')) ? true : undefined,
176
+ onrowdblclick: normalizeAttribute(grid.attr('onrowdblclick')) ? true : undefined,
153
177
  });
154
178
  });
155
179
  const scriptContent = $('script')
@@ -0,0 +1,70 @@
1
+ /**
2
+ * PRD markdown 解析器。
3
+ *
4
+ * 职责:
5
+ * 1. 在需求目录中发现 PRD markdown 文件。
6
+ * 2. 解析 PRD 章节 4.1(功能列表)和 4.2.x(功能详细说明)。
7
+ * 3. 输出结构化的 PrdFunctionEntry[],供 testcase 装配阶段确定性使用。
8
+ *
9
+ * 设计取舍:
10
+ * - 全程使用正则/行解析,不调用 LLM,保证幂等性。
11
+ * - 只识别当前观察到的 PRD 结构(`4.1` 表格 + `4.2.x` 子章节 + 粗体字段标题)。
12
+ * - 解析失败时返回空数组并在 inferenceTrace 中记录,不阻断 pipeline。
13
+ */
14
+ /** PRD 中的单个功能条目。 */
15
+ export type PrdFunctionEntry = {
16
+ /** 功能编号,如 F001。 */
17
+ featureId: string;
18
+ /** 功能名称,如「同步窗口管理系统」。 */
19
+ featureName: string;
20
+ /** 功能模块,如「大厅地址管理」。 */
21
+ featureModule?: string;
22
+ /** 功能描述。 */
23
+ description: string;
24
+ /** 输入条件。 */
25
+ inputConditions?: string;
26
+ /** 处理逻辑,保留列表顺序。 */
27
+ processingLogic: string[];
28
+ /** 输出结果,保留列表顺序。 */
29
+ outputResult: string[];
30
+ /** 业务规则,保留列表顺序。 */
31
+ businessRules: string[];
32
+ /** 约束条件,保留列表顺序。 */
33
+ constraints: string[];
34
+ };
35
+ /** PRD 提取产物。 */
36
+ export type PrdExtraction = {
37
+ /** PRD 文件绝对路径。 */
38
+ prdPath: string;
39
+ /** 解析出的功能条目,按 PRD 顺序排列。 */
40
+ functions: PrdFunctionEntry[];
41
+ /** 解析时间 ISO 字符串,仅用于审计,不影响 testcase 生成。 */
42
+ generatedAt: string;
43
+ };
44
+ /**
45
+ * 在目录中查找 PRD markdown 文件。
46
+ *
47
+ * 规则(满足任一即返回):
48
+ * 1. 文件名包含 `prd`(大小写不敏感)且以 `.md` 结尾。
49
+ * 2. 文件正文包含 `## 4. 功能需求` 标题。
50
+ *
51
+ * @param dir - 需求文档目录。
52
+ * @returns PRD 文件绝对路径;不存在时返回 undefined。
53
+ */
54
+ export declare function findPrdMarkdown(dir: string): string | undefined;
55
+ /**
56
+ * 读取 PRD 文件并提取功能条目。
57
+ *
58
+ * @param prdPath - PRD markdown 文件绝对路径。
59
+ * @param now - 可选的解析时间戳,用于测试冻结。
60
+ * @returns PRD 提取产物。
61
+ */
62
+ export declare function loadPrdExtraction(prdPath: string, now?: string): PrdExtraction;
63
+ /**
64
+ * 解析 PRD markdown 文本,提取功能列表 + 功能详细说明。
65
+ *
66
+ * @param markdown - PRD 完整 markdown 文本。
67
+ * @returns 按 PRD 顺序排列的功能条目数组。
68
+ */
69
+ export declare function extractPrdFunctions(markdown: string): PrdFunctionEntry[];
70
+ //# sourceMappingURL=prd-extractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prd-extractor.d.ts","sourceRoot":"","sources":["../../../src/extractors/prd-extractor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,oBAAoB;AACpB,MAAM,MAAM,gBAAgB,GAAG;IAC7B,mBAAmB;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,sBAAsB;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY;IACZ,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,mBAAmB;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB;IACnB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,mBAAmB;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAEF,gBAAgB;AAChB,MAAM,MAAM,aAAa,GAAG;IAC1B,kBAAkB;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAsB/D;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,SAA2B,GAAG,aAAa,CAIhG;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAqBxE"}
@@ -0,0 +1,248 @@
1
+ /**
2
+ * PRD markdown 解析器。
3
+ *
4
+ * 职责:
5
+ * 1. 在需求目录中发现 PRD markdown 文件。
6
+ * 2. 解析 PRD 章节 4.1(功能列表)和 4.2.x(功能详细说明)。
7
+ * 3. 输出结构化的 PrdFunctionEntry[],供 testcase 装配阶段确定性使用。
8
+ *
9
+ * 设计取舍:
10
+ * - 全程使用正则/行解析,不调用 LLM,保证幂等性。
11
+ * - 只识别当前观察到的 PRD 结构(`4.1` 表格 + `4.2.x` 子章节 + 粗体字段标题)。
12
+ * - 解析失败时返回空数组并在 inferenceTrace 中记录,不阻断 pipeline。
13
+ */
14
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
15
+ import path from 'node:path';
16
+ /**
17
+ * 在目录中查找 PRD markdown 文件。
18
+ *
19
+ * 规则(满足任一即返回):
20
+ * 1. 文件名包含 `prd`(大小写不敏感)且以 `.md` 结尾。
21
+ * 2. 文件正文包含 `## 4. 功能需求` 标题。
22
+ *
23
+ * @param dir - 需求文档目录。
24
+ * @returns PRD 文件绝对路径;不存在时返回 undefined。
25
+ */
26
+ export function findPrdMarkdown(dir) {
27
+ if (!existsSync(dir)) {
28
+ return undefined;
29
+ }
30
+ const candidates = readdirSync(dir).filter((file) => file.endsWith('.md'));
31
+ for (const file of candidates) {
32
+ const fullPath = path.join(dir, file);
33
+ if (/prd/i.test(file)) {
34
+ return fullPath;
35
+ }
36
+ }
37
+ for (const file of candidates) {
38
+ const fullPath = path.join(dir, file);
39
+ const content = readFileSync(fullPath, 'utf8');
40
+ if (/##\s*4\.\s*功能需求/.test(content)) {
41
+ return fullPath;
42
+ }
43
+ }
44
+ return undefined;
45
+ }
46
+ /**
47
+ * 读取 PRD 文件并提取功能条目。
48
+ *
49
+ * @param prdPath - PRD markdown 文件绝对路径。
50
+ * @param now - 可选的解析时间戳,用于测试冻结。
51
+ * @returns PRD 提取产物。
52
+ */
53
+ export function loadPrdExtraction(prdPath, now = new Date().toISOString()) {
54
+ const markdown = readFileSync(prdPath, 'utf8');
55
+ const functions = extractPrdFunctions(markdown);
56
+ return { prdPath, functions, generatedAt: now };
57
+ }
58
+ /**
59
+ * 解析 PRD markdown 文本,提取功能列表 + 功能详细说明。
60
+ *
61
+ * @param markdown - PRD 完整 markdown 文本。
62
+ * @returns 按 PRD 顺序排列的功能条目数组。
63
+ */
64
+ export function extractPrdFunctions(markdown) {
65
+ const functionList = parseFunctionList(markdown);
66
+ const details = parseFunctionDetails(markdown);
67
+ const detailMap = new Map(details.map((d) => [d.featureId, d]));
68
+ // 以功能列表为基准顺序,合并详细说明;若详细说明缺失则使用列表中的描述兜底。
69
+ return functionList.map((listItem) => {
70
+ const detail = detailMap.get(listItem.featureId);
71
+ return {
72
+ featureId: listItem.featureId,
73
+ featureName: listItem.featureName,
74
+ featureModule: listItem.featureModule,
75
+ description: detail?.description ?? listItem.description,
76
+ inputConditions: detail?.inputConditions,
77
+ processingLogic: detail?.processingLogic ?? [],
78
+ outputResult: detail?.outputResult ?? [],
79
+ businessRules: detail?.businessRules ?? [],
80
+ constraints: detail?.constraints ?? [],
81
+ };
82
+ });
83
+ }
84
+ /**
85
+ * 解析 4.1 功能列表表格。
86
+ *
87
+ * 表格列(按当前 PRD 约定):
88
+ * | 功能模块 | 功能编号 | 功能名称 | 功能描述 | 功能类型 | ... |
89
+ */
90
+ function parseFunctionList(markdown) {
91
+ const result = [];
92
+ const sectionMatch = markdown.match(/###\s*4\.1\s*功能列表[\s\S]*?(?=###\s*4\.2|##\s+5|\n##\s)/);
93
+ if (!sectionMatch) {
94
+ return result;
95
+ }
96
+ const lines = sectionMatch[0].split('\n');
97
+ for (const line of lines) {
98
+ const trimmed = line.trim();
99
+ if (!trimmed.startsWith('|'))
100
+ continue;
101
+ if (trimmed.includes('功能编号'))
102
+ continue; // header / separator
103
+ if (/^\|\s*-+/.test(trimmed))
104
+ continue; // markdown separator
105
+ const cells = trimmed
106
+ .split('|')
107
+ .map((c) => c.trim())
108
+ .filter((c) => c.length > 0);
109
+ if (cells.length < 4)
110
+ continue;
111
+ const featureModule = cells[0];
112
+ const featureId = cells[1];
113
+ const featureName = cells[2];
114
+ const description = cells[3];
115
+ if (!/^F\d{3,}$/i.test(featureId))
116
+ continue;
117
+ result.push({ featureId: featureId.toUpperCase(), featureName, featureModule, description });
118
+ }
119
+ return result;
120
+ }
121
+ /**
122
+ * 解析 4.2.x 功能详细说明。
123
+ */
124
+ function parseFunctionDetails(markdown) {
125
+ const result = [];
126
+ // 匹配每个 4.2.x 子章节:#### 4.2.x Fxxx 功能名称
127
+ const sectionRegex = /####\s*4\.2\.(\d+)\s+((F\d{3,})\s+(.+?))\s*(?=\n)/g;
128
+ let match;
129
+ const sections = [];
130
+ while ((match = sectionRegex.exec(markdown)) !== null) {
131
+ sections.push({
132
+ start: match.index,
133
+ featureId: match[3].toUpperCase(),
134
+ featureName: match[4].trim(),
135
+ end: match.index + match[0].length,
136
+ });
137
+ }
138
+ for (let i = 0; i < sections.length; i++) {
139
+ const current = sections[i];
140
+ const nextSection = sections[i + 1];
141
+ const sectionEnd = findSectionEnd(current.end, markdown, nextSection?.start);
142
+ const sectionBody = markdown.slice(current.end, sectionEnd);
143
+ result.push(parseFunctionDetailSection(current.featureId, current.featureName, sectionBody));
144
+ }
145
+ return result;
146
+ }
147
+ /**
148
+ * 计算子章节结束位置。
149
+ *
150
+ * 结束位置取以下候选的最小值:
151
+ * 1. 下一个同层 `####` 子章节的开始位置。
152
+ * 2. 下一个更高层标题(`###` 或 `##`)的开始位置。
153
+ * 3. markdown 末尾。
154
+ */
155
+ function findSectionEnd(start, markdown, nextSectionStart) {
156
+ const nextHeadingMatch = markdown.slice(start).match(/\n#{1,3}\s/m);
157
+ const nextHeadingStart = nextHeadingMatch
158
+ ? start + nextHeadingMatch.index + 1 // +1 跳过前面的换行
159
+ : undefined;
160
+ const candidates = [nextSectionStart, nextHeadingStart].filter((n) => n !== undefined);
161
+ return candidates.length > 0 ? Math.min(...candidates) : markdown.length;
162
+ }
163
+ /**
164
+ * 解析单个 4.2.x 子章节内容。
165
+ */
166
+ function parseFunctionDetailSection(featureId, featureName, sectionBody) {
167
+ const fields = splitDetailFields(sectionBody);
168
+ return {
169
+ featureId,
170
+ featureName,
171
+ description: fields['功能描述']?.join('\n').trim() ?? '',
172
+ inputConditions: fields['输入条件']?.join('\n').trim() || undefined,
173
+ processingLogic: extractListItems(fields['处理逻辑']),
174
+ outputResult: extractListItems(fields['输出结果']),
175
+ businessRules: extractListItems(fields['业务规则']),
176
+ constraints: extractListItems(fields['约束条件']),
177
+ };
178
+ }
179
+ /**
180
+ * 按粗体字段标题把子章节内容切分成多个字段块。
181
+ *
182
+ * 字段标题形如 `**功能描述**:` 或 `**功能描述**:`(注意中文冒号)。
183
+ */
184
+ function splitDetailFields(body) {
185
+ const lines = body.split('\n');
186
+ const fields = {};
187
+ let currentField;
188
+ const fieldHeaderRegex = /^\*\*([^*]+)\*\*[::]\s*(.*)$/;
189
+ for (const rawLine of lines) {
190
+ const line = rawLine.trimEnd();
191
+ const headerMatch = line.trim().match(fieldHeaderRegex);
192
+ if (headerMatch) {
193
+ currentField = headerMatch[1].trim();
194
+ const rest = headerMatch[2].trim();
195
+ fields[currentField] = [];
196
+ if (rest) {
197
+ fields[currentField].push(rest);
198
+ }
199
+ continue;
200
+ }
201
+ if (currentField && line.trim().length > 0) {
202
+ // 跳过空行,但保留列表项和缩进内容
203
+ fields[currentField].push(line);
204
+ }
205
+ }
206
+ return fields;
207
+ }
208
+ /**
209
+ * 从字段块中提取列表项。
210
+ *
211
+ * 支持:
212
+ * - 有序列表 `1. ...`
213
+ * - 无序列表 `- ...`
214
+ * - 缩进列表(二级列表作为前一项的延续,不单独拆分)
215
+ *
216
+ * 过滤:
217
+ * - 空项
218
+ * - 纯 `--` 等表格/排版分隔符
219
+ */
220
+ function extractListItems(lines) {
221
+ if (!lines || lines.length === 0)
222
+ return [];
223
+ const items = [];
224
+ for (const rawLine of lines) {
225
+ const line = rawLine.trim();
226
+ if (line.length === 0)
227
+ continue;
228
+ if (/^-+$/.test(line))
229
+ continue; // 过滤 '--' 分隔符
230
+ const listMatch = line.match(/^(?:\d+\.|-)\s+(.+)$/);
231
+ if (listMatch) {
232
+ const content = listMatch[1].trim();
233
+ if (content)
234
+ items.push(content);
235
+ }
236
+ else if (line.startsWith('-')) {
237
+ const content = line.replace(/^-\s*/, '').trim();
238
+ if (content)
239
+ items.push(content);
240
+ }
241
+ else if (items.length > 0 && rawLine.startsWith(' ')) {
242
+ // 缩进内容追加到前一项
243
+ items[items.length - 1] += ' ' + line;
244
+ }
245
+ // 非列表行(如普通段落)忽略,避免把标题残留混入
246
+ }
247
+ return items;
248
+ }
@@ -104,20 +104,20 @@ function renderGapFindingBlock(workflow, label, assertion) {
104
104
  const kind = /暂未实现|未实现|未配置|联系管理员/.test(observed)
105
105
  ? 'feature_incomplete'
106
106
  : 'behavior_mismatch';
107
- return `
108
- gapFinding: {
109
- kind: ${singleQuoted(kind)},
110
- summary: ${singleQuoted(`运行态推理发现「${label}」未满足业务预期。`)},
111
- observed: ${singleQuoted(observed)},
107
+ return `
108
+ gapFinding: {
109
+ kind: ${singleQuoted(kind)},
110
+ summary: ${singleQuoted(`运行态推理发现「${label}」未满足业务预期。`)},
111
+ observed: ${singleQuoted(observed)},
112
112
  },`;
113
113
  }
114
114
  function renderRuntimeAwareAssertionBlock(input) {
115
115
  const exploration = input.workflow.resolvedRuntime?.exploration;
116
116
  if (!exploration) {
117
- return `
118
- await expect(${input.repoVariable}).toBeVisible();
119
- await ${input.repoVariable}.click();
120
-
117
+ return `
118
+ await expect(${input.repoVariable}).toBeVisible();
119
+ await ${input.repoVariable}.click();
120
+
121
121
  await expect(page.getByText(${singleQuoted(input.assertion)})).toBeVisible({ timeout: 30000 });`;
122
122
  }
123
123
  const confirmVariable = confirmButtonVariableName(input.label);
@@ -125,33 +125,33 @@ function renderRuntimeAwareAssertionBlock(input) {
125
125
  const actualVariable = actualFeedbackVariableName(input.label);
126
126
  const runtimeObserved = exploration.observedMessage?.replace(/\s+/g, ' ').trim() ?? '';
127
127
  const confirmBlock = exploration.confirmDialogPresent
128
- ? `
129
-
130
- const ${confirmVariable} = ${input.locatorOwner}
131
- .locator("xpath=//*[contains(@class,'mini-messagebox')]//a[normalize-space()='确定' or normalize-space()='确认']")
132
- .first();
133
- await expect(${confirmVariable}).toBeVisible({ timeout: 15000 });
128
+ ? `
129
+
130
+ const ${confirmVariable} = ${input.locatorOwner}
131
+ .locator("xpath=//*[contains(@class,'mini-messagebox')]//a[normalize-space()='确定' or normalize-space()='确认']")
132
+ .first();
133
+ await expect(${confirmVariable}).toBeVisible({ timeout: 15000 });
134
134
  await ${confirmVariable}.click();`
135
135
  : '';
136
- return `
137
- await expect(${input.repoVariable}).toBeVisible();
138
- await ${input.repoVariable}.click();${confirmBlock}
139
-
140
- const runtimeObserved反馈文案 = ${singleQuoted(runtimeObserved)};
141
- const ${feedbackVariable} = ${input.locatorOwner}.locator("xpath=//*[contains(@class,'mini-messagebox') or contains(@class,'mini-tips')]");
142
- await page.waitForTimeout(1000);
143
- const 反馈文本列表 = (await ${feedbackVariable}.allInnerTexts())
144
- .map((text) => text.replace(/\\s+/g, ' ').trim())
145
- .filter(Boolean);
146
- const ${actualVariable} =
147
- 反馈文本列表.find((text) => runtimeObserved反馈文案 && text.includes(runtimeObserved反馈文案)) ??
148
- 反馈文本列表.find((text) => text.includes(${singleQuoted(input.assertion)})) ??
149
- runtimeObserved反馈文案 ??
150
- 反馈文本列表.sort((a, b) => b.length - a.length)[0] ??
151
- '';
152
- expect(
153
- ${actualVariable}.includes(${singleQuoted(input.assertion)}),
154
- ${singleQuoted(`${input.label}未达成业务预期:期望出现「${input.assertion}」,系统实际反馈「`)} + ${actualVariable} + '」',
136
+ return `
137
+ await expect(${input.repoVariable}).toBeVisible();
138
+ await ${input.repoVariable}.click();${confirmBlock}
139
+
140
+ const runtimeObserved反馈文案 = ${singleQuoted(runtimeObserved)};
141
+ const ${feedbackVariable} = ${input.locatorOwner}.locator("xpath=//*[contains(@class,'mini-messagebox') or contains(@class,'mini-tips')]");
142
+ await page.waitForTimeout(1000);
143
+ const 反馈文本列表 = (await ${feedbackVariable}.allInnerTexts())
144
+ .map((text) => text.replace(/\\s+/g, ' ').trim())
145
+ .filter(Boolean);
146
+ const ${actualVariable} =
147
+ 反馈文本列表.find((text) => runtimeObserved反馈文案 && text.includes(runtimeObserved反馈文案)) ??
148
+ 反馈文本列表.find((text) => text.includes(${singleQuoted(input.assertion)})) ??
149
+ runtimeObserved反馈文案 ??
150
+ 反馈文本列表.sort((a, b) => b.length - a.length)[0] ??
151
+ '';
152
+ expect(
153
+ ${actualVariable}.includes(${singleQuoted(input.assertion)}),
154
+ ${singleQuoted(`${input.label}未达成业务预期:期望出现「${input.assertion}」,系统实际反馈「`)} + ${actualVariable} + '」',
155
155
  ).toBe(true);`;
156
156
  }
157
157
  export function renderAgentWorkflowTest(options) {
@@ -176,40 +176,40 @@ export function renderAgentWorkflowTest(options) {
176
176
  workflow,
177
177
  });
178
178
  const iframeSetup = iframeSrcKeyword
179
- ? `
180
- const ${frameVariable} = page.locator(${singleQuoted(`iframe[src*="${iframeSrcKeyword}"]`)}).contentFrame();
179
+ ? `
180
+ const ${frameVariable} = page.locator(${singleQuoted(`iframe[src*="${iframeSrcKeyword}"]`)}).contentFrame();
181
181
  `
182
182
  : '';
183
- return `
184
- test(${singleQuoted(title)}, async ({ page }) => {
185
- test.slow();
186
-
187
- await runRecordedWorkflow(
188
- recorder,
189
- {
190
- moduleId: ${singleQuoted(options.moduleId)},
191
- workflowId: ${singleQuoted(workflow.workflowId)},
192
- workflowKind: 'custom',
193
- workflowName: ${singleQuoted(label)},
194
- executionSource: 'agent_inferred',
195
- businessIntent: ${singleQuoted(intent)},
196
- elementActions: ${arrayLiteral(actions)},
197
- assertionExpectation: ${singleQuoted(assertion)},
198
- evidenceSources: ${arrayLiteral(evidenceSources(workflow))},
199
- evidenceRefs: ${renderEvidenceRefs(workflow)},
200
- reviewReason: ${singleQuoted(workflow.reviewReason ?? '该工作流由 gap-planner 识别为自定义业务动作。')},
201
- playwrightSuiteTitle: ${singleQuoted(options.suiteTitle)},
202
- playwrightTestTitle: ${singleQuoted(title)},
203
- ${gapFindingBlock}
204
- },
205
- async () => {
206
- await openModule(page);
207
- ${iframeSetup}
208
- const ${repoVariable} = ${locatorOwner}.locator(${locatorLiteral(locator)});
209
- ${assertionBlock}
210
- },
211
- );
212
- });
183
+ return `
184
+ test(${singleQuoted(title)}, async ({ page }) => {
185
+ test.slow();
186
+
187
+ await runRecordedWorkflow(
188
+ recorder,
189
+ {
190
+ moduleId: ${singleQuoted(options.moduleId)},
191
+ workflowId: ${singleQuoted(workflow.workflowId)},
192
+ workflowKind: 'custom',
193
+ workflowName: ${singleQuoted(label)},
194
+ executionSource: 'agent_inferred',
195
+ businessIntent: ${singleQuoted(intent)},
196
+ elementActions: ${arrayLiteral(actions)},
197
+ assertionExpectation: ${singleQuoted(assertion)},
198
+ evidenceSources: ${arrayLiteral(evidenceSources(workflow))},
199
+ evidenceRefs: ${renderEvidenceRefs(workflow)},
200
+ reviewReason: ${singleQuoted(workflow.reviewReason ?? '该工作流由 gap-planner 识别为自定义业务动作。')},
201
+ playwrightSuiteTitle: ${singleQuoted(options.suiteTitle)},
202
+ playwrightTestTitle: ${singleQuoted(title)},
203
+ ${gapFindingBlock}
204
+ },
205
+ async () => {
206
+ await openModule(page);
207
+ ${iframeSetup}
208
+ const ${repoVariable} = ${locatorOwner}.locator(${locatorLiteral(locator)});
209
+ ${assertionBlock}
210
+ },
211
+ );
212
+ });
213
213
  `;
214
214
  }
215
215
  export function appendAgentWorkflowTest(options) {