@epoint-testtech/ep-stage-skill 0.0.11 → 0.0.13

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 (45) hide show
  1. package/codex-skill/ep-stage/code-list-generate/SKILL.md +168 -0
  2. package/codex-skill/ep-stage/code-list-generate/assets/code_list.template.json5 +325 -0
  3. package/codex-skill/ep-stage/code-list-generate/assets/example-zwplace.json5 +588 -0
  4. package/codex-skill/ep-stage/code-list-generate/evals/evals.json +47 -0
  5. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/action/SchemeResourceAddAction.java +119 -0
  6. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/action/SchemeResourceDetailAction.java +70 -0
  7. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/action/SchemeResourceEditAction.java +107 -0
  8. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/action/SchemeResourceListAction.java +167 -0
  9. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/api/ISchemeResourceService.java +84 -0
  10. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/api/entity/SchemeResource.java +218 -0
  11. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/impl/SchemeResourceService.java +146 -0
  12. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/java/com/epoint/schemeresource/impl/SchemeResourceServiceImpl.java +110 -0
  13. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/webapp/perpage/schemeresource/schemeresourceadd.html +158 -0
  14. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/webapp/perpage/schemeresource/schemeresourcedetail.html +89 -0
  15. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/webapp/perpage/schemeresource/schemeresourceedit.html +125 -0
  16. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/schemeresource-req/src/main/webapp/perpage/schemeresource/schemeresourcelist.html +224 -0
  17. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/docs//345/234/272/346/211/200/347/252/227/345/217/243/344/277/241/346/201/257/347/256/241/347/220/206/345/217/212/345/220/204/347/253/257/347/273/237/350/256/241/346/243/200/347/264/242/346/237/245/347/234/213_prd.md +602 -0
  18. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/action/GxhZwPlaceAddAction.java +210 -0
  19. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/action/GxhZwPlaceDetailAction.java +69 -0
  20. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/action/GxhZwPlaceEditAction.java +235 -0
  21. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/action/GxhZwPlaceListAction.java +145 -0
  22. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/api/IGxhZwPlaceService.java +87 -0
  23. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/api/entity/GxhZwPlace.java +306 -0
  24. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/api/entity/GxhZwPlaceHistory.java +205 -0
  25. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/api/entity/GxhZwWindow.java +184 -0
  26. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/impl/GxhZwPlaceService.java +151 -0
  27. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/java/com/epoint/zwplace/impl/GxhZwPlaceServiceImpl.java +96 -0
  28. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/webapp/perpage/zwplace/gxhzwplaceadd.html +253 -0
  29. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/webapp/perpage/zwplace/gxhzwplacedetail.html +146 -0
  30. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/webapp/perpage/zwplace/gxhzwplaceedit.html +275 -0
  31. package/codex-skill/ep-stage/code-list-generate/evals/fixtures/zwplace-req/src/main/webapp/perpage/zwplace/gxhzwplacelist.html +168 -0
  32. package/codex-skill/ep-stage/code-list-generate/evals/grade-code-list.mjs +184 -0
  33. package/codex-skill/ep-stage/code-list-generate/references/epoint-extraction.md +106 -0
  34. package/codex-skill/ep-stage/code-list-generate/references/field-contract.md +122 -0
  35. package/codex-skill/ep-stage/code-list-generate/scripts/validate-code-list.mjs +202 -0
  36. package/codex-skill/ep-stage/glue-create-project/SKILL.md +227 -223
  37. package/dist/src/extractors/code-list-json5.d.ts +5 -0
  38. package/dist/src/extractors/code-list-json5.d.ts.map +1 -1
  39. package/dist/src/generators/stage-skeleton-script.d.ts +4 -0
  40. package/dist/src/generators/stage-skeleton-script.d.ts.map +1 -1
  41. package/dist/src/generators/stage-skeleton-script.js +399 -362
  42. package/dist/src/testcase/contract-testcase-assembler.d.ts +10 -1
  43. package/dist/src/testcase/contract-testcase-assembler.d.ts.map +1 -1
  44. package/dist/src/testcase/contract-testcase-assembler.js +46 -13
  45. package/package.json +1 -1
