8e6b3abac3
- PluginManager: 采用上游 useMarketPlugins(300行: 分页append/loadingMore/autoLoadBlocked/竞态AbortController/highlightFilter) + MarketPluginList(无限滚动LoadMoreSentinel+PluginViewToggle卡片/列表切换); OfficialPluginList.module.less 保留三处 var(--color-border) 定制+追加上游移动端@media; index.module.less 三向并合; 本地 index.tsx 保留(operator 权限 gate) - Market: MarketPanel(新 install: MarketInstallController 接口)/useMarketInstall/useMarketSearch/SkillIcon/ResultCard 采用上游 - Channels: ChannelDrawer/channelConfig/constants 上游版; 定制点保留(channelIcons console=var(--color-primary)、PendingApprovalsDrawer var(--color-success)) - api/types/chat.ts 采用上游(ChatGroup/ChatSource/ChatGroupKind/group_id); pages/Agent/Skills/index.tsx 适配 useMarketInstall - utils: chatGroups.ts + 14 个上游缺失工具测试补齐; CronJobs 测试补齐 - 测试修复: design-mock 补 Tabs 桩(上游 PluginViewToggle 依赖); MarketPluginList.test 采用上游版; skill.test 品牌映射云超服; lazyWithRetry.test 适配本地 dev-server 回退 - 验证: tsc 0 错误、build 通过、阶段 4 相关 45 测试文件 427 用例全过
57 lines
2.1 KiB
TypeScript
57 lines
2.1 KiB
TypeScript
/**
|
|
* parseErrorDetail extracts structured error details from request.ts
|
|
* formatted errors ("message - {json}") and from raw JSON messages,
|
|
* so the UI can show server-provided details instead of opaque text.
|
|
*/
|
|
import { describe, it, expect } from "vitest";
|
|
import { parseErrorDetail } from "./error";
|
|
|
|
describe("parseErrorDetail", () => {
|
|
it("returns null for non-Error values", () => {
|
|
expect(parseErrorDetail("just a string")).toBeNull();
|
|
expect(parseErrorDetail(42)).toBeNull();
|
|
expect(parseErrorDetail(null)).toBeNull();
|
|
expect(parseErrorDetail(undefined)).toBeNull();
|
|
});
|
|
|
|
it("returns null for a plain-text error message", () => {
|
|
expect(parseErrorDetail(new Error("network is down"))).toBeNull();
|
|
});
|
|
|
|
it("parses JSON after the ' - ' separator used by request.ts", () => {
|
|
const error = new Error('Request failed - {"detail": "quota exceeded"}');
|
|
// The detail key is unwrapped so callers get the server message directly
|
|
expect(parseErrorDetail(error)).toBe("quota exceeded");
|
|
});
|
|
|
|
it("unwraps the detail key when present", () => {
|
|
const error = new Error('400 Bad Request - {"detail": "invalid key"}');
|
|
expect(parseErrorDetail(error)).toBe("invalid key");
|
|
});
|
|
|
|
it("returns the parsed object when there is no detail key", () => {
|
|
const error = new Error('500 - {"code": "internal"}');
|
|
expect(parseErrorDetail(error)).toEqual({ code: "internal" });
|
|
});
|
|
|
|
it("falls back to parsing the whole message when no separator", () => {
|
|
const error = new Error('{"detail": "rate limited"}');
|
|
expect(parseErrorDetail(error)).toBe("rate limited");
|
|
});
|
|
|
|
it("returns null when the separator payload is not valid JSON", () => {
|
|
const error = new Error("failed - not json at all");
|
|
expect(parseErrorDetail(error)).toBeNull();
|
|
});
|
|
|
|
it("returns null when the whole message is not JSON", () => {
|
|
const error = new Error("{broken json");
|
|
expect(parseErrorDetail(error)).toBeNull();
|
|
});
|
|
|
|
it("returns null when the whole message parses to a non-object", () => {
|
|
const error = new Error("42");
|
|
expect(parseErrorDetail(error)).toBeNull();
|
|
});
|
|
});
|