You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

174 lines
6.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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