feat(08-05-business-detail-routing): 完成采购与资产独立路由
这个提交包含在:
@@ -0,0 +1,94 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
import {
|
||||
buildAssetDetailPath,
|
||||
buildPurchaseRequestDetailPath,
|
||||
readAssetListFilters,
|
||||
readPurchaseListFilters,
|
||||
resolveAssetLegacyRedirect,
|
||||
resolvePurchaseRequestLegacyRedirect,
|
||||
} from "../../src/lib/businessDetailRoutes";
|
||||
import { resolveNotificationTargetLink } from "../../src/lib/notificationLinks";
|
||||
import { resolvePcDetailSource } from "../../src/lib/pcDetailNavigation";
|
||||
import { resolveMobileEquivalentPath } from "../../src/lib/runtimeContext";
|
||||
import { resolvePageMeta } from "../../src/features/navigation/schema";
|
||||
|
||||
test("采购与资产 canonical 路径统一编码实体 ID,并对空 ID 回落列表", () => {
|
||||
expect(buildPurchaseRequestDetailPath("purchase/001")).toBe("/purchase-requests/purchase%2F001");
|
||||
expect(buildPurchaseRequestDetailPath(" ")).toBe("/purchase-requests");
|
||||
expect(buildAssetDetailPath("asset/001")).toBe("/assets/asset%2F001");
|
||||
expect(buildAssetDetailPath("")).toBe("/assets");
|
||||
|
||||
expect(resolveNotificationTargetLink({ targetType: "purchase_request", targetId: "purchase/001" })).toBe(
|
||||
"/purchase-requests/purchase%2F001",
|
||||
);
|
||||
expect(resolveNotificationTargetLink({ targetType: "asset_card", targetId: "asset/001" })).toBe(
|
||||
"/assets/asset%2F001",
|
||||
);
|
||||
});
|
||||
|
||||
test("旧查询链接只保留白名单筛选,并 replace 到 canonical 详情", () => {
|
||||
const purchaseRedirect = resolvePurchaseRequestLegacyRedirect(new URLSearchParams(
|
||||
"id=purchase%2F001&status=approved&scope=all&supplierId=supplier-1&page=2&unknown=secret",
|
||||
));
|
||||
expect(purchaseRedirect).toEqual({
|
||||
to: "/purchase-requests/purchase%2F001",
|
||||
from: "/purchase-requests?status=approved&scope=all&supplierId=supplier-1&page=2",
|
||||
});
|
||||
|
||||
const assetRedirect = resolveAssetLegacyRedirect(new URLSearchParams(
|
||||
"id=asset-1&q=MacBook&status=in_use&assetType=fixed_asset&sensitivity=internal&scope=own&unknown=secret",
|
||||
));
|
||||
expect(assetRedirect).toEqual({
|
||||
to: "/assets/asset-1",
|
||||
from: "/assets?q=MacBook&status=in_use&assetType=fixed_asset&sensitivity=internal&scope=own",
|
||||
});
|
||||
expect(resolvePurchaseRequestLegacyRedirect(new URLSearchParams("id=%20%20"))).toBeNull();
|
||||
expect(resolveAssetLegacyRedirect(new URLSearchParams("id="))).toBeNull();
|
||||
});
|
||||
|
||||
test("列表筛选读取会收紧非法枚举、页码和越权范围", () => {
|
||||
expect(readPurchaseListFilters(new URLSearchParams("status=unknown&scope=all&page=-2"), false)).toMatchObject({
|
||||
status: "draft",
|
||||
mine: true,
|
||||
page: 1,
|
||||
});
|
||||
expect(readPurchaseListFilters(new URLSearchParams("status=all&scope=all&page=3"), true)).toMatchObject({
|
||||
status: "",
|
||||
mine: false,
|
||||
page: 3,
|
||||
});
|
||||
|
||||
expect(readAssetListFilters(new URLSearchParams("status=unknown&assetType=unknown&sensitivity=unknown&scope=all"), false)).toMatchObject({
|
||||
status: "",
|
||||
assetType: "",
|
||||
sensitivity: "",
|
||||
mine: true,
|
||||
});
|
||||
expect(readAssetListFilters(new URLSearchParams(), true).mine).toBe(false);
|
||||
expect(readAssetListFilters(new URLSearchParams("scope=own"), true).mine).toBe(true);
|
||||
});
|
||||
|
||||
test("详情返回来源按模块白名单校验,拒绝外部、跨模块和当前页", () => {
|
||||
expect(resolvePcDetailSource(
|
||||
"/purchase-requests?status=approved&scope=own",
|
||||
"/purchase-requests/purchase-1",
|
||||
"purchase-request",
|
||||
)).toBe("/purchase-requests?status=approved&scope=own");
|
||||
expect(resolvePcDetailSource("/projects/project-1", "/purchase-requests/purchase-1", "purchase-request")).toBe(
|
||||
"/projects/project-1",
|
||||
);
|
||||
expect(resolvePcDetailSource("/assets?scope=own", "/assets/asset-1", "asset")).toBe("/assets?scope=own");
|
||||
expect(resolvePcDetailSource("/projects/project-1", "/assets/asset-1", "asset")).toBeNull();
|
||||
expect(resolvePcDetailSource("//example.com/assets", "/assets/asset-1", "asset")).toBeNull();
|
||||
expect(resolvePcDetailSource("/assets/asset-1", "/assets/asset-1", "asset")).toBeNull();
|
||||
});
|
||||
|
||||
test("页面元数据与移动工作台回落覆盖新路由", () => {
|
||||
expect(resolvePageMeta("/purchase-requests/purchase-1")).toMatchObject({ title: "采购详情", group: "业务" });
|
||||
expect(resolvePageMeta("/assets/new")).toMatchObject({ title: "新增资产", group: "业务" });
|
||||
expect(resolvePageMeta("/assets/asset-1")).toMatchObject({ title: "资产详情", group: "业务" });
|
||||
expect(resolveMobileEquivalentPath("/purchase-requests/purchase-1")).toBe("/mobile/purchase-requests/purchase-1");
|
||||
expect(resolveMobileEquivalentPath("/assets/new")).toBe("/mobile/assets/scan");
|
||||
expect(resolveMobileEquivalentPath("/assets/asset-1")).toBe("/mobile/assets/scan");
|
||||
});
|
||||
在新工单中引用
屏蔽一个用户