@epoint-testtech/ep-stage-skill 0.0.10 → 0.5.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/SKILL.md +27 -27
  2. package/codex-skill/ep-stage/glue-create-project/SKILL.md +223 -223
  3. package/codex-skill/ep-stage/glue-generate-testcase/SKILL.md +275 -275
  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 -95
  6. package/codex-skill/ep-stage/glue-run-test/SKILL.md +273 -296
  7. package/codex-skill/ep-stage/glue-run-test/references/ai-runtime-loop.md +30 -30
  8. package/codex-skill/ep-stage/glue-run-test/references/crud-pipeline.md +132 -132
  9. package/codex-skill/ep-stage/glue-run-test/references/gap-review-protocol.md +44 -44
  10. package/codex-skill/ep-stage/glue-run-test/references/harness-principles.md +46 -46
  11. package/codex-skill/ep-stage/glue-run-test/references/playwright-cli-odav.md +127 -166
  12. package/codex-skill/ep-stage/glue-run-test/scripts/generate-crud-spec.mjs +149 -149
  13. package/codex-skill/ep-stage/recording-to-glue/SKILL.md +28 -28
  14. package/codex-skill/ep-stage/scripts/validate-skill.mjs +95 -98
  15. package/dist/src/cli/generate-testcase.d.ts +0 -1
  16. package/dist/src/cli/generate-testcase.d.ts.map +1 -1
  17. package/dist/src/cli/generate-testcase.js +1 -6
  18. package/dist/src/cli/index.d.ts.map +1 -1
  19. package/dist/src/cli/index.js +14 -38
  20. package/dist/src/cli/run.d.ts +0 -2
  21. package/dist/src/cli/run.d.ts.map +1 -1
  22. package/dist/src/cli/run.js +21 -70
  23. package/dist/src/cli/spec.d.ts.map +1 -1
  24. package/dist/src/cli/spec.js +0 -9
  25. package/dist/src/contracts/glue-contract-v1.d.ts +0 -35
  26. package/dist/src/contracts/glue-contract-v1.d.ts.map +1 -1
  27. package/dist/src/extractors/code-list-json5.d.ts +1 -34
  28. package/dist/src/extractors/code-list-json5.d.ts.map +1 -1
  29. package/dist/src/extractors/code-list-json5.js +0 -1
  30. package/dist/src/extractors/html-page.d.ts +0 -9
  31. package/dist/src/extractors/html-page.d.ts.map +1 -1
  32. package/dist/src/extractors/html-page.js +2 -26
  33. package/dist/src/generators/agent-inferred-workflow-script.js +66 -66
  34. package/dist/src/generators/stage-skeleton-script.d.ts +0 -50
  35. package/dist/src/generators/stage-skeleton-script.d.ts.map +1 -1
  36. package/dist/src/generators/stage-skeleton-script.js +116 -449
  37. package/dist/src/runtime/ai-runtime-trace.d.ts.map +1 -1
  38. package/dist/src/runtime/ai-runtime-trace.js +0 -8
  39. package/dist/src/testcase/contract-testcase-assembler.d.ts +0 -28
  40. package/dist/src/testcase/contract-testcase-assembler.d.ts.map +1 -1
  41. package/dist/src/testcase/contract-testcase-assembler.js +75 -329
  42. package/dist/src/validation/contract.d.ts.map +1 -1
  43. package/dist/src/validation/contract.js +0 -29
  44. package/dist/test/cli/generate-testcase-json5.test.d.ts +2 -0
  45. package/dist/test/cli/generate-testcase-json5.test.d.ts.map +1 -0
  46. package/dist/test/cli/generate-testcase-json5.test.js +154 -0
  47. package/dist/test/cli/index.test.d.ts +2 -0
  48. package/dist/test/cli/index.test.d.ts.map +1 -0
  49. package/dist/test/cli/index.test.js +114 -0
  50. package/dist/test/cli/run.test.d.ts +2 -0
  51. package/dist/test/cli/run.test.d.ts.map +1 -0
  52. package/dist/test/cli/run.test.js +429 -0
  53. package/dist/test/cli/spec.test.d.ts +2 -0
  54. package/dist/test/cli/spec.test.d.ts.map +1 -0
  55. package/dist/test/cli/spec.test.js +112 -0
  56. package/dist/test/code-list-json5.test.d.ts +2 -0
  57. package/dist/test/code-list-json5.test.d.ts.map +1 -0
  58. package/dist/test/code-list-json5.test.js +84 -0
  59. package/dist/test/code-list.test.d.ts +2 -0
  60. package/dist/test/code-list.test.d.ts.map +1 -0
  61. package/dist/test/code-list.test.js +39 -0
  62. package/dist/test/gap-inference.test.d.ts +2 -0
  63. package/dist/test/gap-inference.test.d.ts.map +1 -0
  64. package/dist/test/gap-inference.test.js +597 -0
  65. package/dist/test/generalization.test.d.ts +2 -0
  66. package/dist/test/generalization.test.d.ts.map +1 -0
  67. package/dist/test/generalization.test.js +73 -0
  68. package/dist/test/material-inventory.test.d.ts +2 -0
  69. package/dist/test/material-inventory.test.d.ts.map +1 -0
  70. package/dist/test/material-inventory.test.js +155 -0
  71. package/dist/test/observable-chain.test.d.ts +2 -0
  72. package/dist/test/observable-chain.test.d.ts.map +1 -0
  73. package/dist/test/observable-chain.test.js +123 -0
  74. package/dist/test/page-structure.test.d.ts +2 -0
  75. package/dist/test/page-structure.test.d.ts.map +1 -0
  76. package/dist/test/page-structure.test.js +45 -0
  77. package/dist/test/runtime/ai-runtime-trace.test.d.ts +2 -0
  78. package/dist/test/runtime/ai-runtime-trace.test.d.ts.map +1 -0
  79. package/dist/test/runtime/ai-runtime-trace.test.js +78 -0
  80. package/dist/test/scenario-inference.test.d.ts +2 -0
  81. package/dist/test/scenario-inference.test.d.ts.map +1 -0
  82. package/dist/test/scenario-inference.test.js +73 -0
  83. package/dist/test/stage-context.test.d.ts +2 -0
  84. package/dist/test/stage-context.test.d.ts.map +1 -0
  85. package/dist/test/stage-context.test.js +395 -0
  86. package/dist/test/testcase/testcase-markdown.test.d.ts +2 -0
  87. package/dist/test/testcase/testcase-markdown.test.d.ts.map +1 -0
  88. package/dist/test/testcase/testcase-markdown.test.js +48 -0
  89. package/dist/test/testcase-generator.test.d.ts +2 -0
  90. package/dist/test/testcase-generator.test.d.ts.map +1 -0
  91. package/dist/test/testcase-generator.test.js +276 -0
  92. package/dist/test/testcase-skeleton.test.d.ts +2 -0
  93. package/dist/test/testcase-skeleton.test.d.ts.map +1 -0
  94. package/dist/test/testcase-skeleton.test.js +185 -0
  95. package/dist/test/testcase-spec-assembly.test.d.ts +2 -0
  96. package/dist/test/testcase-spec-assembly.test.d.ts.map +1 -0
  97. package/dist/test/testcase-spec-assembly.test.js +105 -0
  98. package/dist/test/util/credentials.test.d.ts +2 -0
  99. package/dist/test/util/credentials.test.d.ts.map +1 -0
  100. package/dist/test/util/credentials.test.js +64 -0
  101. package/dist/test/util/i18n-testcase.test.d.ts +2 -0
  102. package/dist/test/util/i18n-testcase.test.d.ts.map +1 -0
  103. package/dist/test/util/i18n-testcase.test.js +119 -0
  104. package/dist/test/util/softlink.test.d.ts +2 -0
  105. package/dist/test/util/softlink.test.d.ts.map +1 -0
  106. package/dist/test/util/softlink.test.js +82 -0
  107. package/dist/test/validation/contract.test.d.ts +2 -0
  108. package/dist/test/validation/contract.test.d.ts.map +1 -0
  109. package/dist/test/validation/contract.test.js +116 -0
  110. package/dist/test/validation/credentials.test.d.ts +2 -0
  111. package/dist/test/validation/credentials.test.d.ts.map +1 -0
  112. package/dist/test/validation/credentials.test.js +74 -0
  113. package/dist/test/validation/projects-index.test.d.ts +2 -0
  114. package/dist/test/validation/projects-index.test.d.ts.map +1 -0
  115. package/dist/test/validation/projects-index.test.js +48 -0
  116. package/dist/test/validation/testcase-v1-draft.test.d.ts +2 -0
  117. package/dist/test/validation/testcase-v1-draft.test.d.ts.map +1 -0
  118. package/dist/test/validation/testcase-v1-draft.test.js +173 -0
  119. package/dist/vitest.config.d.ts +3 -0
  120. package/dist/vitest.config.d.ts.map +1 -0
  121. package/dist/vitest.config.js +7 -0
  122. package/docs/README.md +124 -124
  123. package/examples/schemeresource-observable-docs/schemeresource.context.md +20 -20
  124. package/examples/schemeresource.module-hints.json +38 -38
  125. package/examples/schemeresource.observable.code_list.md +37 -37
  126. package/examples/zwplace-observable-docs/zwplace.context.md +16 -16
  127. package/examples/zwplace-placecategory-validation.json +29 -29
  128. package/examples/zwplace.module-hints.json +69 -69
  129. package/examples/zwplace.observable.code_list.md +37 -37
  130. package/package.json +19 -13
  131. package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-tpl.md +0 -72
  132. package/codex-skill/ep-stage/glue-run-test/scripts/extract-page-elements-eval.mjs +0 -233
  133. package/codex-skill/ep-stage/page-element-scan/SKILL.md +0 -91
  134. package/codex-skill/ep-stage/page-element-scan/evals/evals.json +0 -37
  135. package/codex-skill/ep-stage/page-element-scan/evals/fixtures/sample-list-page.html +0 -85
  136. package/codex-skill/ep-stage/page-element-scan/references/pagination-controls.md +0 -153
  137. package/codex-skill/ep-stage/page-element-scan/scripts/extract-page-elements-eval.mjs +0 -233
  138. package/codex-skill/ep-stage/page-element-scan/scripts/extract-page-elements.mjs +0 -298
  139. package/codex-skill/ep-stage/page-element-scan/scripts/scan-page.mjs +0 -749
  140. package/dist/src/cli/probe.d.ts +0 -46
  141. package/dist/src/cli/probe.d.ts.map +0 -1
  142. package/dist/src/cli/probe.js +0 -447
  143. package/dist/src/cli/run-report.d.ts +0 -46
  144. package/dist/src/cli/run-report.d.ts.map +0 -1
  145. package/dist/src/cli/run-report.js +0 -331
  146. package/dist/src/extractors/prd-extractor.d.ts +0 -70
  147. package/dist/src/extractors/prd-extractor.d.ts.map +0 -1
  148. package/dist/src/extractors/prd-extractor.js +0 -248
  149. package/dist/src/testcase/button-interaction-inference.d.ts +0 -37
  150. package/dist/src/testcase/button-interaction-inference.d.ts.map +0 -1
  151. package/dist/src/testcase/button-interaction-inference.js +0 -385
  152. package/dist/src/testcase/prd-button-mapper.d.ts +0 -72
  153. package/dist/src/testcase/prd-button-mapper.d.ts.map +0 -1
  154. package/dist/src/testcase/prd-button-mapper.js +0 -148
  155. package/dist/src/testcase/testcase-v2.d.ts +0 -50
  156. package/dist/src/testcase/testcase-v2.d.ts.map +0 -1
  157. package/dist/src/testcase/testcase-v2.js +0 -1
  158. package/docs/mvp-usage-guide.md +0 -298
