@epoint-testtech/ep-stage-skill 0.0.10 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/cli/index.js
CHANGED
|
File without changes
|
|
@@ -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;
|
|
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"}
|
|
@@ -421,9 +421,11 @@ function pickQueryKeyLabel(label, searchFieldLabels, dataKeyLabel) {
|
|
|
421
421
|
* @param queryKey - 当前用例查询主键。
|
|
422
422
|
* @param searchFieldLabels - 列表页查询字段标签集合。
|
|
423
423
|
* @param dataKeyLabel - dataKey 对应的中文标签。
|
|
424
|
+
* @param deleteOutcome - 删除策略(与 skeletonSlots.assertions.deletePolicy 同源),
|
|
425
|
+
* 仅 delete 用例消费;缺省按 success_delete 出文案。
|
|
424
426
|
* @returns testcase 执行字段。
|
|
425
427
|
*/
|
|
426
|
-
function buildGlueExecutionFields(actionKind, queryKeyLabel, queryComponent, queryKeyInForm, searchFieldLabels, dataKeyLabel) {
|
|
428
|
+
function buildGlueExecutionFields(actionKind, queryKeyLabel, queryComponent, queryKeyInForm, searchFieldLabels, dataKeyLabel, deleteOutcome) {
|
|
427
429
|
const queryKey = actionKind === 'export'
|
|
428
430
|
? pickQueryKeyLabel(dataKeyLabel ?? searchFieldLabels[0] ?? '不适用', searchFieldLabels, dataKeyLabel)
|
|
429
431
|
: pickQueryKeyLabel(queryKeyLabel, searchFieldLabels, dataKeyLabel);
|
|
@@ -483,15 +485,27 @@ function buildGlueExecutionFields(actionKind, queryKeyLabel, queryComponent, que
|
|
|
483
485
|
};
|
|
484
486
|
}
|
|
485
487
|
if (actionKind === 'delete') {
|
|
488
|
+
const deleteSteps = [
|
|
489
|
+
`1. 在列表页按${queryKey}搜索前置数据。`,
|
|
490
|
+
'2. 勾选搜索结果中的目标记录。',
|
|
491
|
+
'3. 点击{{删除按钮}}。',
|
|
492
|
+
'4. 在确认弹窗中点击{{确认按钮}}。',
|
|
493
|
+
].join(' ');
|
|
494
|
+
// 断言文案必须与 skeletonSlots.assertions.deletePolicy 同源:
|
|
495
|
+
// 业务规则阻断删除时若仍写「列表应无匹配记录」,用例描述会与脚本实际执行(断言阻断文案)相悖。
|
|
496
|
+
if (deleteOutcome?.deletePolicy === 'blocked_by_business_rule') {
|
|
497
|
+
const blockMessage = deleteOutcome.blockedDeleteMessage ?? '不允许删除';
|
|
498
|
+
return {
|
|
499
|
+
queryKey,
|
|
500
|
+
precondition: `${smokeEntry} 2. 已基于${queryKey}完成一条测试数据新增(保存并关闭,数据为在用状态)。`,
|
|
501
|
+
steps: deleteSteps,
|
|
502
|
+
assertion: `系统应阻断删除并提示『${blockMessage}』;再次按${queryKey}搜索该数据,记录仍存在。`,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
486
505
|
return {
|
|
487
506
|
queryKey,
|
|
488
507
|
precondition: `${smokeEntry} 2. 已基于${queryKey}完成一条测试数据新增。`,
|
|
489
|
-
steps:
|
|
490
|
-
`1. 在列表页按${queryKey}搜索前置数据。`,
|
|
491
|
-
'2. 勾选搜索结果中的目标记录。',
|
|
492
|
-
'3. 点击{{删除按钮}}。',
|
|
493
|
-
'4. 在确认弹窗中点击{{确认按钮}}。',
|
|
494
|
-
].join(' '),
|
|
508
|
+
steps: deleteSteps,
|
|
495
509
|
assertion: `删除后再次按${queryKey}搜索该数据,列表应无匹配记录。`,
|
|
496
510
|
};
|
|
497
511
|
}
|
|
@@ -515,7 +529,7 @@ function buildGlueExecutionFields(actionKind, queryKeyLabel, queryComponent, que
|
|
|
515
529
|
* @param dataKeyLabel - dataKey 对应的中文标签。
|
|
516
530
|
* @returns cases 数组(generationStrategy 全 glue)。
|
|
517
531
|
*/
|
|
518
|
-
function buildGlueCases(moduleName, searchFields, formFieldLabels, sequence, dataKeyLabel) {
|
|
532
|
+
function buildGlueCases(moduleName, searchFields, formFieldLabels, sequence, dataKeyLabel, deleteOutcome) {
|
|
519
533
|
const searchFieldLabels = searchFields.map((fieldInfo) => fieldInfo.label);
|
|
520
534
|
const componentForLabel = (label) => searchFields.find((fieldInfo) => fieldInfo.label === label)?.component ?? 'input';
|
|
521
535
|
const queryKeyInForm = (label) => formFieldLabels.includes(label);
|
|
@@ -527,7 +541,7 @@ function buildGlueCases(moduleName, searchFields, formFieldLabels, sequence, dat
|
|
|
527
541
|
generationStrategy: 'glue',
|
|
528
542
|
coverageStatus,
|
|
529
543
|
reviewStatus: 'confirmed',
|
|
530
|
-
...buildGlueExecutionFields(actionKind, queryKeyLabel, componentForLabel(queryKeyLabel), queryKeyInForm(queryKeyLabel), searchFieldLabels, dataKeyLabel),
|
|
544
|
+
...buildGlueExecutionFields(actionKind, queryKeyLabel, componentForLabel(queryKeyLabel), queryKeyInForm(queryKeyLabel), searchFieldLabels, dataKeyLabel, deleteOutcome),
|
|
531
545
|
});
|
|
532
546
|
// 计划驱动:保证用例条数/顺序与 spec 生成器逐条渲染的 test 一一对应。
|
|
533
547
|
// listbox 键的 read 已由骨架强制断言行锚点命中,不再是 partial。
|
|
@@ -706,15 +720,24 @@ export function assembleJson5ContractAndTestcase(input) {
|
|
|
706
720
|
// 从 code_list.businessRules 里找 scope 含 'delete' 的规则(如 delete.onlyDraftAllowed
|
|
707
721
|
// "非 DRAFT 状态删除时被阻断"),命中则映射为 blocked_by_business_rule;否则 success_delete。
|
|
708
722
|
// assertionHint 直接作为 skeleton 的阻断消息断言值。
|
|
709
|
-
const
|
|
723
|
+
const deleteScopedRules = (codeList.businessRules ?? []).filter((r) => {
|
|
710
724
|
const anyRule = r;
|
|
711
725
|
return Array.isArray(anyRule.scope) && anyRule.scope.includes('delete');
|
|
712
726
|
});
|
|
727
|
+
// 自愈加固:delete scope 可能混入「未勾选拦截」等操作前置规则(语义上不是记录级删除阻断),
|
|
728
|
+
// 不能简单取第一条。优先级:assertionHint 含阻断语义关键词(不可删除/不允许删除等)者 >
|
|
729
|
+
// 带 assertionHint 者 > 第一条。上游 code-list 规范仍要求阻断规则排第一,此处只是兜底。
|
|
730
|
+
const BLOCK_HINT_PATTERN = /不可删除|不允许删除|无法删除|不能删除|禁止删除/;
|
|
731
|
+
const readHint = (r) => {
|
|
732
|
+
const hint = r.assertionHint
|
|
733
|
+
?? r.assertion;
|
|
734
|
+
return typeof hint === 'string' && hint.trim().length > 0 ? hint : undefined;
|
|
735
|
+
};
|
|
736
|
+
const deleteRule = deleteScopedRules.find((r) => BLOCK_HINT_PATTERN.test(readHint(r) ?? ''))
|
|
737
|
+
?? deleteScopedRules.find((r) => readHint(r) !== undefined)
|
|
738
|
+
?? deleteScopedRules[0];
|
|
713
739
|
const deletePolicy = deleteRule ? 'blocked_by_business_rule' : 'success_delete';
|
|
714
|
-
const blockedDeleteMessage = deleteRule
|
|
715
|
-
? (deleteRule.assertionHint
|
|
716
|
-
?? deleteRule.assertion)
|
|
717
|
-
: undefined;
|
|
740
|
+
const blockedDeleteMessage = deleteRule ? readHint(deleteRule) : undefined;
|
|
718
741
|
const skeletonSlotsCrud = {
|
|
719
742
|
moduleId: module,
|
|
720
743
|
searchConditions: buildSearchConditionSlots(codeList, listPage, addOrEditPage),
|
|
@@ -765,7 +788,10 @@ export function assembleJson5ContractAndTestcase(input) {
|
|
|
765
788
|
unresolvedSlots: [], // 框架默认策略都已确认,不进 unresolvedSlots
|
|
766
789
|
};
|
|
767
790
|
const caseIdSequence = { pageIndex: 1, nextCaseIndex: 1 };
|
|
768
|
-
const cases = buildGlueCases(moduleName, searchFields, formFieldLabels, caseIdSequence, dataKeyLabel
|
|
791
|
+
const cases = buildGlueCases(moduleName, searchFields, formFieldLabels, caseIdSequence, dataKeyLabel, {
|
|
792
|
+
deletePolicy,
|
|
793
|
+
blockedDeleteMessage,
|
|
794
|
+
});
|
|
769
795
|
const childGridCases = childGridSlots ? buildChildGridCases(childGridSlots, firstRole, caseIdSequence) : [];
|
|
770
796
|
cases.push(...childGridCases);
|
|
771
797
|
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.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
@@ -19,6 +19,18 @@
|
|
|
19
19
|
"registry": "https://registry.npmjs.org/"
|
|
20
20
|
},
|
|
21
21
|
"description": "Agent skills and TypeScript tools for ep-stage glue-code workflows.",
|
|
22
|
+
"scripts": {
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"build": "tsc -p tsconfig.build.json",
|
|
26
|
+
"prepare": "pnpm run build",
|
|
27
|
+
"prepack": "pnpm run build",
|
|
28
|
+
"generate:crud-contract": "node --import tsx src/cli/dev/extract-contract.ts",
|
|
29
|
+
"generate:playwright-tests": "node --import tsx src/cli/index.ts spec",
|
|
30
|
+
"run:gap-pipeline": "node --import tsx src/cli/run-gap-pipeline.ts",
|
|
31
|
+
"cli": "node --import tsx src/cli/index.ts",
|
|
32
|
+
"generate:testcase": "node --import tsx src/cli/index.ts testcase"
|
|
33
|
+
},
|
|
22
34
|
"dependencies": {
|
|
23
35
|
"cheerio": "^1.0.0",
|
|
24
36
|
"json5": "^2.2.3",
|
|
@@ -29,15 +41,5 @@
|
|
|
29
41
|
"tsx": "^4.20.0",
|
|
30
42
|
"typescript": "^6.0.3",
|
|
31
43
|
"vitest": "^4.0.0"
|
|
32
|
-
},
|
|
33
|
-
"scripts": {
|
|
34
|
-
"typecheck": "tsc --noEmit",
|
|
35
|
-
"test": "vitest run",
|
|
36
|
-
"build": "tsc -p tsconfig.build.json",
|
|
37
|
-
"generate:crud-contract": "node --import tsx src/cli/dev/extract-contract.ts",
|
|
38
|
-
"generate:playwright-tests": "node --import tsx src/cli/index.ts spec",
|
|
39
|
-
"run:gap-pipeline": "node --import tsx src/cli/run-gap-pipeline.ts",
|
|
40
|
-
"cli": "node --import tsx src/cli/index.ts",
|
|
41
|
-
"generate:testcase": "node --import tsx src/cli/index.ts testcase"
|
|
42
44
|
}
|
|
43
|
-
}
|
|
45
|
+
}
|