import { describe, expect, it } from "vitest"; import { RpaError } from "@/modules/rpa/errors"; import { RpaDataInvalidError } from "@/modules/quote/types"; import { formatQuoteErrorMessage, isChineseUserFacingMessage, resolveQuoteFailureCode, resolveQuoteFailureMessage, toChineseUserFacingMessage, translateKnownEnglishPhrases, } from "@/modules/quote/quote-error-messages"; describe("quote-error-messages", () => { it("RpaError 保留细分 error_code", () => { expect( resolveQuoteFailureCode( new RpaError("ADDRESS_SUGGESTION_NOT_FOUND", "未找到地址"), ), ).toBe("ADDRESS_SUGGESTION_NOT_FOUND"); }); it("技术英文原文不直出,回退中文默认", () => { const msg = resolveQuoteFailureMessage( new RpaError( "STRUCT_CHANGE", "locator.waitFor: Timeout 15000ms exceeded", ), "QUOTE_ENTRY_UNAVAILABLE", ); expect(msg).toMatch(/自动化操作超时|报价入口/); expect(msg).not.toMatch(/locator\.waitFor/i); }); it("Playwright 超时译为具体控件原因,不再套用不相关「附加服务未展开」", () => { const msg = toChineseUserFacingMessage( "locator.waitFor: Timeout 30000ms exceeded.\nCall log:\n - waiting for getByTestId('ship-create-continue-button')", "QUOTE_UNAVAILABLE", ); expect(msg).toContain("自动化操作超时"); expect(msg).toContain("继续"); expect(msg).not.toMatch(/附加服务选项未展开/); expect(msg).not.toContain("创建货件/报价表单控件"); expect(msg).not.toBe("暂时无法获取报价,请稍后重试"); }); it("短超时点击提货地址框给出遮挡/加载原因", () => { const msg = toChineseUserFacingMessage( "locator.click: Timeout 5000ms exceeded.\nCall log:\n - waiting for getByTestId('quote-create-pickup-input-search')", "PAGE_LOAD_TIMEOUT", ); expect(msg).toContain("提货地址搜索框"); expect(msg).toMatch(/Inbox|遮挡|加载/); expect(msg).not.toMatch(/附加服务选项未展开/); }); it("历史笼统「创建货件/报价表单控件」文案展示时纠正", () => { const legacy = "自动化操作超时(等待约 5 秒):未能等到「创建货件/报价表单控件」出现或变为可见。可能原因:官网页面结构变更、附加服务选项未展开、网络过慢或登录态异常。"; const msg = formatQuoteErrorMessage("PAGE_LOAD_TIMEOUT", legacy); expect(msg).not.toContain("创建货件/报价表单控件"); expect(msg).not.toMatch(/附加服务选项未展开/); expect(msg).toMatch(/Inbox|继续|地址|创建货件/); }); it("rate-card 等待超时指向报价卡片而非笼统表单", () => { const msg = toChineseUserFacingMessage( "locator.waitFor: Timeout 45000ms exceeded.\nCall log:\n - waiting for getByTestId('rate-card')", "PAGE_LOAD_TIMEOUT", ); expect(msg).toContain("承运商报价卡片"); expect(msg).toMatch(/算价|运力|二级详情/); }); it("可提货日 getByRole 超时译为可提货日专用文案", () => { const msg = toChineseUserFacingMessage( "locator.click: Timeout 30000ms exceeded.\nCall log:\n - waiting for getByRole('button', { name: /Tue, Jul 28th, 2026.*Chevron/i })", "PAGE_LOAD_TIMEOUT", ); expect(msg).toContain("可提货日"); expect(msg).toContain("Inbox"); expect(msg).not.toMatch(/locator\.|getByRole/i); }); it("RpaDataInvalidError 映射为 RPA_DATA_INVALID 并保留中文原因", () => { expect(resolveQuoteFailureCode(new RpaDataInvalidError("缺少标准档报价"))).toBe( "RPA_DATA_INVALID", ); expect( resolveQuoteFailureMessage( new RpaDataInvalidError("缺少标准档报价"), "RPA_DATA_INVALID", ), ).toMatch(/标准档|报价/); }); it("含 rate-card 的中文业务句仍可展示", () => { expect( isChineseUserFacingMessage("登录态 rate-card 未能解析出承运商报价"), ).toBe(true); expect( toChineseUserFacingMessage( "登录态 rate-card 未能解析出承运商报价", "RPA_DATA_INVALID", ), ).toContain("承运商报价"); }); it("合格中文原文保留", () => { expect( formatQuoteErrorMessage("QUOTE_UNAVAILABLE", "该线路暂无可用运力,请换地址"), ).toBe("该线路暂无可用运力,请换地址"); }); it("formatQuoteErrorMessage 按 code 回退", () => { expect(formatQuoteErrorMessage("QUOTE_TIMEOUT")).toContain("420 秒"); }); it("PROVIDER_LOGIN_FAILED 统一用户文案", () => { expect( resolveQuoteFailureCode( new RpaError("PROVIDER_LOGIN_FAILED", "内部细节"), ), ).toBe("PROVIDER_LOGIN_FAILED"); expect( resolveQuoteFailureMessage( new RpaError("SESSION_EXPIRED", "仍停在登录页,账密可能错误"), "SESSION_EXPIRED", ), ).toContain("即时保存更新"); expect(formatQuoteErrorMessage("PROVIDER_LOGIN_FAILED")).toContain( "账号或密码可能不正确", ); expect( formatQuoteErrorMessage( "RPA_DATA_INVALID", "FLOCK_LOGIN_FAILED:账号或密码错误", ), ).toContain("即时保存更新"); }); it("英文官网文案译为中文", () => { expect(translateKnownEnglishPhrases("No rates found")).toBe( "未找到可用报价", ); expect( toChineseUserFacingMessage("No carriers available for this lane", "CARRIER_NO_CAPACITY"), ).toBe("该线路暂无可用运力"); }); it("附加服务 appointment 超时译为可读中文,不再吞成通用文案", () => { const msg = toChineseUserFacingMessage( "locator.waitFor: Timeout 8000ms exceeded.\nCall log:\n - waiting for getByTestId('address-book-accessorials-option-appointment').first() to be visible", "QUOTE_UNAVAILABLE", ); expect(msg).toContain("自动化操作超时"); expect(msg).toMatch(/预约|附加服务/); expect(msg).not.toBe("暂时无法获取报价,请稍后重试"); }); it("附加服务不可用业务文案原样保留", () => { const raw = "派送附加服务「appointment」在官网下拉中不可用或未展开(可见选项:liftgate)。请取消该附加服务后重试,或确认地址类型是否支持。"; expect(toChineseUserFacingMessage(raw, "CARRIER_NO_CAPACITY")).toBe(raw); }); it("登录态日历失败剥离英文 want/sample,仅保留中文", () => { const raw = "登录态日历未点到 2026-08-05 want=Wed Aug 5 sample=Chevron right|Select month and year|Wed Jul 01 2026"; const msg = toChineseUserFacingMessage(raw, "RPA_DATA_INVALID"); expect(msg).toBe("未能选择可提货日 2026-08-05,请稍后重试或更换日期"); expect(msg).not.toMatch(/Chevron|want=|sample=|Wed Aug/i); }); });