@@ -1,298 +0,0 @@
1
- /**
2
- * extract-page-elements.mjs
3
- *
4
- * 可在浏览器上下文直接运行的 DOM 扫描内核(无外部依赖)。
5
- * 用途:
6
- * 1. 被 scan-page.mjs import 复用扫描逻辑
7
- * 2. 被 playwright-cli eval "<call>" 注入到已有 session 中实时采集 element map
8
- *
9
- * playwright-cli eval 使用示例(主 frame):
10
- * playwright-cli -s=$SESSION eval "< $(cat extract-page-elements.mjs) ; print(JSON.stringify(extractPageElements()))"
11
- *
12
- * playwright-cli eval 使用示例(遍历所有 frame):
13
- * playwright-cli -s=$SESSION eval "< $(cat extract-page-elements.mjs) ; print(JSON.stringify(extractAllFramesElementMap()))"
14
- *
15
- * 导出函数签名:
16
- * extractPageElements() -> { title, elements[] } 当前 frame 扫描
17
- * extractAllFramesElementMap() -> { [label]: { locator, controlType, visible } } 所有 frame 聚合
18
- */
19
-
20
- /**
21
- * 单 frame 扫描内核(与 scan-page.mjs 内部逻辑保持同步)。
22
- */
23
- function extractPageElements() {
24
- const norm = (s) => (s || '').replace(/\s+/g, ' ').trim();
25
-
26
- function isVisible(el) {
27
- const style = window.getComputedStyle(el);
28
- if (style.display === 'none' || style.visibility === 'hidden') return false;
29
- const rect = el.getBoundingClientRect();
30
- return rect.width > 0 && rect.height > 0;
31
- }
32
-
33
- function isEnabled(el) {
34
- const cls = el.className || '';
35
- return !el.disabled
36
- && el.getAttribute('aria-disabled') !== 'true'
37
- && !String(cls).includes('mini-disabled')
38
- && !String(cls).includes('disabled');
39
- }
40
-
41
- function fieldLabel(el) {
42
- // 分页控件(页码输入框、页面尺寸下拉框):不在表单字段采集范围内
43
- if (el.classList.contains('pagination-number')) return '';
44
- if (el.closest('.mini-pagination, .pagination-pagerchange')) return '';
45
- const wrapper = el.closest('[label]');
46
- if (wrapper) return norm(wrapper.getAttribute('label'));
47
- if (el.getAttribute('aria-label')) return norm(el.getAttribute('aria-label'));
48
- if (el.id) {
49
- const lab = document.querySelector(`label[for="${el.id}"]`);
50
- if (lab) return norm(lab.innerText);
51
- }
52
- const containerLabel = el.closest('div, td')?.querySelector('label');
53
- if (containerLabel) return norm(containerLabel.innerText).replace(/[::]$/, '');
54
- const prev = el.closest('td, div')?.previousElementSibling;
55
- if (prev && /label|td/.test(prev.tagName.toLowerCase())) return norm(prev.innerText).replace(/[::]$/, '');
56
- return norm(el.placeholder || el.getAttribute('bind') || el.name || el.id || '').replace(/^[  ]+|[  ]+$/g, '') || '';
57
- }
58
-
59
- function controlType(el) {
60
- const cls = String(el.className || '');
61
- const mini = cls.match(/mini-[a-z]+/);
62
- if (mini) return mini[0];
63
- if (el.tagName === 'SELECT') return 'html-select';
64
- if (el.tagName === 'TEXTAREA') return 'html-textarea';
65
- return `html-${el.type || 'input'}`;
66
- }
67
-
68
- function hiddenInputLocator(el, label) {
69
- const itemDiv = el.closest('.mini-radiobuttonlist-item, .mini-checkbox');
70
- if (itemDiv && itemDiv.id) return `#${CSS.escape(itemDiv.id)}`;
71
- if (label) {
72
- const type = el.type === 'radio' ? 'mini-radiobuttonlist-item' : 'mini-checkbox';
73
- return `xpath=//div[contains(@class,'${type}')][normalize-space()='${label}']`;
74
- }
75
- return null;
76
- }
77
-
78
- function fieldLocator(el, label) {
79
- const type = controlType(el);
80
- const tag = el.tagName.toLowerCase();
81
- if (label && type.startsWith('mini-')) {
82
- const exact = `xpath=//div[@label='${label}']//${tag}[contains(@class,'${type}') or contains(@class,'${type}-input')]`;
83
- if (document.querySelector(`div[label='${label}']`)) return exact;
84
- const bareLabel = label.replace(/\d+/g, '').replace(/\s+/g, ' ').trim();
85
- return `xpath=//div[@label and contains(@label,'${bareLabel.replace(/[^一-龥a-zA-Z]/g, '')}')]//${tag}[contains(@class,'${type}') or contains(@class,'${type}-input')]`;
86
- }
87
- if (el.id) {
88
- if (el.id.includes('$')) {
89
- const prev = hiddenInputLocator(el, label);
90
- if (prev) return prev;
91
- }
92
- return `#${CSS.escape(el.id)}`;
93
- }
94
- if (el.name) return `${tag}[name="${el.name}"]`;
95
- if (label) return `xpath=//div[@label='${label}']//${tag}`;
96
- return tag;
97
- }
98
-
99
- function buttonLocator(el, label) {
100
- const tag = el.tagName.toLowerCase();
101
- const cls = String(el.className || '');
102
- if (cls.includes('mini-button')) return `xpath=//${tag}[contains(@class,'mini-button')][normalize-space()='${label}']`;
103
- if (cls.includes('cond-srh-btn')) return `xpath=//a[contains(@class,'cond-srh-btn')][normalize-space()='${label}']`;
104
- if (el.id) return `#${el.id}`;
105
- return `role=button[name="${label}"]`;
106
- }
107
-
108
- const elements = [];
109
- const seen = new Set();
110
- function push(el) {
111
- const key = `${el.type}|${el.label}|${el.locator}`;
112
- if (seen.has(key)) return;
113
- seen.add(key);
114
- elements.push(el);
115
- }
116
-
117
- // 按钮
118
- document.querySelectorAll(
119
- 'a.mini-button, button, input[type=button], input[type=submit], input[type=reset], [role=button], a.cond-srh-btn, a.mini-tools, span.mini-tools',
120
- ).forEach((el) => {
121
- const label = norm(el.innerText || el.value || el.getAttribute('data-tooltip') || el.title || '');
122
- if (!label) return;
123
- push({ type: 'button', label, locator: buttonLocator(el, label), visible: isVisible(el), enabled: isEnabled(el) });
124
- });
125
-
126
- // 表单字段
127
- document.querySelectorAll(
128
- 'input:not([type=button]):not([type=submit]):not([type=reset]):not([type=hidden]), textarea, select',
129
- ).forEach((el) => {
130
- if (!isVisible(el) && el.closest('.mini-textbox, .mini-combobox, .mini-datepicker, .mini-spinner')) return;
131
- if ((el.type === 'radio' || el.type === 'checkbox') && !isVisible(el)) return;
132
- const label = fieldLabel(el);
133
- if (!label) return;
134
- push({
135
- type: 'field', label, controlType: controlType(el),
136
- locator: fieldLocator(el, label),
137
- visible: isVisible(el), enabled: isEnabled(el),
138
- required: el.required || el.getAttribute('required') === 'true' || !!el.closest('.starred, [starred]'),
139
- });
140
- });
141
-
142
- // MiniUI RadioButtonList / CheckBox 独立控件(父 div 可点击)
143
- document.querySelectorAll('.mini-radiobuttonlist-item, .mini-checkbox').forEach((el) => {
144
- const input = el.querySelector('input[type=radio], input[type=checkbox]');
145
- if (!input) return;
146
- let label = norm(input.labels?.[0]?.innerText || el.innerText || '').replace(/[::]$/, '');
147
- if (!label) return;
148
- const numInput = el.querySelector('.mini-export-num');
149
- if (numInput) label = label.replace(/\d+/g, '');
150
- const isRadio = input.type === 'radio';
151
- const itemId = el.id;
152
- const locator = itemId ? `#${CSS.escape(itemId)}`
153
- : `xpath=//div[contains(@class,'${isRadio ? 'mini-radiobuttonlist-item' : 'mini-checkbox'})][contains(normalize-space(),'${label}')]`;
154
- push({ type: 'field', label, controlType: isRadio ? 'mini-radiobuttonlist' : 'mini-checkbox', locator, visible: isVisible(el), enabled: isEnabled(el) });
155
- });
156
-
157
- // 列表
158
- document.querySelectorAll('.mini-datagrid').forEach((grid) => {
159
- const gridId = grid.id || 'datagrid';
160
- const headers = [...grid.querySelectorAll('.mini-grid-headertxt, .mini-grid-headerText, th')].map((h) => norm(h.innerText)).filter(Boolean);
161
- const rowActions = [...grid.querySelectorAll('[data-tooltip]')].map((a) => norm(a.getAttribute('data-tooltip'))).filter(Boolean);
162
- push({ type: 'grid', label: gridId, locator: `#${gridId}`, visible: isVisible(grid), enabled: true, columns: headers, rowActions: [...new Set(rowActions)] });
163
- });
164
-
165
- // 标签页
166
- document.querySelectorAll('.mini-tabs li, [role=tab]').forEach((el) => {
167
- const label = norm(el.innerText);
168
- if (!label) return;
169
- push({ type: 'tab', label, locator: `role=tab[name="${label}"]`, visible: isVisible(el), enabled: isEnabled(el) });
170
- });
171
-
172
- // 普通链接
173
- document.querySelectorAll('a[href]:not(.mini-button)').forEach((el) => {
174
- const label = norm(el.innerText);
175
- if (!label || !isVisible(el)) return;
176
- push({ type: 'link', label, locator: `role=link[name="${label}"]`, visible: true, enabled: isEnabled(el), href: el.getAttribute('href') });
177
- });
178
-
179
- // MiniUI 分页控件
180
- document.querySelectorAll('.mini-grid-pager').forEach((pager) => {
181
- // 确定所属 grid 的 id
182
- const parentGrid = pager.closest('.mini-datagrid');
183
- const gridId = parentGrid?.id || 'datagrid';
184
- const prefix = parentGrid ? `#${CSS.escape(gridId)}` : '';
185
-
186
- // 总条数文本
187
- const pagerInfo = pager.querySelector('.pagination-pagerinfo');
188
- if (pagerInfo) {
189
- const label = norm(pagerInfo.innerText); // 如 "共536条"
190
- push({ type: 'pagination', label, locator: `${prefix} .pagination-pagerinfo`, visible: true, enabled: true });
191
- }
192
-
193
- // 分页按钮组
194
- const btns = pager.querySelectorAll('.pagination-buttons a');
195
- btns.forEach((btn) => {
196
- const action = btn.getAttribute('action');
197
- const isEllipsis = btn.classList.contains('pagination-button-ellipsis');
198
- let label = norm(btn.innerText) || '';
199
- let subType = '';
200
- let locatorSuffix = '';
201
-
202
- if (action === 'prev') {
203
- subType = 'pagination-prev';
204
- label = '上一页';
205
- locatorSuffix = `.pagination-buttons a[action=prev]`;
206
- } else if (action === 'next') {
207
- subType = 'pagination-next';
208
- label = '下一页';
209
- locatorSuffix = `.pagination-buttons a[action=next]`;
210
- } else if (action === 'none' || isEllipsis) {
211
- subType = 'pagination-ellipsis';
212
- label = '...';
213
- locatorSuffix = `.pagination-button-ellipsis`;
214
- } else {
215
- // 页码数字按钮
216
- subType = 'pagination-page';
217
- label = norm(btn.innerText); // "1", "2", "49"
218
- locatorSuffix = `.pagination-buttons a.pagination-button:not(.pagination-button-ellipsis)`;
219
- }
220
- push({
221
- type: 'pagination',
222
- subType,
223
- label,
224
- locator: `${prefix} ${locatorSuffix}`,
225
- visible: isVisible(btn),
226
- enabled: !btn.classList.contains('disabled'),
227
- });
228
- });
229
-
230
- // 页面尺寸下拉(只采集可见的)
231
- const pageSizeBtn = pager.querySelector('.mini-pagination > span.mini-buttonedit');
232
- if (pageSizeBtn && isVisible(pageSizeBtn)) {
233
- const hiddenVal = pageSizeBtn.querySelector('input[type=hidden]');
234
- push({
235
- type: 'pagination',
236
- subType: 'pagination-size',
237
- label: '页面尺寸',
238
- locator: `${prefix} .mini-pagination > span.mini-buttonedit`,
239
- value: hiddenVal?.value || '',
240
- visible: true,
241
- enabled: true,
242
- });
243
- }
244
-
245
- // 页码跳转输入框
246
- const jumpInput = pager.querySelector('.pagination-pagerchange input.pagination-number');
247
- if (jumpInput && isVisible(jumpInput)) {
248
- push({
249
- type: 'pagination',
250
- subType: 'pagination-jump',
251
- label: '跳至页',
252
- locator: `${prefix} .pagination-pagerchange input.pagination-number`,
253
- visible: true,
254
- enabled: isEnabled(jumpInput),
255
- });
256
- }
257
- });
258
-
259
- return { title: document.title, elements };
260
- }
261
-
262
- /**
263
- * 遍历所有 frame(含子 iframe),对每个 frame 执行 extractPageElements(),
264
- * 聚合所有 elements,去重后以 { label → { locator, controlType, visible } } 返回。
265
- * 专供 playwright-cli eval 在 O-D-A-V Act 之后使用:捕获弹窗/子表等动态新增元素。
266
- */
267
- function extractAllFramesElementMap() {
268
- const result = {};
269
- // 主 frame
270
- try {
271
- const main = extractPageElements();
272
- for (const el of main.elements) {
273
- if (!result[el.label] || el.visible) result[el.label] = { locator: el.locator, controlType: el.controlType, visible: el.visible };
274
- }
275
- } catch (_) {}
276
-
277
- // 子 frame(通过 window.frames 枚举)
278
- try {
279
- for (let i = 0; i < window.frames.length; i++) {
280
- const fr = window.frames[i];
281
- try {
282
- const r = fr.extractPageElements ? fr.extractPageElements() : null;
283
- if (!r) continue;
284
- for (const el of r.elements) {
285
- if (!result[el.label] || el.visible) result[el.label] = { locator: el.locator, controlType: el.controlType, visible: el.visible };
286
- }
287
- } catch (_) { /* 跨域 frame 跳过 */ }
288
- }
289
- } catch (_) {}
290
-
291
- return result;
292
- }
293
-
294
- // Node.js 环境:CommonJS 导出(被 scan-page.mjs import)
295
- // 浏览器 / playwright-cli eval 环境:全局函数直接可用
296
- if (typeof module !== 'undefined' && module.exports) {
297
- module.exports = { extractPageElements, extractAllFramesElementMap };
298
- }