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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 -316
  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
@@ -435,420 +435,6 @@ ${renderOptionalStringProperty('deleteConfirmButton', slots.locators.deleteConfi
435
435
  ${renderOptionalStringProperty('blockedDeleteMessage', slots.assertions.blockedDeleteMessage, 12)} },
436
436
  }`;
437
437
  }
438
- function renderChildGridColumns(columns, level) {
439
- const indent = ' '.repeat(level);
440
- const innerIndent = ' '.repeat(level + 4);
441
- const items = columns.map((col) => {
442
- const parts = [
443
- `field: ${singleQuoted(col.field)}`,
444
- `label: ${singleQuoted(col.label)}`,
445
- `component: ${singleQuoted(col.component)}`,
446
- `required: ${String(col.required)}`,
447
- ];
448
- if (col.modelRef !== undefined) {
449
- parts.push(`modelRef: ${singleQuoted(col.modelRef)}`);
450
- }
451
- return `${innerIndent}{ ${parts.join(', ')} }`;
452
- }).join(',\n');
453
- return `${indent}[\n${items},\n${indent}]`;
454
- }
455
- function renderChildGridLocators(locators, level) {
456
- const indent = ' '.repeat(level);
457
- const innerIndent = ' '.repeat(level + 4);
458
- const fields = [
459
- ['container', locators.container],
460
- ['addRowButton', locators.addRowButton],
461
- ['deleteSelectedButton', locators.deleteSelectedButton],
462
- ['grid', locators.grid],
463
- ['editWrapInput', locators.editWrapInput],
464
- ['editWrapListboxTrigger', locators.editWrapListboxTrigger],
465
- ['editWrapListboxInput', locators.editWrapListboxInput],
466
- ['rowCheckboxByIndex', locators.rowCheckboxByIndex],
467
- ['cellByIndexAndField', locators.cellByIndexAndField],
468
- ['cellValueByIndexAndField', locators.cellValueByIndexAndField],
469
- ];
470
- const lines = fields.map(([key, value]) => `${innerIndent}${key}: ${locatorLiteral(value)},`).join('\n');
471
- return `${indent}{\n${lines}\n${indent}}`;
472
- }
473
- /**
474
- * 将子表槽位数组渲染为 TypeScript 对象字面量。
475
- *
476
- * @param slots - 子表槽位数组
477
- * @returns TypeScript 源码
478
- */
479
- export function renderChildGridSlots(slots) {
480
- if (slots.length === 0)
481
- return '[]';
482
- const items = slots.map((slot) => `{
483
- gridId: ${singleQuoted(slot.gridId)},
484
- label: ${singleQuoted(slot.label)},
485
- pageRole: ${singleQuoted(slot.pageRole)},
486
- editMode: ${singleQuoted(slot.editMode)},
487
- columns: ${renderChildGridColumns(slot.columns, 8).trimStart()},
488
- locators: ${renderChildGridLocators(slot.locators, 8).trimStart()},
489
- }`).join(',\n');
490
- return `[\n${items}\n]`;
491
- }
492
- /**
493
- * 渲染子表行内编辑的填写步骤。
494
- *
495
- * @param slot 子表槽位
496
- * @param valuePrefix 填充值前缀,例如 "测试" / "修改后"
497
- * @param indent 语句块缩进空格数,默认 10
498
- * @returns 可直接嵌入生成测试源码的语句块
499
- */
500
- function renderChildGridFillSteps(slot, valuePrefix, indent = 10) {
501
- const spaces = ' '.repeat(indent);
502
- return slot.columns.map((col) => {
503
- if (col.component === 'input') {
504
- return `${spaces}await childGridPage.editCell(1, ${singleQuoted(col.field)});
505
- ${spaces}await childGridPage.fillInputCell(${singleQuoted(`${valuePrefix}${col.label}`)});`;
506
- }
507
- return `${spaces}await childGridPage.editCell(1, ${singleQuoted(col.field)});
508
- ${spaces}await childGridPage.selectListboxCell(childGridSlots[0].columns.find((c) => c.field === ${singleQuoted(col.field)})!);`;
509
- }).join('\n');
510
- }
511
- /**
512
- * 渲染子表持久化后的单元格断言步骤。
513
- *
514
- * @param slot 子表槽位
515
- * @param valuePrefix 填充值前缀,用于 input 类型精确匹配
516
- * @returns 可直接嵌入生成测试源码的语句块(已含 8 空格缩进)
517
- */
518
- function renderChildGridVerifyAssertions(slot, valuePrefix) {
519
- return slot.columns
520
- .map((col) => {
521
- if (col.component === 'input') {
522
- return ` await verifyChildGridPage.assertCellValue(1, ${singleQuoted(col.field)}, ${singleQuoted(`${valuePrefix}${col.label}`)});`;
523
- }
524
- return ` await verifyChildGridPage.assertCellNotEmpty(1, ${singleQuoted(col.field)});`;
525
- })
526
- .join('\n');
527
- }
528
- /**
529
- * 渲染单个子表的新增行测试。
530
- */
531
- function renderChildGridCreateTest(slot, crudSlots, moduleLabel, moduleId, caseRef) {
532
- const label = slot.label;
533
- const fillSteps = renderChildGridFillSteps(slot, '测试');
534
- const verifyAssertions = renderChildGridVerifyAssertions(slot, '测试');
535
- return ` test(${singleQuoted(caseRef ? `${caseRef.caseId} ${caseRef.title}` : `${label}-新增行并随主表保存`)}, async ({ page }) => {
536
- test.slow();
537
-
538
- await runRecordedWorkflow(
539
- recorder,
540
- workflowMeta({
541
- workflowId: ${singleQuoted(`${moduleId}.childGrid.${slot.gridId}.create`)},
542
- workflowKind: 'create',
543
- workflowName: ${singleQuoted(caseRef?.title ?? `${label}-新增行`)},
544
- businessIntent: ${singleQuoted(`在新增${moduleLabel}时维护${label}子表,验证子表数据随主表保存并持久化。`)},
545
- elementActions: ['进入新增页', '点击子表新增行', '填写子表单元格', '保存主表', '返回列表搜索并校验子表数据'],
546
- assertionExpectation: ${singleQuoted(`保存后重新打开编辑页,${label}子表应显示新增的行数据。`)},
547
- playwrightTestTitle: ${singleQuoted(caseRef ? `${caseRef.caseId} ${caseRef.title}` : `${label}-新增行并随主表保存`)},
548
- }),
549
- async () => {
550
- await openModule(page);
551
- const crudPage = new CrudPage(page, crudSlots);
552
- const childWorkflowData = crudPage.prepareWorkflowData();
553
- await crudPage.runCreateWorkflowWithChildGrid(childWorkflowData, async (addContext) => {
554
- const childGridPage = new ChildGridPage(page, addContext, childGridSlots[0]);
555
- await childGridPage.addRow();
556
- ${fillSteps}
557
- });
558
-
559
- // 验证子表数据持久化:搜索主记录并重新打开编辑页
560
- const editActionLocator = ${locatorLiteral(crudSlots.locators.listEditActionLocatorTemplate)}.replace('{{value}}', childWorkflowData.searchValue);
561
- await page.locator(editActionLocator).click();
562
- const editFrame = page.locator(\`iframe[src*="${crudSlots.frames.editIframeSrcKeyword}"]\`).contentFrame();
563
- const verifyChildGridPage = new ChildGridPage(page, editFrame, childGridSlots[0]);
564
- ${verifyAssertions}
565
- },
566
- );
567
- });`;
568
- }
569
- /**
570
- * 渲染单个子表的编辑行测试。
571
- */
572
- function renderChildGridUpdateTest(slot, crudSlots, moduleLabel, moduleId, caseRef) {
573
- const label = slot.label;
574
- const createFillSteps = renderChildGridFillSteps(slot, '测试');
575
- const verifyAssertions = renderChildGridVerifyAssertions(slot, '修改后');
576
- return ` test(${singleQuoted(caseRef ? `${caseRef.caseId} ${caseRef.title}` : `${label}-编辑行并随主表保存`)}, async ({ page }) => {
577
- test.slow();
578
-
579
- await runRecordedWorkflow(
580
- recorder,
581
- workflowMeta({
582
- workflowId: ${singleQuoted(`${moduleId}.childGrid.${slot.gridId}.update`)},
583
- workflowKind: 'update',
584
- workflowName: ${singleQuoted(caseRef?.title ?? `${label}-编辑行`)},
585
- businessIntent: ${singleQuoted(`在修改${moduleLabel}时维护${label}子表,验证子表数据随主表保存并持久化。`)},
586
- elementActions: ['进入列表页', '创建带窗口信息子表的主记录', '打开编辑页', '修改子表单元格', '保存主表', '重新搜索并校验子表数据'],
587
- assertionExpectation: ${singleQuoted(`保存后重新打开编辑页,${label}子表应显示更新后的行数据。`)},
588
- playwrightTestTitle: ${singleQuoted(caseRef ? `${caseRef.caseId} ${caseRef.title}` : `${label}-编辑行并随主表保存`)},
589
- }),
590
- async () => {
591
- await openModule(page);
592
- const crudPage = new CrudPage(page, crudSlots);
593
- const childWorkflowData = crudPage.prepareWorkflowData();
594
-
595
- // 前置:创建带${label}子表的主记录
596
- await crudPage.runCreateWorkflowWithChildGrid(childWorkflowData, async (addContext) => {
597
- const childGridPage = new ChildGridPage(page, addContext, childGridSlots[0]);
598
- await childGridPage.addRow();
599
- ${createFillSteps}
600
- });
601
-
602
- // 打开编辑页并修改子表行
603
- const editActionLocator = ${locatorLiteral(crudSlots.locators.listEditActionLocatorTemplate)}.replace('{{value}}', childWorkflowData.searchValue);
604
- await page.locator(editActionLocator).click();
605
- const editFrame = page.locator(\`iframe[src*="${crudSlots.frames.editIframeSrcKeyword}"]\`).contentFrame();
606
- const childGridPage = new ChildGridPage(page, editFrame, childGridSlots[0]);
607
- ${renderChildGridFillSteps(slot, '修改后', 8)}
608
-
609
- // 保存主表
610
- await editFrame.locator(crudSlots.locators.editSaveButton).click();
611
-
612
- // 验证子表数据持久化:重新搜索主记录并打开编辑页
613
- const verifyEditActionLocator = ${locatorLiteral(crudSlots.locators.listEditActionLocatorTemplate)}.replace('{{value}}', childWorkflowData.searchValue);
614
- await page.locator(verifyEditActionLocator).click();
615
- const verifyEditFrame = page.locator(\`iframe[src*="${crudSlots.frames.editIframeSrcKeyword}"]\`).contentFrame();
616
- const verifyChildGridPage = new ChildGridPage(page, verifyEditFrame, childGridSlots[0]);
617
- ${verifyAssertions}
618
- },
619
- );
620
- });`;
621
- }
622
- /**
623
- * 渲染单个子表的删除行测试。
624
- *
625
- * 注意:本测试会断言删除成功后子表行被真正移除。若系统存在 confirm 确定后仍删除失败的 bug,
626
- * 该断言将失败,从而暴露问题,而不是为了绕过 bug 而降低断言标准。
627
- */
628
- function renderChildGridDeleteTest(slot, crudSlots, moduleLabel, moduleId, caseRef) {
629
- const label = slot.label;
630
- const createFillSteps = renderChildGridFillSteps(slot, '测试');
631
- return ` test(${singleQuoted(caseRef ? `${caseRef.caseId} ${caseRef.title}` : `${label}-删除行`)}, async ({ page }) => {
632
- test.slow();
633
-
634
- await runRecordedWorkflow(
635
- recorder,
636
- workflowMeta({
637
- workflowId: ${singleQuoted(`${moduleId}.childGrid.${slot.gridId}.delete`)},
638
- workflowKind: 'delete',
639
- workflowName: ${singleQuoted(caseRef?.title ?? `${label}-删除行`)},
640
- businessIntent: ${singleQuoted(`验证${moduleLabel}的${label}子表删除选中行后,数据应被真正移除。`)},
641
- elementActions: ['进入列表页', '创建带窗口信息子表的主记录', '打开编辑页', '勾选子表行', '点击删除选中', '确认删除', '校验子表行已移除'],
642
- assertionExpectation: ${singleQuoted(`确认删除后,${label}子表的数据行数应为 0,被删除的数据不再存在。`)},
643
- playwrightTestTitle: ${singleQuoted(caseRef ? `${caseRef.caseId} ${caseRef.title}` : `${label}-删除行`)},
644
- }),
645
- async () => {
646
- await openModule(page);
647
- const crudPage = new CrudPage(page, crudSlots);
648
- const childWorkflowData = crudPage.prepareWorkflowData();
649
-
650
- // 前置:创建带${label}子表的主记录
651
- await crudPage.runCreateWorkflowWithChildGrid(childWorkflowData, async (addContext) => {
652
- const childGridPage = new ChildGridPage(page, addContext, childGridSlots[0]);
653
- await childGridPage.addRow();
654
- ${createFillSteps}
655
- });
656
-
657
- // 打开编辑页
658
- const editActionLocator = ${locatorLiteral(crudSlots.locators.listEditActionLocatorTemplate)}.replace('{{value}}', childWorkflowData.searchValue);
659
- await page.locator(editActionLocator).click();
660
- const editFrame = page.locator(\`iframe[src*="${crudSlots.frames.editIframeSrcKeyword}"]\`).contentFrame();
661
-
662
- const childGridPage = new ChildGridPage(page, editFrame, childGridSlots[0]);
663
- const confirmPromise = childGridPage.acceptCustomConfirm('确定要删除');
664
- await childGridPage.selectRow(1);
665
- await childGridPage.deleteSelected();
666
-
667
- await confirmPromise;
668
-
669
- // 断言删除成功:子表数据行数应为 0
670
- await page.waitForTimeout(500);
671
- await childGridPage.assertRowCount(0);
672
- },
673
- );
674
- });`;
675
- }
676
- /**
677
- * 渲染单个子表的必填校验测试。
678
- */
679
- function renderChildGridRequiredTest(slot, moduleLabel, moduleId, caseRef) {
680
- const label = slot.label;
681
- const requiredFields = slot.columns.filter((c) => c.required);
682
- const requiredLabels = requiredFields.map((c) => c.label).join('、');
683
- return ` test(${singleQuoted(caseRef ? `${caseRef.caseId} ${caseRef.title}` : `${label}-必填校验`)}, async ({ page }) => {
684
- test.slow();
685
-
686
- await runRecordedWorkflow(
687
- recorder,
688
- workflowMeta({
689
- workflowId: ${singleQuoted(`${moduleId}.childGrid.${slot.gridId}.required`)},
690
- workflowKind: 'create',
691
- workflowName: ${singleQuoted(caseRef?.title ?? `${label}-必填校验`)},
692
- businessIntent: ${singleQuoted(`验证${label}子表必填字段未填写时,主表保存应被阻断并给出校验提示。`)},
693
- elementActions: ['进入新增页', '点击子表新增行', '不填必填字段', '保存主表', '观察校验提示'],
694
- assertionExpectation: ${singleQuoted(`系统应阻断保存并提示「${requiredLabels}不能为空」。`)},
695
- playwrightTestTitle: ${singleQuoted(caseRef ? `${caseRef.caseId} ${caseRef.title}` : `${label}-必填校验`)},
696
- }),
697
- async () => {
698
- await openModule(page);
699
- const crudPage = new CrudPage(page, crudSlots);
700
- const childWorkflowData = crudPage.prepareWorkflowData();
701
-
702
- let alertPromise: Promise<void> | undefined;
703
-
704
- await crudPage.runCreateWorkflowWithChildGridWithoutSearch(childWorkflowData, async (addContext) => {
705
- const childGridPage = new ChildGridPage(page, addContext, childGridSlots[0]);
706
- alertPromise = childGridPage.assertAlert('不能为空');
707
- await childGridPage.addRow();
708
- });
709
-
710
- if (!alertPromise) {
711
- throw new Error('alertPromise 未初始化');
712
- }
713
- await alertPromise;
714
- },
715
- );
716
- });`;
717
- }
718
- /**
719
- * 渲染所有子表测试块(新增行、编辑行、删除行、必填校验)。
720
- */
721
- function renderChildGridTests(slots, crudSlots, moduleLabel, moduleId, glueCases) {
722
- if (slots.length === 0)
723
- return '';
724
- return slots
725
- .flatMap((slot) => {
726
- if (slot.editMode === 'none') {
727
- return [];
728
- }
729
- if (slot.editMode === 'row_button_dialog') {
730
- return [` // TODO: ${slot.label} 子表为行按钮弹窗编辑模式,测试待实现`];
731
- }
732
- // 子表用例标题(如「窗口信息-新增行」)→ caseId 引用,供测试标题带用例ID。
733
- const caseRefOf = (suffix) => glueCases?.find((testCase) => testCase.title === `${slot.label}-${suffix}`);
734
- return [
735
- renderChildGridCreateTest(slot, crudSlots, moduleLabel, moduleId, caseRefOf('新增行')),
736
- renderChildGridUpdateTest(slot, crudSlots, moduleLabel, moduleId, caseRefOf('编辑行')),
737
- renderChildGridDeleteTest(slot, crudSlots, moduleLabel, moduleId, caseRefOf('删除行')),
738
- renderChildGridRequiredTest(slot, moduleLabel, moduleId, caseRefOf('必填校验')),
739
- ];
740
- })
741
- .join('\n\n');
742
- }
743
- /** 解析 glue CRUD 用例标题:`${moduleName} ${actionKind}(${queryKey})`。 */
744
- function parseCrudCaseTitle(title) {
745
- const match = title.match(/\s(create|read|update|delete)((.+))\s*$/);
746
- if (!match)
747
- return null;
748
- return {
749
- actionKind: match[1],
750
- queryKeyLabel: match[2],
751
- };
752
- }
753
- /** 把用例 steps 文本("1. …。 2. …。")拆成 elementActions 数组。 */
754
- function splitCaseSteps(steps, fallback) {
755
- if (!steps)
756
- return fallback;
757
- const parts = steps
758
- .split(/(?=\d+\.\s)/)
759
- .map((part) => part.replace(/^\d+\.\s*/, '').trim())
760
- .filter(Boolean);
761
- return parts.length > 0 ? parts : fallback;
762
- }
763
- /**
764
- * 按用例逐条渲染主表 CRUD 测试(一用例一脚本)。
765
- *
766
- * 串行 describe 内用 workflowDataByKey 按查询主键分组共享记录:
767
- * create 用例新建记录;同键 read/update/delete 复用;只读键(如系统管理的状态字段)
768
- * 挂靠最近一个 create 组的记录。用例顺序即装配器产出顺序。
769
- */
770
- function renderCrudCaseTests(cases, context) {
771
- const { moduleId, componentForLabel } = context;
772
- const createKeys = new Set(cases.filter((c) => c.actionKind === 'create').map((c) => c.queryKeyLabel));
773
- let lastCreateKey = '';
774
- const blocks = cases.map((testCase) => {
775
- const fullTitle = `${testCase.caseId} ${testCase.title}`;
776
- const key = testCase.queryKeyLabel;
777
- if (testCase.actionKind === 'create') {
778
- lastCreateKey = key;
779
- }
780
- const dataKey = createKeys.has(key) ? key : lastCreateKey;
781
- const workflowKind = testCase.actionKind;
782
- const elementActions = splitCaseSteps(testCase.steps, ['按用例步骤执行']).map((a) => singleQuoted(a)).join(', ');
783
- const businessIntent = testCase.steps ?? `按用例「${testCase.title}」步骤执行。`;
784
- const assertionExpectation = testCase.assertion ?? '按用例断言验证执行结果。';
785
- let body;
786
- switch (testCase.actionKind) {
787
- case 'create':
788
- body = ` await openModule(page);
789
- const crudPage = new CrudPage(page, crudSlots);
790
- const data = crudPage.prepareWorkflowData();
791
- workflowDataByKey[${singleQuoted(key)}] = data;
792
- await crudPage.runCreateWorkflow(data);`;
793
- break;
794
- case 'read':
795
- body = ` await openModule(page);
796
- const data = workflowDataByKey[${singleQuoted(dataKey)}];
797
- if (!data) {
798
- throw new Error(${singleQuoted(`${testCase.title} 依赖 create(${dataKey})用例先行生成 workflowData`)});
799
- }
800
- const crudPage = new CrudPage(page, crudSlots);
801
- await crudPage.runReadWorkflowByField(data, ${singleQuoted(key)});`;
802
- break;
803
- case 'update':
804
- body = ` await openModule(page);
805
- const data = workflowDataByKey[${singleQuoted(dataKey)}];
806
- if (!data) {
807
- throw new Error(${singleQuoted(`${testCase.title} 依赖 create(${dataKey})用例先行生成 workflowData`)});
808
- }
809
- const crudPage = new CrudPage(page, crudSlots);
810
- ${componentForLabel(key) === 'listbox'
811
- ? ` await crudPage.runUpdateListboxWorkflow(data, ${singleQuoted(key)});`
812
- : ` await crudPage.runUpdateWorkflow(data);`}`;
813
- break;
814
- case 'delete':
815
- body = ` await openModule(page);
816
- const data = workflowDataByKey[${singleQuoted(dataKey)}];
817
- if (!data) {
818
- throw new Error(${singleQuoted(`${testCase.title} 依赖 create(${dataKey})用例先行生成 workflowData`)});
819
- }
820
- const crudPage = new CrudPage(page, crudSlots);
821
- await crudPage.runDeleteWorkflow(data);`;
822
- break;
823
- }
824
- return ` test(${singleQuoted(fullTitle)}, async ({ page }) => {
825
- test.slow();
826
-
827
- await runRecordedWorkflow(
828
- recorder,
829
- workflowMeta({
830
- workflowId: ${singleQuoted(`${moduleId}.${testCase.caseId}`)},
831
- workflowKind: '${workflowKind}',
832
- workflowName: ${singleQuoted(testCase.title)},
833
- businessIntent: ${singleQuoted(businessIntent)},
834
- elementActions: [${elementActions}],
835
- assertionExpectation: ${singleQuoted(assertionExpectation)},
836
- playwrightTestTitle: ${singleQuoted(fullTitle)},
837
- }),
838
- async () => {
839
- ${body}
840
- },
841
- );
842
- });`;
843
- });
844
- return ` test.describe('主表 CRUD 流程', () => {
845
- test.describe.configure({ mode: 'serial' });
846
- const workflowDataByKey: Record<string, CrudWorkflowData | undefined> = {};
847
-
848
- ${blocks.join('\n\n')}
849
-
850
- });`;
851
- }
852
438
  /**
853
439
  * 渲染导出骨架槽位对象。
854
440
  *
@@ -913,36 +499,20 @@ function renderExportSlotsFromSlot(exportSlot, listIframeSrcKeyword) {
913
499
  * @returns 完整的 `.spec.ts` 源码
914
500
  */
915
501
  export function generateStageSkeletonCrudSpecFromSlots(options) {
916
- const { moduleId, moduleLabel, directUrl, primaryRole, requirementName, crudSlots, exportSlot, childGridSlots, glueCases } = options;
502
+ const { moduleId, moduleLabel, directUrl, primaryRole, requirementName, crudSlots, exportSlot } = options;
917
503
  const suiteTitle = `${moduleLabel} CRUD + Export`;
918
- const childGridImport = childGridSlots?.length
919
- ? ', ChildGridPage, type ChildGridFlowSlots'
920
- : '';
921
- // 一用例一脚本:按 glue 用例逐条渲染 CRUD 测试,保证报告结果数 == 用例数。
922
- const componentForLabel = (label) => crudSlots.searchConditions.find((c) => c.label === label)?.component ?? 'input';
923
- const crudCases = (glueCases ?? []).flatMap((testCase) => {
924
- const parsed = parseCrudCaseTitle(testCase.title);
925
- return parsed ? [{ ...testCase, ...parsed }] : [];
926
- });
927
- // 子表用例标题形如「窗口信息-新增行」,export 用例以「 export」结尾,均不参与主表 CRUD 映射。
928
- const childGridTitles = new Set((childGridSlots ?? []).flatMap((slot) => [
929
- `${slot.label}-新增行`,
930
- `${slot.label}-编辑行`,
931
- `${slot.label}-删除行`,
932
- `${slot.label}-必填校验`,
933
- ]));
934
- const unmappable = (glueCases ?? []).filter((testCase) => !parseCrudCaseTitle(testCase.title) &&
935
- !testCase.title.endsWith(' export') &&
936
- !childGridTitles.has(testCase.title));
937
- if (unmappable.length > 0) {
938
- throw new Error(`glue 用例标题无法映射到 CRUD 阶段:${unmappable.map((c) => `${c.caseId}(${c.title})`).join('、')}。` +
939
- '期望格式「<模块名> create|read|update|delete(<查询主键>)」。');
940
- }
941
- const crudCaseTests = renderCrudCaseTests(crudCases, { moduleId, moduleLabel, componentForLabel });
942
- const exportCaseRef = (glueCases ?? []).find((testCase) => testCase.title.endsWith(' export'));
504
+ const deletePolicy = crudSlots.assertions.deletePolicy;
505
+ const blockedDeleteMessage = crudSlots.assertions.blockedDeleteMessage;
506
+ // 删除工作流的业务意图和断言取决于 deletePolicy
507
+ const deleteIntent = deletePolicy === 'blocked_by_business_rule'
508
+ ? `在列表页选择${moduleLabel}记录后点击删除,验证业务规则拦截删除操作${blockedDeleteMessage ? `(${blockedDeleteMessage})` : ''}。`
509
+ : `在列表页选择${moduleLabel}记录后点击删除并确认,验证列表中不再存在该记录。`;
510
+ const deleteAssertion = deletePolicy === 'blocked_by_business_rule'
511
+ ? `删除操作应被业务规则拦截${blockedDeleteMessage ? `,提示「${blockedDeleteMessage}」` : ''},列表保留该记录。`
512
+ : '删除成功后,列表中不再存在该记录,搜索结果为空。';
943
513
  return `import { test, expect, type Page } from '@playwright/test';
944
514
  import { loadCredentials, StagePath, type Credential } from '@epoint-testtech/stage-core';
945
- import { CrudPage, ExportPage, LoginPage${childGridImport}, type CrudFlowSlots, type ExportFlowSlots, type CrudWorkflowData } from '../../skeletons';
515
+ import { CrudPage, ExportPage, LoginPage, type CrudFlowSlots, type ExportFlowSlots, type CrudWorkflowData } from '../../skeletons';
946
516
  import { GlueWorkflowRecorder, runRecordedWorkflow, type GlueWorkflowRecordInput } from '../../report/glue-report';
947
517
 
948
518
  test.describe(${singleQuoted(suiteTitle)}, () => {
@@ -951,7 +521,9 @@ test.describe(${singleQuoted(suiteTitle)}, () => {
951
521
  const primaryRole = ${singleQuoted(primaryRole)};
952
522
  const crudSlots: CrudFlowSlots = ${renderStageSkeletonCrudSlots(crudSlots)};
953
523
  const exportSlots = ${renderExportSlotsFromSlot(exportSlot, crudSlots.frames.listIframeSrcKeyword)};
954
- ${childGridSlots?.length ? ` const childGridSlots: ChildGridFlowSlots[] = ${renderChildGridSlots(childGridSlots)};` : ''}
524
+
525
+ test.describe.configure({ mode: 'serial' });
526
+ let workflowData: CrudWorkflowData | undefined;
955
527
 
956
528
  const recorder = new GlueWorkflowRecorder({
957
529
  outputRoot: StagePath.projectRootDirPath,
@@ -1008,21 +580,118 @@ ${childGridSlots?.length ? ` const childGridSlots: ChildGridFlowSlots[] = ${ren
1008
580
  ...input,
1009
581
  };
1010
582
  }
1011
- ${crudCaseTests}
1012
583
 
1013
- test(${singleQuoted(exportCaseRef ? `${exportCaseRef.caseId} ${exportCaseRef.title}` : `Export:导出${moduleLabel}列表并校验下载文件`)}, async ({ page }, testInfo) => {
584
+ test('Create:新增${moduleLabel}并校验列表存在对应记录', async ({ page }) => {
1014
585
  test.slow();
1015
586
 
1016
587
  await runRecordedWorkflow(
1017
588
  recorder,
1018
589
  workflowMeta({
1019
- workflowId: ${singleQuoted(exportCaseRef ? `${moduleId}.${exportCaseRef.caseId}` : `${moduleId}.export`)},
590
+ workflowId: ${singleQuoted(`${moduleId}.create`)},
591
+ workflowKind: 'create',
592
+ workflowName: '新增${moduleLabel}',
593
+ businessIntent: '在列表页点击新增按钮,填写表单并保存,验证列表中存在新增的记录。',
594
+ elementActions: ['点击列表页新增按钮打开新增弹框', '在新增弹框中填写表单字段', '点击保存并关闭提交表单', '在列表页搜索并验证记录存在'],
595
+ assertionExpectation: '新增成功后,列表页应能通过搜索找到刚新增的记录。',
596
+ playwrightTestTitle: 'Create:新增${moduleLabel}并校验列表存在对应记录',
597
+ }),
598
+ async () => {
599
+ await openModule(page);
600
+ const crudPage = new CrudPage(page, crudSlots);
601
+ workflowData = crudPage.prepareWorkflowData();
602
+ await crudPage.runCreateWorkflow(workflowData);
603
+ },
604
+ );
605
+ });
606
+
607
+ test('Read:搜索${moduleLabel}并校验搜索结果', async ({ page }) => {
608
+ test.slow();
609
+
610
+ await runRecordedWorkflow(
611
+ recorder,
612
+ workflowMeta({
613
+ workflowId: ${singleQuoted(`${moduleId}.read`)},
614
+ workflowKind: 'read',
615
+ workflowName: '搜索${moduleLabel}',
616
+ businessIntent: '使用搜索条件查询记录,验证搜索结果与搜索条件匹配。',
617
+ elementActions: ['在搜索区域填写搜索条件', '点击搜索按钮提交查询', '在结果列表中校验搜索结果'],
618
+ assertionExpectation: '搜索结果列表中应包含符合搜索条件的记录。',
619
+ playwrightTestTitle: 'Read:搜索${moduleLabel}并校验搜索结果',
620
+ }),
621
+ async () => {
622
+ await openModule(page);
623
+ if (!workflowData) {
624
+ throw new Error('Read 工作流依赖 Create 工作流先行生成 workflowData');
625
+ }
626
+ const crudPage = new CrudPage(page, crudSlots);
627
+ await crudPage.runReadWorkflow(workflowData);
628
+ },
629
+ );
630
+ });
631
+
632
+ test('Update:修改${moduleLabel}并校验修改结果', async ({ page }) => {
633
+ test.slow();
634
+
635
+ await runRecordedWorkflow(
636
+ recorder,
637
+ workflowMeta({
638
+ workflowId: ${singleQuoted(`${moduleId}.update`)},
639
+ workflowKind: 'update',
640
+ workflowName: '修改${moduleLabel}',
641
+ businessIntent: '定位目标记录行,点击修改按钮进入编辑弹框,修改字段后保存,验证修改生效。',
642
+ elementActions: ['在列表中定位目标记录行', '点击行操作中的修改按钮打开编辑弹框', '修改表单字段', '点击保存并关闭提交修改'],
643
+ assertionExpectation: '修改成功后,列表页应显示修改后的字段值。',
644
+ playwrightTestTitle: 'Update:修改${moduleLabel}并校验修改结果',
645
+ }),
646
+ async () => {
647
+ await openModule(page);
648
+ if (!workflowData) {
649
+ throw new Error('Update 工作流依赖 Create 工作流先行生成 workflowData');
650
+ }
651
+ const crudPage = new CrudPage(page, crudSlots);
652
+ await crudPage.runUpdateWorkflow(workflowData);
653
+ },
654
+ );
655
+ });
656
+
657
+ test('Delete:删除${moduleLabel}并校验${deletePolicy === 'blocked_by_business_rule' ? '业务规则拦截' : '删除结果'}', async ({ page }) => {
658
+ test.slow();
659
+
660
+ await runRecordedWorkflow(
661
+ recorder,
662
+ workflowMeta({
663
+ workflowId: ${singleQuoted(`${moduleId}.delete`)},
664
+ workflowKind: 'delete',
665
+ workflowName: '删除${moduleLabel}',
666
+ businessIntent: ${singleQuoted(deleteIntent)},
667
+ elementActions: ['在列表中选择目标记录', '点击删除按钮触发删除确认', ${deletePolicy === 'blocked_by_business_rule' ? "'确认删除操作并验证拦截提示'" : "'确认删除操作'"}],
668
+ assertionExpectation: ${singleQuoted(deleteAssertion)},
669
+ playwrightTestTitle: 'Delete:删除${moduleLabel}并校验${deletePolicy === 'blocked_by_business_rule' ? '业务规则拦截' : '删除结果'}',
670
+ }),
671
+ async () => {
672
+ await openModule(page);
673
+ if (!workflowData) {
674
+ throw new Error('Delete 工作流依赖 Create 工作流先行生成 workflowData');
675
+ }
676
+ const crudPage = new CrudPage(page, crudSlots);
677
+ await crudPage.runDeleteWorkflow(workflowData);
678
+ },
679
+ );
680
+ });
681
+
682
+ test('Export:导出${moduleLabel}列表并校验下载文件', async ({ page }, testInfo) => {
683
+ test.slow();
684
+
685
+ await runRecordedWorkflow(
686
+ recorder,
687
+ workflowMeta({
688
+ workflowId: ${singleQuoted(`${moduleId}.export`)},
1020
689
  workflowKind: 'export',
1021
- workflowName: ${singleQuoted(exportCaseRef ? exportCaseRef.title : `导出${moduleLabel}列表`)},
690
+ workflowName: '导出${moduleLabel}列表',
1022
691
  businessIntent: '在列表页触发全部导出,验证导出弹框可驱动并成功下载数据文件。',
1023
692
  elementActions: ['点击列表页数据导出按钮打开导出弹框', '在导出弹框选择「全部」并确认导出', '等待 download 事件并断言下载文件存在且非空'],
1024
693
  assertionExpectation: '导出操作应触发下载,落盘文件存在且大小大于 0。',
1025
- playwrightTestTitle: ${singleQuoted(exportCaseRef ? `${exportCaseRef.caseId} ${exportCaseRef.title}` : `Export:导出${moduleLabel}列表并校验下载文件`)},
694
+ playwrightTestTitle: 'Export:导出${moduleLabel}列表并校验下载文件',
1026
695
  }),
1027
696
  async () => {
1028
697
  await openModule(page);
@@ -1032,8 +701,6 @@ ${crudCaseTests}
1032
701
  );
1033
702
  });
1034
703
 
1035
- ${childGridSlots?.length ? renderChildGridTests(childGridSlots, crudSlots, moduleLabel, moduleId, glueCases) : ''}
1036
-
1037
704
  // @stage-gap-append
1038
705
  });
1039
706
  `;
@@ -1 +1 @@
1
- {"version":3,"file":"ai-runtime-trace.d.ts","sourceRoot":"","sources":["../../../src/runtime/ai-runtime-trace.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,cAAc,GAAG,QAAQ,CAAC;AAEjG,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,MAAM,EAAE,mBAAmB,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,mBAAmB,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,cAAc,CAAC;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,8BAA8B,CAAC;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB,GAAG,cAAc,CASjB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAGlF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,OAAO,CAAC;CAC9B,CAmCA;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQvD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,MAAM,CAyBT"}
1
+ {"version":3,"file":"ai-runtime-trace.d.ts","sourceRoot":"","sources":["../../../src/runtime/ai-runtime-trace.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,cAAc,GAAG,QAAQ,CAAC;AAEjG,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,MAAM,EAAE,mBAAmB,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,mBAAmB,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,cAAc,CAAC;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,8BAA8B,CAAC;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB,GAAG,cAAc,CASjB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAGlF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,OAAO,CAAC;CAC9B,CA2BA;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQvD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,MAAM,CAyBT"}
@@ -34,14 +34,6 @@ export function writeAiRuntimeTrace(tracePath, trace) {
34
34
  * @returns 失败类型、中文摘要和是否需要人工确认。
35
35
  */
36
36
  export function classifyRunFailure(output) {
37
- // 断言失败优先于 iframe/locator 关键字,因为 Playwright 错误信息里常同时包含这些词。
38
- if (/expect\(.*\)\.(toHaveCount|toBe|toContain|toEqual|toHaveText|toBeVisible)/i.test(output)) {
39
- return {
40
- failureType: 'assertion_failure',
41
- summary: '业务断言未通过(如行数、文本值不匹配)',
42
- needsHumanDirection: true,
43
- };
44
- }
45
37
  if (/frame|iframe/i.test(output)) {
46
38
  return {
47
39
  failureType: 'iframe_context_error',