From f2162c6731a0509ee2fbd660a5eab1d1aa24706b Mon Sep 17 00:00:00 2001 From: Nicolas Varrot Date: Fri, 13 Feb 2026 08:42:59 +0000 Subject: [PATCH] test: add i18n and highlight.js test suites (18 cases, 95 total) --- src/lib/__tests__/highlight.test.ts | 64 ++++++++++++++++++++ src/lib/__tests__/i18n.test.ts | 90 +++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 src/lib/__tests__/highlight.test.ts create mode 100644 src/lib/__tests__/i18n.test.ts diff --git a/src/lib/__tests__/highlight.test.ts b/src/lib/__tests__/highlight.test.ts new file mode 100644 index 0000000..31398d3 --- /dev/null +++ b/src/lib/__tests__/highlight.test.ts @@ -0,0 +1,64 @@ +import { describe, it, expect } from 'vitest'; +import hljs, { rehypeHighlightLanguages, rehypeHighlightOptions } from '../highlight'; + +describe('highlight', () => { + const expectedLanguages = [ + 'bash', 'css', 'diff', 'dockerfile', 'go', 'ini', + 'javascript', 'json', 'markdown', 'python', 'rust', + 'shell', 'sql', 'typescript', 'xml', 'yaml', + ]; + + describe('hljs instance', () => { + it('has all expected languages registered', () => { + const registered = hljs.listLanguages(); + for (const lang of expectedLanguages) { + expect(registered).toContain(lang); + } + }); + + it('resolves common aliases', () => { + const aliases = ['sh', 'zsh', 'js', 'jsx', 'ts', 'tsx', 'py', 'html', 'yml', 'rs']; + for (const alias of aliases) { + const lang = hljs.getLanguage(alias); + expect(lang, `alias "${alias}" should resolve`).toBeDefined(); + } + }); + + it('highlights JavaScript code', () => { + const result = hljs.highlight('const x = 42;', { language: 'javascript' }); + expect(result.value).toContain('hljs-'); + expect(result.language).toBe('javascript'); + }); + + it('highlights Python code', () => { + const result = hljs.highlight('def hello():\n pass', { language: 'python' }); + expect(result.value).toContain('hljs-'); + }); + + it('auto-detects language', () => { + const result = hljs.highlightAuto('{"key": "value"}'); + expect(result.language).toBe('json'); + }); + }); + + describe('rehypeHighlightLanguages', () => { + it('exports all expected languages as functions', () => { + for (const lang of expectedLanguages) { + expect(typeof rehypeHighlightLanguages[lang]).toBe('function'); + } + }); + }); + + describe('rehypeHighlightOptions', () => { + it('has languages and aliases', () => { + expect(rehypeHighlightOptions.languages).toBeDefined(); + expect(rehypeHighlightOptions.aliases).toBeDefined(); + }); + + it('aliases map to valid language names', () => { + for (const [lang] of Object.entries(rehypeHighlightOptions.aliases)) { + expect(expectedLanguages).toContain(lang); + } + }); + }); +}); diff --git a/src/lib/__tests__/i18n.test.ts b/src/lib/__tests__/i18n.test.ts new file mode 100644 index 0000000..6d0542c --- /dev/null +++ b/src/lib/__tests__/i18n.test.ts @@ -0,0 +1,90 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { t, getLocale, setLocale, supportedLocales, localeLabels } from '../i18n'; + +describe('i18n', () => { + const originalLocale = getLocale(); + + afterEach(() => { + setLocale(originalLocale); + }); + + describe('supportedLocales', () => { + it('includes en and fr', () => { + expect(supportedLocales).toContain('en'); + expect(supportedLocales).toContain('fr'); + }); + + it('has labels for all supported locales', () => { + for (const loc of supportedLocales) { + expect(localeLabels[loc]).toBeDefined(); + expect(typeof localeLabels[loc]).toBe('string'); + } + }); + }); + + describe('getLocale / setLocale', () => { + it('returns a supported locale', () => { + expect(supportedLocales).toContain(getLocale()); + }); + + it('switches locale', () => { + setLocale('fr'); + expect(getLocale()).toBe('fr'); + setLocale('en'); + expect(getLocale()).toBe('en'); + }); + }); + + describe('t()', () => { + it('returns English strings when locale is en', () => { + setLocale('en'); + expect(t('login.connect')).toBe('Connect'); + }); + + it('returns French strings when locale is fr', () => { + setLocale('fr'); + expect(t('login.connect')).toBe('Connexion'); + }); + + it('returns the key itself for unknown keys', () => { + setLocale('en'); + // Cast to bypass TS — simulates a missing key at runtime + const result = t('nonexistent.key' as never); + expect(result).toBe('nonexistent.key'); + }); + + it('all en keys have corresponding fr translations', () => { + setLocale('en'); + const enResult = t('login.title'); + setLocale('fr'); + const frResult = t('login.title'); + // Both should return non-empty strings (not the key) + expect(enResult.length).toBeGreaterThan(0); + expect(frResult.length).toBeGreaterThan(0); + }); + }); + + describe('onLocaleChange', () => { + it('notifies listeners on locale change', async () => { + const { onLocaleChange } = await import('../i18n'); + let callCount = 0; + const unsub = onLocaleChange(() => callCount++); + + setLocale('fr'); + setLocale('en'); + + expect(callCount).toBe(2); + unsub(); + }); + + it('unsubscribe stops notifications', async () => { + const { onLocaleChange } = await import('../i18n'); + let callCount = 0; + const unsub = onLocaleChange(() => callCount++); + unsub(); + + setLocale('fr'); + expect(callCount).toBe(0); + }); + }); +});