build / build (push) Successful in 1m28s
- 修复办理页「查看流程图」双 hash URL:改 router.resolve + toAppHashUrl 单 hash
- 修复管理端办件抽屉「查看大图」域名尾点(origin + BASE_URL("./") 拼接)
- 新增 flowError 口径层与办理页内联失败条,审批失败提示可读、不泄漏堆栈
- 审批节点抽屉新增「预览候选人」真入口 + AssigneePreviewModal(只读,不写回定义)
- 线级停发/恢复失败改为单条错误面,避免同错双 toast
- 审批抽屉核心区/高级设置分层;插入菜单常用/高级分组
- 新增 FlowAssignEmbed 选人 iframe 父侧宿主(origin/source/信封/context 四校验)
- 修复 /diagram、/assign-preview 面包屑误显示「流程编辑器」
56 lines
2.8 KiB
TypeScript
56 lines
2.8 KiB
TypeScript
import { test } from "node:test";
|
||
import assert from "node:assert/strict";
|
||
import { describeFlowError, flowFailTip, FLOW_GENERIC_FALLBACK, FLOW_NETWORK_FALLBACK } from "../src/utils/flowError";
|
||
|
||
/** 审批失败文案单测:口径=具体业务话原样透出,通用/异常形态换兜底话术且不外泄内部信息 */
|
||
|
||
test("后端给出通用 code=6 占位文案:换成不误导的兜底话术,不猜原因", () => {
|
||
assert.equal(describeFlowError("内部服务错误"), FLOW_GENERIC_FALLBACK);
|
||
assert.equal(describeFlowError("内部服务错误!"), FLOW_GENERIC_FALLBACK);
|
||
assert.equal(describeFlowError("业务错误"), FLOW_GENERIC_FALLBACK);
|
||
assert.equal(describeFlowError(""), FLOW_GENERIC_FALLBACK);
|
||
assert.ok(!FLOW_GENERIC_FALLBACK.includes("成功"));
|
||
});
|
||
|
||
test("契约内的具体业务 msg 原样透出,不改写不误导", () => {
|
||
assert.equal(describeFlowError("流程线不存在:8d07f049"), "流程线不存在:8d07f049");
|
||
assert.equal(describeFlowError("该任务已被他人办理"), "该任务已被他人办理");
|
||
assert.equal(
|
||
describeFlowError("该待办缺少流程推进记录,需管理员回填后办理"),
|
||
"该待办缺少流程推进记录,需管理员回填后办理",
|
||
);
|
||
});
|
||
|
||
test("异常/SQL/令牌形态不回显:堆栈与凭据不进页面", () => {
|
||
const leaks = [
|
||
"java.lang.IllegalStateException: D09 task has no proven activation; audited backfill required",
|
||
"at com.yunhai.flowcenter.service.FlowTaskService.handle(FlowTaskService.java:100)",
|
||
"Caused by: SQLException error code [1064]",
|
||
"refresh token=Bearer abcdef123456",
|
||
"<html><body>500 Internal Server Error</body></html>",
|
||
];
|
||
for (const raw of leaks) {
|
||
const out = describeFlowError(raw);
|
||
assert.equal(out, FLOW_GENERIC_FALLBACK, raw);
|
||
assert.ok(!/java\.|Exception|SQL|Bearer|<html>/i.test(out));
|
||
}
|
||
});
|
||
|
||
test("网络/登录态失败:按网络口径提示,不与业务失败混说", () => {
|
||
assert.equal(describeFlowError("网络错误"), FLOW_NETWORK_FALLBACK);
|
||
assert.equal(describeFlowError("Failed to fetch"), FLOW_NETWORK_FALLBACK);
|
||
assert.equal(describeFlowError("timeout of 20000ms exceeded"), FLOW_NETWORK_FALLBACK);
|
||
});
|
||
|
||
test("Error 对象可直接传入(取 message)", () => {
|
||
assert.equal(describeFlowError(new Error("流程线不存在:g1")), "流程线不存在:g1");
|
||
assert.equal(describeFlowError(new Error("内部服务错误")), FLOW_GENERIC_FALLBACK);
|
||
});
|
||
|
||
test("页面内联提示带动作名,明确「未生效」而不是成功", () => {
|
||
const tip = flowFailTip("同意", new Error("内部服务错误"));
|
||
assert.ok(tip.startsWith("同意未完成:"));
|
||
assert.ok(tip.includes(FLOW_GENERIC_FALLBACK));
|
||
assert.ok(!tip.includes("已同意"));
|
||
});
|