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"); });