basant307/AI_Governance_Project
045
1/**2 * @license3 * Copyright 2025 Qwen4 * SPDX-License-Identifier: Apache-2.05 */6 7import { describe, it, expect } from 'vitest';8import {9 getCommandSourceBadge,10 getCommandSourceGroup,11 formatSupportedModes,12 getCommandDisplayName,13 getCommandSubcommandNames,14 formatCommandSourceLabel,15} from './commandMetadata.js';16import type { SlashCommand } from '../ui/commands/types.js';17import { CommandKind } from '../ui/commands/types.js';18 19function makeCmd(overrides: Partial<SlashCommand> = {}): SlashCommand {20 return {21 name: 'test',22 description: 'Test command',23 kind: CommandKind.BUILT_IN,24 source: 'builtin-command',25 ...overrides,26 action: async () => {},27 } as unknown as SlashCommand;28}29 30// ---------------------------------------------------------------------------31// getCommandSourceBadge32// ---------------------------------------------------------------------------33describe('getCommandSourceBadge', () => {34 it('returns null for builtin-command', () => {35 expect(36 getCommandSourceBadge(makeCmd({ source: 'builtin-command' })),37 ).toBeNull();38 });39 40 it('returns [Skill] for bundled-skill', () => {41 expect(getCommandSourceBadge(makeCmd({ source: 'bundled-skill' }))).toBe(42 '[Skill]',43 );44 });45 46 it('returns [Custom] for skill-dir-command with User label but no source detail', () => {47 expect(48 getCommandSourceBadge(49 makeCmd({ source: 'skill-dir-command', sourceLabel: 'User' }),50 ),51 ).toBe('[Custom]');52 });53 54 it('returns [User] for localized skill-dir-command with user source detail', () => {55 expect(56 getCommandSourceBadge(57 makeCmd({58 source: 'skill-dir-command',59 sourceLabel: '用户',60 sourceDetail: 'user',61 }),62 ),63 ).toBe('[User]');64 });65 66 it('returns [Custom] for skill-dir-command with Project label but no source detail', () => {67 expect(68 getCommandSourceBadge(69 makeCmd({ source: 'skill-dir-command', sourceLabel: 'Project' }),70 ),71 ).toBe('[Custom]');72 });73 74 it('returns [Project] for localized skill-dir-command with project source detail', () => {75 expect(76 getCommandSourceBadge(77 makeCmd({78 source: 'skill-dir-command',79 sourceLabel: '项目',80 sourceDetail: 'project',81 }),82 ),83 ).toBe('[Project]');84 });85 86 it('returns [Custom] for skill-dir-command with other label', () => {87 expect(88 getCommandSourceBadge(89 makeCmd({ source: 'skill-dir-command', sourceLabel: 'Other' }),90 ),91 ).toBe('[Custom]');92 });93 94 it('returns [Plugin] for plugin-command with Extension: prefix but no source detail', () => {95 expect(96 getCommandSourceBadge(97 makeCmd({ source: 'plugin-command', sourceLabel: 'Extension: my-ext' }),98 ),99 ).toBe('[Plugin]');100 });101 102 it('returns [Extension] for localized plugin-command with extension source detail', () => {103 expect(104 getCommandSourceBadge(105 makeCmd({106 source: 'plugin-command',107 sourceLabel: '扩展:my-ext',108 sourceDetail: 'extension',109 }),110 ),111 ).toBe('[Extension]');112 });113 114 it('returns [Plugin] for plugin-command without Extension: prefix', () => {115 expect(116 getCommandSourceBadge(117 makeCmd({ source: 'plugin-command', sourceLabel: 'My Plugin' }),118 ),119 ).toBe('[Plugin]');120 });121 122 it('returns [MCP] for mcp-prompt', () => {123 expect(getCommandSourceBadge(makeCmd({ source: 'mcp-prompt' }))).toBe(124 '[MCP]',125 );126 });127 128 it('returns null for unknown source (default branch)', () => {129 expect(130 getCommandSourceBadge(131 makeCmd({ source: 'unknown-source' as SlashCommand['source'] }),132 ),133 ).toBeNull();134 });135});136 137// ---------------------------------------------------------------------------138// getCommandSourceGroup139// ---------------------------------------------------------------------------140describe('getCommandSourceGroup', () => {141 it('returns built-in group for builtin-command', () => {142 const g = getCommandSourceGroup(makeCmd({ source: 'builtin-command' }));143 expect(g.key).toBe('built-in');144 expect(g.order).toBe(0);145 });146 147 it('returns bundled-skill group', () => {148 const g = getCommandSourceGroup(makeCmd({ source: 'bundled-skill' }));149 expect(g.key).toBe('bundled-skill');150 expect(g.order).toBe(1);151 });152 153 it('returns custom group for skill-dir-command', () => {154 const g = getCommandSourceGroup(makeCmd({ source: 'skill-dir-command' }));155 expect(g.key).toBe('custom');156 expect(g.order).toBe(2);157 });158 159 it('returns plugin group for plugin-command', () => {160 const g = getCommandSourceGroup(makeCmd({ source: 'plugin-command' }));161 expect(g.key).toBe('plugin');162 expect(g.order).toBe(3);163 });164 165 it('returns mcp group for mcp-prompt', () => {166 const g = getCommandSourceGroup(makeCmd({ source: 'mcp-prompt' }));167 expect(g.key).toBe('mcp');168 expect(g.order).toBe(4);169 });170 171 it('returns other group for unknown source', () => {172 const g = getCommandSourceGroup(173 makeCmd({ source: 'unknown-source' as SlashCommand['source'] }),174 );175 expect(g.key).toBe('other');176 expect(g.order).toBe(5);177 });178});179 180// ---------------------------------------------------------------------------181// formatSupportedModes182// ---------------------------------------------------------------------------183describe('formatSupportedModes', () => {184 it('returns [all] when all three modes are present', () => {185 const cmd = makeCmd({186 supportedModes: ['interactive', 'non_interactive', 'acp'],187 });188 expect(formatSupportedModes(cmd)).toBe('[all]');189 });190 191 it('returns [headless] when non_interactive and acp but not interactive', () => {192 const cmd = makeCmd({193 supportedModes: ['non_interactive', 'acp'],194 });195 expect(formatSupportedModes(cmd)).toBe('[headless]');196 });197 198 it('returns [interactive] when only interactive mode', () => {199 const cmd = makeCmd({ supportedModes: ['interactive'] });200 expect(formatSupportedModes(cmd)).toBe('[interactive]');201 });202 203 it('formats individual modes with short tokens', () => {204 const cmd = makeCmd({ supportedModes: ['interactive', 'acp'] });205 const result = formatSupportedModes(cmd);206 expect(result).toContain('[i]');207 expect(result).toContain('[acp]');208 });209});210 211// ---------------------------------------------------------------------------212// getCommandDisplayName213// ---------------------------------------------------------------------------214describe('getCommandDisplayName', () => {215 it('returns plain name with prefix', () => {216 const cmd = makeCmd({ name: 'review' });217 expect(getCommandDisplayName(cmd, { prefix: '/' })).toBe('/review');218 });219 220 it('appends matched alias when provided', () => {221 const cmd = makeCmd({ name: 'stats', altNames: ['usage'] });222 expect(223 getCommandDisplayName(cmd, { prefix: '/', matchedAlias: 'usage' }),224 ).toBe('/stats (alias: usage)');225 });226 227 it('appends altNames when includeAliases not false', () => {228 const cmd = makeCmd({ name: 'stats', altNames: ['usage', 'u'] });229 expect(getCommandDisplayName(cmd)).toBe('stats (usage, u)');230 });231 232 it('omits altNames when includeAliases is false', () => {233 const cmd = makeCmd({ name: 'stats', altNames: ['usage'] });234 expect(getCommandDisplayName(cmd, { includeAliases: false })).toBe('stats');235 });236 237 it('returns plain name when no altNames', () => {238 const cmd = makeCmd({ name: 'clear', altNames: undefined });239 expect(getCommandDisplayName(cmd)).toBe('clear');240 });241});242 243// ---------------------------------------------------------------------------244// getCommandSubcommandNames245// ---------------------------------------------------------------------------246describe('getCommandSubcommandNames', () => {247 it('returns empty array when no subCommands', () => {248 expect(getCommandSubcommandNames(makeCmd())).toEqual([]);249 });250 251 it('returns names of non-hidden subCommands', () => {252 const cmd = makeCmd({253 subCommands: [254 { name: 'add', hidden: false } as SlashCommand,255 { name: 'remove', hidden: true } as SlashCommand,256 { name: 'list', hidden: false } as SlashCommand,257 ],258 });259 expect(getCommandSubcommandNames(cmd)).toEqual(['add', 'list']);260 });261});262 263// ---------------------------------------------------------------------------264// formatCommandSourceLabel265// ---------------------------------------------------------------------------266describe('formatCommandSourceLabel', () => {267 it('returns sourceLabel when present', () => {268 const cmd = makeCmd({ source: 'builtin-command', sourceLabel: 'My Label' });269 expect(formatCommandSourceLabel(cmd)).toBe('My Label');270 });271 272 it('returns Built-in for builtin-command without sourceLabel', () => {273 const cmd = makeCmd({ source: 'builtin-command', sourceLabel: undefined });274 expect(formatCommandSourceLabel(cmd)).toBe('Built-in');275 });276 277 it('returns Skill for bundled-skill', () => {278 const cmd = makeCmd({ source: 'bundled-skill', sourceLabel: undefined });279 expect(formatCommandSourceLabel(cmd)).toBe('Skill');280 });281 282 it('returns Custom for skill-dir-command', () => {283 const cmd = makeCmd({284 source: 'skill-dir-command',285 sourceLabel: undefined,286 });287 expect(formatCommandSourceLabel(cmd)).toBe('Custom');288 });289 290 it('returns Plugin for plugin-command', () => {291 const cmd = makeCmd({ source: 'plugin-command', sourceLabel: undefined });292 expect(formatCommandSourceLabel(cmd)).toBe('Plugin');293 });294 295 it('returns MCP for mcp-prompt', () => {296 const cmd = makeCmd({ source: 'mcp-prompt', sourceLabel: undefined });297 expect(formatCommandSourceLabel(cmd)).toBe('MCP');298 });299 300 it('returns Workflow for workflow-command', () => {301 const cmd = makeCmd({ source: 'workflow-command', sourceLabel: undefined });302 expect(formatCommandSourceLabel(cmd)).toBe('Workflow');303 });304 305 it('returns Unknown when source is falsy', () => {306 const cmd = makeCmd({307 source: undefined as unknown as SlashCommand['source'],308 sourceLabel: undefined,309 });310 expect(formatCommandSourceLabel(cmd)).toBe('Unknown');311 });312});313 