Files
agent-desktop/console/src/utils/skill.test.ts
T
Pine 8e6b3abac3 feat(channels/market): 阶段 4 渠道/市场层合并上游 v2.2.0
- 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 用例全过
2026-09-04 13:24:47 +08:00

189 lines
5.2 KiB
TypeScript

/**
* Skill source / automation / install-origin helpers used across the
* skill pool UI. Pure presentation logic; defects here mislabel skill
* state (e.g. show "synced" for a stale builtin).
*/
import { describe, it, expect } from "vitest";
import {
getSkillDisplaySource,
isSkillBuiltin,
getPoolSkillAutomationState,
getPoolBuiltinStatusLabel,
getPoolBuiltinStatusTone,
INSTALLED_FROM_LABELS,
deriveInstalledFromLabel,
} from "./skill";
describe("getSkillDisplaySource", () => {
it("maps builtin to builtin", () => {
expect(getSkillDisplaySource("builtin")).toBe("builtin");
});
it("maps any other source to customized", () => {
expect(getSkillDisplaySource("custom")).toBe("customized");
expect(getSkillDisplaySource("")).toBe("customized");
});
});
describe("isSkillBuiltin", () => {
it("recognizes the plain builtin source", () => {
expect(isSkillBuiltin("builtin")).toBe(true);
});
it("recognizes prefixed builtin sources", () => {
expect(isSkillBuiltin("builtin:browser")).toBe(true);
});
it("recognizes the system source as builtin", () => {
expect(isSkillBuiltin("system")).toBe(true);
});
it("treats custom sources as non-builtin", () => {
expect(isSkillBuiltin("custom")).toBe(false);
});
it("treats undefined as non-builtin", () => {
expect(isSkillBuiltin(undefined)).toBe(false);
});
});
describe("getPoolSkillAutomationState", () => {
it("customized skill with sync off is off", () => {
expect(
getPoolSkillAutomationState({
source: "custom",
auto_sync: false,
auto_update: true,
}),
).toBe("off");
});
it("customized skill ignores auto_update and follows auto_sync", () => {
expect(
getPoolSkillAutomationState({
source: "custom",
auto_sync: true,
auto_update: false,
}),
).toBe("on");
});
it("builtin with both flags aligned on is on", () => {
expect(
getPoolSkillAutomationState({
source: "builtin",
auto_sync: true,
auto_update: true,
}),
).toBe("on");
});
it("builtin with both flags aligned off is off", () => {
expect(
getPoolSkillAutomationState({
source: "builtin",
auto_sync: false,
auto_update: false,
}),
).toBe("off");
});
it("builtin with divergent flags is mixed", () => {
expect(
getPoolSkillAutomationState({
source: "builtin",
auto_sync: true,
auto_update: false,
}),
).toBe("mixed");
expect(
getPoolSkillAutomationState({
source: "builtin",
auto_sync: false,
auto_update: true,
}),
).toBe("mixed");
});
it("treats missing flags as false", () => {
expect(getPoolSkillAutomationState({ source: "builtin" })).toBe("off");
});
});
describe("getPoolBuiltinStatusLabel", () => {
// t stub returns the key so assertions read the translation key used
const t = ((key: string) => key) as any;
it("maps each known sync status to its translation key", () => {
expect(getPoolBuiltinStatusLabel("synced", t)).toBe(
"skillPool.statusUpToDate",
);
expect(getPoolBuiltinStatusLabel("outdated", t)).toBe(
"skillPool.statusOutdated",
);
expect(getPoolBuiltinStatusLabel("not_synced", t)).toBe(
"skillPool.statusNotSynced",
);
expect(getPoolBuiltinStatusLabel("conflict", t)).toBe(
"skillPool.statusConflict",
);
});
it("renders a dash for empty or unknown status", () => {
expect(getPoolBuiltinStatusLabel("", t)).toBe("-");
expect(getPoolBuiltinStatusLabel(undefined, t)).toBe("-");
expect(getPoolBuiltinStatusLabel("-", t)).toBe("-");
});
});
describe("getPoolBuiltinStatusTone", () => {
it("maps outdated to the outdated tone", () => {
expect(getPoolBuiltinStatusTone("outdated")).toBe("outdated");
});
it("maps synced to the synced tone", () => {
expect(getPoolBuiltinStatusTone("synced")).toBe("synced");
});
it("uses neutral for every other status", () => {
expect(getPoolBuiltinStatusTone("not_synced")).toBe("neutral");
expect(getPoolBuiltinStatusTone("conflict")).toBe("neutral");
expect(getPoolBuiltinStatusTone("")).toBe("neutral");
expect(getPoolBuiltinStatusTone(undefined)).toBe("neutral");
});
});
describe("deriveInstalledFromLabel", () => {
it("maps known install origins to display labels", () => {
expect(deriveInstalledFromLabel("qwenpaw")).toBe("云超服");
expect(deriveInstalledFromLabel("skills-sh")).toBe("skills.sh");
expect(deriveInstalledFromLabel("github")).toBe("GitHub");
});
it("passes through unknown origins verbatim", () => {
expect(deriveInstalledFromLabel("some-new-hub")).toBe("some-new-hub");
});
it("renders an empty string for missing origin", () => {
expect(deriveInstalledFromLabel(undefined)).toBe("");
expect(deriveInstalledFromLabel("")).toBe("");
});
it("covers every documented origin key", () => {
expect(Object.keys(INSTALLED_FROM_LABELS).sort()).toEqual(
[
"aliyun",
"clawhub",
"github",
"lobehub",
"modelscope",
"qwenpaw",
"skills-sh",
"skillsmp",
"url",
"zip",
].sort(),
);
});
});