SPB Git

Repository Public

branding.test.ts 4.3 KB 88 lines typescript

1import { describe, expect, it } from 'vitest';2import {3  BRANDING_KEY, DEFAULT_BRANDING, brandOf, confirmBranding, continueCommand, initialBranding, loginHint, parseBranding,4  readCachedBranding, serverBranding, skillKindsPhrase, skillsEmptyHint,5} from '@/lib/branding';67const CODEX = { agent: 'codex', productName: 'Codex Remote', agentName: 'Codex' } as const;8const CLAUDE = { agent: 'claude', productName: 'Claude Remote', agentName: 'Claude Code' } as const;910function memory(initial?: string) {11  const data = new Map<string, string>(initial === undefined ? [] : [[BRANDING_KEY, initial]]);12  return { getItem: (k: string) => data.get(k) ?? null, setItem: (k: string, v: string) => void data.set(k, v), data };13}1415describe('branding resolution', () => {16  it('defaults to the Claude product', () => {17    expect(DEFAULT_BRANDING).toEqual(CLAUDE);18    expect(initialBranding(memory())).toEqual(CLAUDE);19    expect(initialBranding(null)).toEqual(CLAUDE);20  });2122  it('paints with a valid cached value', () => {23    expect(initialBranding(memory(JSON.stringify(CODEX)))).toEqual(CODEX);24  });2526  it('ignores a corrupted cache', () => {27    for (const bad of ['{not json', 'null', '"codex"', '{"agent":"gpt","productName":"X","agentName":"Y"}', '{"agent":"codex"}', JSON.stringify({ ...CODEX, productName: '' }), JSON.stringify({ ...CODEX, productName: 'x'.repeat(41) })]) {28      expect(readCachedBranding(memory(bad))).toBeNull();29      expect(initialBranding(memory(bad))).toEqual(CLAUDE);30    }31  });3233  it('survives a storage that throws', () => {34    const broken = { getItem: () => { throw new Error('denied'); }, setItem: () => { throw new Error('denied'); } };35    expect(initialBranding(broken)).toEqual(CLAUDE);36    expect(confirmBranding(broken, CODEX)).toEqual(CODEX);37  });3839  it('falls back to Claude when the server sends no (or an invalid) branding', () => {40    expect(serverBranding(undefined)).toEqual(CLAUDE);41    expect(serverBranding({ agent: 'other', productName: 'X', agentName: 'Y' })).toEqual(CLAUDE);42    // an older server (no `branding`) behind a Codex cache: the cache is corrected43    const store = memory(JSON.stringify(CODEX));44    expect(confirmBranding(store, undefined)).toEqual(CLAUDE);45    expect(initialBranding(store)).toEqual(CLAUDE);46  });4748  it('lets the server value override the cache and rewrites it', () => {49    const store = memory(JSON.stringify(CLAUDE));50    expect(initialBranding(store)).toEqual(CLAUDE);51    expect(confirmBranding(store, CODEX)).toEqual(CODEX);52    expect(JSON.parse(store.data.get(BRANDING_KEY) ?? 'null')).toEqual(CODEX);53    expect(initialBranding(store)).toEqual(CODEX);54  });5556  it('trims names and keeps only the known fields', () => {57    expect(parseBranding({ agent: 'codex', productName: ' Codex Remote ', agentName: 'Codex', extra: 1 })).toEqual(CODEX);58  });59});6061describe('brand wording', () => {62  const claude = brandOf(CLAUDE);63  const codex = brandOf(CODEX);6465  it('derives names and capabilities from the profile', () => {66    expect([claude.product, claude.agentName, claude.short, claude.binary]).toEqual(['Claude Remote', 'Claude Code', 'Claude', 'claude']);67    expect([codex.product, codex.agentName, codex.short, codex.binary]).toEqual(['Codex Remote', 'Codex', 'Codex', 'codex']);68    expect(codex.caps.mcpHeaders).toBe(false);69    expect(codex.caps.mcpTransports).not.toContain('sse');70    expect(claude.caps.projectMcpScope).toBe(true);71  });7273  it('words the login hint per agent', () => {74    const text = (h: { before: string; code: string; after: string }) => h.before + h.code + h.after;75    expect(text(loginHint(claude))).toBe('Open a Claude session and run /login.');76    expect(text(loginHint(codex))).toBe('Run codex login in a shell session.');77  });7879  it('words the conversation and skills hints per agent', () => {80    expect(continueCommand(claude)).toBe('claude --continue');81    expect(continueCommand(codex)).toBe('codex resume --last');82    expect(skillKindsPhrase(claude.caps.skillKinds)).toBe('Skills, slash commands and sub-agents');83    expect(skillKindsPhrase(codex.caps.skillKinds)).toBe('Skills and sub-agents');84    expect(skillsEmptyHint(claude)).toBe('Skills in ~/.claude/skills, commands and agents appear here.');85    expect(skillsEmptyHint(codex)).toBe('Skills in ~/.codex/skills and agents appear here.');86  });87});88
Files main
Menu

Theme