@@ -43,6 +43,12 @@ export type CrudCaseStage = {
43
43
  actionKind: GlueActionKind;
44
44
  queryKeyLabel: string;
45
45
  component: 'input' | 'listbox';
46
+ /**
47
+ * 条件性删除(如"草稿可删、在用不可删")时的删除用例变体:
48
+ * draft_success = 满足可删条件(草稿)删除成功;blocked = 不满足条件(在用)被业务规则阻断。
49
+ * 仅 actionKind='delete' 且条件性规则时出现;无条件阻断/正常可删不设置(保持原有用例形态)。
50
+ */
51
+ deleteVariant?: 'draft_success' | 'blocked';
46
52
  };
47
53
  /**
48
54
  * 按查询字段推导 CRUD 用例阶段序列。
@@ -50,15 +56,18 @@ export type CrudCaseStage = {
50
56
  * 规则:在新增表单中出现的查询字段(可写)产出完整 create/read/update/delete 四条;
51
57
  * 不在表单中的字段(如系统管理的状态)只产出 read 一条。
52
58
  * export 不在此计划内,由装配器单独追加。
59
+ * 条件性删除规则(conditionalDelete=true)时,可写键的 delete 由一条变两条:
60
+ * 草稿删除成功(draft_success)在前、在用删除阻断(blocked)在后。
53
61
  *
54
62
  * @param searchFields - 列表页查询字段(有序)。
55
63
  * @param formFieldLabels - 新增表单字段标签集合。
64
+ * @param conditionalDelete - 删除是否为条件性(businessRules 的 delete 规则带 deletableCondition)。
56
65
  * @returns 有序阶段计划,顺序与 buildGlueCases 产出的用例顺序一一对应。
57
66
  */
58
67
  export declare function planCrudCaseStages(searchFields: Array<{
59
68
  label: string;
60
69
  component: 'input' | 'listbox';
61
- }>, formFieldLabels: string[]): CrudCaseStage[];
70
+ }>, formFieldLabels: string[], conditionalDelete?: boolean): CrudCaseStage[];
62
71
  /**
63
72
  * 装配 contract + testcase + inference-trace 账本。
64
73
  *
@@ -1 +1 @@
1
- {"version":3,"file":"contract-testcase-assembler.d.ts","sourceRoot":"","sources":["../../../src/testcase/contract-testcase-assembler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAA2E,MAAM,kCAAkC,CAAC;AACxJ,OAAO,KAAK,EAAE,sBAAsB,EAAqB,MAAM,kCAAkC,CAAC;AAElG,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AASrE,oBAAoB;AACpB,MAAM,MAAM,aAAa,GAAG;IAC1B,iCAAiC;IACjC,QAAQ,EAAE,sBAAsB,CAAC;IACjC,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,sBAAsB,CAAC;IACjC,QAAQ,EAAE,sBAAsB,CAAC;IACjC,mEAAmE;IACnE,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC,CAAC;AAkZF,mBAAmB;AACnB,KAAK,cAAc,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEzE;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,cAAc,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,GAAG,SAAS,CAAA;CAAE,CAAC,EACtE,eAAe,EAAE,MAAM,EAAE,GACxB,aAAa,EAAE,CAejB;AAkaD;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CA8KrF"}
1
+ {"version":3,"file":"contract-testcase-assembler.d.ts","sourceRoot":"","sources":["../../../src/testcase/contract-testcase-assembler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAA2E,MAAM,kCAAkC,CAAC;AACxJ,OAAO,KAAK,EAAE,sBAAsB,EAAqB,MAAM,kCAAkC,CAAC;AAElG,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AASrE,oBAAoB;AACpB,MAAM,MAAM,aAAa,GAAG;IAC1B,iCAAiC;IACjC,QAAQ,EAAE,sBAAsB,CAAC;IACjC,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,sBAAsB,CAAC;IACjC,QAAQ,EAAE,sBAAsB,CAAC;IACjC,mEAAmE;IACnE,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC,CAAC;AAwZF,mBAAmB;AACnB,KAAK,cAAc,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEzE;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,EAAE,cAAc,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B;;;;OAIG;IACH,aAAa,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;CAC7C,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,GAAG,SAAS,CAAA;CAAE,CAAC,EACtE,eAAe,EAAE,MAAM,EAAE,EACzB,iBAAiB,UAAQ,GACxB,aAAa,EAAE,CAsBjB;AAmbD;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CA6LrF"}
@@ -49,7 +49,7 @@ const DEFAULT_LOCATORS = {
49
49
  * @param listPage - code_list.pages[role=list]。
50
50
  * @returns 覆盖 StageSkeletonCrudFlowSlots.locators 全部字段的定位表达式。
51
51
  */
52
- function buildCrudLocators(listPage) {
52
+ function buildCrudLocators(listPage, addOrEditPage, conditionalDelete = false) {
53
53
  const gridId = 'datagrid';
54
54
  const createLabel = listPage.buttons?.find((b) => b.action === 'create')?.label ?? '新增';
55
55
  const deleteLabel = listPage.buttons?.find((b) => b.action === 'delete')?.label ?? '删除选定';
@@ -59,6 +59,10 @@ function buildCrudLocators(listPage) {
59
59
  })?.label ?? '修改';
60
60
  const miniButton = (label) => `xpath=//a[contains(@class,'mini-button')][normalize-space()='${label}']`;
61
61
  const gridCell = `xpath=//*[@id='${gridId}']//div[text()='{{value}}']`;
62
+ // 条件性删除需要「草稿删除成功」用例:新增页保存草稿按钮(action=save,如「保存」)
63
+ // 与关闭按钮(action=cancel,草稿保存后弹窗不关闭,需显式关闭回列表页)。
64
+ const draftSaveLabel = addOrEditPage?.buttons?.find((b) => b.action === 'save')?.label;
65
+ const dialogCloseLabel = addOrEditPage?.buttons?.find((b) => b.action === 'cancel')?.label;
62
66
  return {
63
67
  createButton: miniButton(createLabel),
64
68
  addAutofillTrigger: `xpath=//*[@title='默认填充']`,
@@ -70,6 +74,8 @@ function buildCrudLocators(listPage) {
70
74
  listRowSelectLocatorTemplate: gridCell,
71
75
  listDeleteButton: miniButton(deleteLabel),
72
76
  deleteConfirmButton: 'text=确定',
77
+ ...(conditionalDelete && draftSaveLabel ? { addDraftSaveButton: miniButton(draftSaveLabel) } : {}),
78
+ ...(conditionalDelete && dialogCloseLabel ? { addDialogCloseButton: miniButton(dialogCloseLabel) } : {}),
73
79
  };
74
80
  }
75
81
  /**
@@ -365,16 +371,25 @@ function normalizeQueryComponent(fieldInfo) {
365
371
  * 规则:在新增表单中出现的查询字段(可写)产出完整 create/read/update/delete 四条;
366
372
  * 不在表单中的字段(如系统管理的状态)只产出 read 一条。
367
373
  * export 不在此计划内,由装配器单独追加。
374
+ * 条件性删除规则(conditionalDelete=true)时,可写键的 delete 由一条变两条:
375
+ * 草稿删除成功(draft_success)在前、在用删除阻断(blocked)在后。
368
376
  *
369
377
  * @param searchFields - 列表页查询字段(有序)。
370
378
  * @param formFieldLabels - 新增表单字段标签集合。
379
+ * @param conditionalDelete - 删除是否为条件性(businessRules 的 delete 规则带 deletableCondition)。
371
380
  * @returns 有序阶段计划,顺序与 buildGlueCases 产出的用例顺序一一对应。
372
381
  */
373
- export function planCrudCaseStages(searchFields, formFieldLabels) {
382
+ export function planCrudCaseStages(searchFields, formFieldLabels, conditionalDelete = false) {
374
383
  const stages = [];
375
384
  for (const field of searchFields) {
376
385
  if (formFieldLabels.includes(field.label)) {
377
- stages.push({ actionKind: 'create', queryKeyLabel: field.label, component: field.component }, { actionKind: 'read', queryKeyLabel: field.label, component: field.component }, { actionKind: 'update', queryKeyLabel: field.label, component: field.component }, { actionKind: 'delete', queryKeyLabel: field.label, component: field.component });
386
+ stages.push({ actionKind: 'create', queryKeyLabel: field.label, component: field.component }, { actionKind: 'read', queryKeyLabel: field.label, component: field.component }, { actionKind: 'update', queryKeyLabel: field.label, component: field.component });
387
+ if (conditionalDelete) {
388
+ stages.push({ actionKind: 'delete', queryKeyLabel: field.label, component: field.component, deleteVariant: 'draft_success' }, { actionKind: 'delete', queryKeyLabel: field.label, component: field.component, deleteVariant: 'blocked' });
389
+ }
390
+ else {
391
+ stages.push({ actionKind: 'delete', queryKeyLabel: field.label, component: field.component });
392
+ }
378
393
  }
379
394
  else {
380
395
  stages.push({ actionKind: 'read', queryKeyLabel: field.label, component: field.component });
@@ -425,7 +440,7 @@ function pickQueryKeyLabel(label, searchFieldLabels, dataKeyLabel) {
425
440
  * 仅 delete 用例消费;缺省按 success_delete 出文案。
426
441
  * @returns testcase 执行字段。
427
442
  */
428
- function buildGlueExecutionFields(actionKind, queryKeyLabel, queryComponent, queryKeyInForm, searchFieldLabels, dataKeyLabel, deleteOutcome) {
443
+ function buildGlueExecutionFields(actionKind, queryKeyLabel, queryComponent, queryKeyInForm, searchFieldLabels, dataKeyLabel, deleteOutcome, deleteVariant) {
429
444
  const queryKey = actionKind === 'export'
430
445
  ? pickQueryKeyLabel(dataKeyLabel ?? searchFieldLabels[0] ?? '不适用', searchFieldLabels, dataKeyLabel)
431
446
  : pickQueryKeyLabel(queryKeyLabel, searchFieldLabels, dataKeyLabel);
@@ -493,8 +508,18 @@ function buildGlueExecutionFields(actionKind, queryKeyLabel, queryComponent, que
493
508
  ].join(' ');
494
509
  // 断言文案必须与 skeletonSlots.assertions.deletePolicy 同源:
495
510
  // 业务规则阻断删除时若仍写「列表应无匹配记录」,用例描述会与脚本实际执行(断言阻断文案)相悖。
496
- if (deleteOutcome?.deletePolicy === 'blocked_by_business_rule') {
497
- const blockMessage = deleteOutcome.blockedDeleteMessage ?? '不允许删除';
511
+ // 条件性删除(如"草稿可删、在用不可删")按 deleteVariant 分叉:
512
+ // draft_success = 草稿数据删除成功;blocked = 在用数据删除被阻断。
513
+ if (deleteVariant === 'draft_success') {
514
+ return {
515
+ queryKey,
516
+ precondition: `${smokeEntry} 2. 已基于${queryKey}完成一条测试数据新增(仅保存,数据为草稿状态)。`,
517
+ steps: deleteSteps,
518
+ assertion: `删除后再次按${queryKey}搜索该数据,列表应无匹配记录。`,
519
+ };
520
+ }
521
+ if (deleteVariant === 'blocked' || deleteOutcome?.deletePolicy === 'blocked_by_business_rule') {
522
+ const blockMessage = deleteOutcome?.blockedDeleteMessage ?? '不允许删除';
498
523
  return {
499
524
  queryKey,
500
525
  precondition: `${smokeEntry} 2. 已基于${queryKey}完成一条测试数据新增(保存并关闭,数据为在用状态)。`,
@@ -529,23 +554,25 @@ function buildGlueExecutionFields(actionKind, queryKeyLabel, queryComponent, que
529
554
  * @param dataKeyLabel - dataKey 对应的中文标签。
530
555
  * @returns cases 数组(generationStrategy 全 glue)。
531
556
  */
532
- function buildGlueCases(moduleName, searchFields, formFieldLabels, sequence, dataKeyLabel, deleteOutcome) {
557
+ function buildGlueCases(moduleName, searchFields, formFieldLabels, sequence, dataKeyLabel, deleteOutcome, conditionalDelete = false) {
533
558
  const searchFieldLabels = searchFields.map((fieldInfo) => fieldInfo.label);
534
559
  const componentForLabel = (label) => searchFields.find((fieldInfo) => fieldInfo.label === label)?.component ?? 'input';
535
560
  const queryKeyInForm = (label) => formFieldLabels.includes(label);
536
- const mk = (actionKind, queryKeyLabel, coverageStatus) => ({
561
+ // 条件性删除的标题后缀:spec 生成器按此后缀识别删除变体(parseCrudCaseTitle 同步解析)。
562
+ const deleteTitleSuffix = (variant) => variant === 'draft_success' ? '-草稿' : variant === 'blocked' ? '-在用' : '';
563
+ const mk = (actionKind, queryKeyLabel, coverageStatus, deleteVariant) => ({
537
564
  caseId: nextCaseId(sequence),
538
565
  title: actionKind === 'export'
539
566
  ? `${moduleName} export`
540
- : `${moduleName} ${actionKind}(${queryKeyLabel})`,
567
+ : `${moduleName} ${actionKind}(${queryKeyLabel}${deleteTitleSuffix(deleteVariant)})`,
541
568
  generationStrategy: 'glue',
542
569
  coverageStatus,
543
570
  reviewStatus: 'confirmed',
544
- ...buildGlueExecutionFields(actionKind, queryKeyLabel, componentForLabel(queryKeyLabel), queryKeyInForm(queryKeyLabel), searchFieldLabels, dataKeyLabel, deleteOutcome),
571
+ ...buildGlueExecutionFields(actionKind, queryKeyLabel, componentForLabel(queryKeyLabel), queryKeyInForm(queryKeyLabel), searchFieldLabels, dataKeyLabel, deleteOutcome, deleteVariant),
545
572
  });
546
573
  // 计划驱动:保证用例条数/顺序与 spec 生成器逐条渲染的 test 一一对应。
547
574
  // listbox 键的 read 已由骨架强制断言行锚点命中,不再是 partial。
548
- const cases = planCrudCaseStages(searchFields, formFieldLabels).map((stage) => mk(stage.actionKind, stage.queryKeyLabel, 'covered'));
575
+ const cases = planCrudCaseStages(searchFields, formFieldLabels, conditionalDelete).map((stage) => mk(stage.actionKind, stage.queryKeyLabel, 'covered', stage.deleteVariant));
549
576
  cases.push(mk('export', dataKeyLabel ?? searchFieldLabels[0] ?? '不适用', 'covered'));
550
577
  return cases;
551
578
  }
@@ -738,11 +765,17 @@ export function assembleJson5ContractAndTestcase(input) {
738
765
  ?? deleteScopedRules[0];
739
766
  const deletePolicy = deleteRule ? 'blocked_by_business_rule' : 'success_delete';
740
767
  const blockedDeleteMessage = deleteRule ? readHint(deleteRule) : undefined;
768
+ // 条件性删除(如"草稿可删、在用不可删"):规则带 deletableCondition 时,
769
+ // 每个可写查询主键产出两类删除用例(草稿删除成功 + 在用删除阻断)。
770
+ const deletableCondition = deleteRule
771
+ ? deleteRule.deletableCondition
772
+ : undefined;
773
+ const conditionalDelete = Boolean(deleteRule) && typeof deletableCondition === 'string' && deletableCondition.trim().length > 0;
741
774
  const skeletonSlotsCrud = {
742
775
  moduleId: module,
743
776
  searchConditions: buildSearchConditionSlots(codeList, listPage, addOrEditPage),
744
777
  frames: resolveFrameKeywords(codeList),
745
- locators: buildCrudLocators(listPage),
778
+ locators: buildCrudLocators(listPage, addOrEditPage, conditionalDelete),
746
779
  assertions: {
747
780
  deletePolicy,
748
781
  blockedDeleteMessage,
@@ -791,7 +824,7 @@ export function assembleJson5ContractAndTestcase(input) {
791
824
  const cases = buildGlueCases(moduleName, searchFields, formFieldLabels, caseIdSequence, dataKeyLabel, {
792
825
  deletePolicy,
793
826
  blockedDeleteMessage,
794
- });
827
+ }, conditionalDelete);
795
828
  const childGridCases = childGridSlots ? buildChildGridCases(childGridSlots, firstRole, caseIdSequence) : [];
796
829
  cases.push(...childGridCases);
797
830
  const buttonInferences = inferButtonInteractions(codeList, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epoint-testtech/ep-stage-skill",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "type": "module",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",