import { describe, it, expect } from 'vitest'; import { toLocaleUpper, toLocaleLower, buildGoogleFontsUrl, PXLKIT_FONTS, TURKISH_CHARACTERS, } from '../locale'; /* ═══════════════════════════════════════════════════════════════════════════════ toLocaleUpper — Turkish vs English uppercasing ═══════════════════════════════════════════════════════════════════════════════ */ describe('toLocaleUpper', () => { describe('Turkish locale (tr)', () => { it('converts dotted lowercase i to dotted uppercase İ', () => { expect(toLocaleUpper('i', 'tr')).toBe('İ'); }); it('converts dotless lowercase ı to dotless uppercase I', () => { expect(toLocaleUpper('ı', 'tr')).toBe('I'); }); it('converts "istanbul" to "İSTANBUL" (with dotted İ)', () => { expect(toLocaleUpper('istanbul', 'tr')).toBe('İSTANBUL'); }); it('correctly uppercases the Turkish pangram', () => { const result = toLocaleUpper(TURKISH_CHARACTERS.pangram, 'tr'); // "Pijamalı hasta yağız şoföre çabucak güvendi" // → "PİJAMALI HASTA YAĞIZ ŞOFÖRE ÇABUCAK GÜVENDİ" expect(result).toContain('İ'); // dotted İ from 'i' expect(result).toContain('Ş'); // Ş from 'ş' expect(result).toContain('Ğ'); // Ğ from 'ğ' expect(result).toContain('Ç'); // Ç from 'ç' expect(result).toContain('Ö'); // Ö from 'ö' expect(result).toContain('Ü'); // Ü from 'ü' }); it('correctly uppercases Turkish special characters', () => { expect(toLocaleUpper('ç', 'tr')).toBe('Ç'); expect(toLocaleUpper('ğ', 'tr')).toBe('Ğ'); expect(toLocaleUpper('ö', 'tr')).toBe('Ö'); expect(toLocaleUpper('ş', 'tr')).toBe('Ş'); expect(toLocaleUpper('ü', 'tr')).toBe('Ü'); }); it('correctly uppercases the İstanbul sample text', () => { const result = toLocaleUpper(TURKISH_CHARACTERS.sample, 'tr'); // "İstanbul güneşli bir şehirdir" expect(result).toBe('İSTANBUL GÜNEŞLİ BİR ŞEHİRDİR'); }); it('handles empty strings', () => { expect(toLocaleUpper('', 'tr')).toBe(''); }); it('handles strings with no Turkish characters', () => { expect(toLocaleUpper('hello world', 'tr')).toBe('HELLO WORLD'); }); it('handles mixed Turkish and ASCII characters', () => { expect(toLocaleUpper('test işlem', 'tr')).toBe('TEST İŞLEM'); }); }); describe('English locale (en)', () => { it('converts i to I (without dot)', () => { expect(toLocaleUpper('i', 'en')).toBe('I'); }); it('converts "istanbul" to "ISTANBUL" (without dot on I)', () => { expect(toLocaleUpper('istanbul', 'en')).toBe('ISTANBUL'); }); it('handles standard English text', () => { expect(toLocaleUpper('hello world', 'en')).toBe('HELLO WORLD'); }); it('handles empty strings', () => { expect(toLocaleUpper('', 'en')).toBe(''); }); }); describe('default locale', () => { it('defaults to English when no locale is specified', () => { expect(toLocaleUpper('istanbul')).toBe('ISTANBUL'); }); }); }); /* ═══════════════════════════════════════════════════════════════════════════════ toLocaleLower — Turkish vs English lowercasing ═══════════════════════════════════════════════════════════════════════════════ */ describe('toLocaleLower', () => { describe('Turkish locale (tr)', () => { it('converts dotted uppercase İ to dotted lowercase i', () => { expect(toLocaleLower('İ', 'tr')).toBe('i'); }); it('converts dotless uppercase I to dotless lowercase ı', () => { expect(toLocaleLower('I', 'tr')).toBe('ı'); }); it('converts "İSTANBUL" to "istanbul"', () => { expect(toLocaleLower('İSTANBUL', 'tr')).toBe('istanbul'); }); it('converts "ISTANBUL" (dotless I) to "ıstanbul"', () => { expect(toLocaleLower('ISTANBUL', 'tr')).toBe('ıstanbul'); }); it('correctly lowercases Turkish special uppercase characters', () => { expect(toLocaleLower('Ç', 'tr')).toBe('ç'); expect(toLocaleLower('Ğ', 'tr')).toBe('ğ'); expect(toLocaleLower('Ö', 'tr')).toBe('ö'); expect(toLocaleLower('Ş', 'tr')).toBe('ş'); expect(toLocaleLower('Ü', 'tr')).toBe('ü'); }); it('handles empty strings', () => { expect(toLocaleLower('', 'tr')).toBe(''); }); }); describe('English locale (en)', () => { it('converts I to i', () => { expect(toLocaleLower('I', 'en')).toBe('i'); }); it('handles standard English text', () => { expect(toLocaleLower('HELLO WORLD', 'en')).toBe('hello world'); }); }); describe('default locale', () => { it('defaults to English when no locale is specified', () => { expect(toLocaleLower('ISTANBUL')).toBe('istanbul'); }); }); }); /* ═══════════════════════════════════════════════════════════════════════════════ Roundtrip: upper → lower → upper consistency ═══════════════════════════════════════════════════════════════════════════════ */ describe('roundtrip consistency', () => { it('Turkish: lowercase → upper → lower returns original', () => { const original = 'pijamalı hasta yağız şoföre çabucak güvendi'; const upper = toLocaleUpper(original, 'tr'); const lower = toLocaleLower(upper, 'tr'); expect(lower).toBe(original); }); it('English: lowercase → upper → lower returns original', () => { const original = 'hello world'; const upper = toLocaleUpper(original, 'en'); const lower = toLocaleLower(upper, 'en'); expect(lower).toBe(original); }); it('Turkish dotted i roundtrip preserves the dot', () => { // i → İ → i (all in Turkish locale) const step1 = toLocaleUpper('i', 'tr'); // → İ expect(step1).toBe('İ'); const step2 = toLocaleLower(step1, 'tr'); // → i expect(step2).toBe('i'); }); it('Turkish dotless ı roundtrip preserves dotless', () => { // ı → I → ı (all in Turkish locale) const step1 = toLocaleUpper('ı', 'tr'); // → I expect(step1).toBe('I'); const step2 = toLocaleLower(step1, 'tr'); // → ı expect(step2).toBe('ı'); }); }); /* ═══════════════════════════════════════════════════════════════════════════════ buildGoogleFontsUrl ═══════════════════════════════════════════════════════════════════════════════ */ describe('buildGoogleFontsUrl', () => { it('produces a valid URL string', () => { const url = buildGoogleFontsUrl('en'); expect(url).toMatch(/^https:\/\/fonts\.googleapis\.com\/css2\?/); }); it('includes all three font families', () => { const url = buildGoogleFontsUrl('en'); expect(url).toContain('family=Press+Start+2P'); expect(url).toContain('family=Inter'); expect(url).toContain('family=JetBrains+Mono'); }); it('includes weight specifiers for multi-weight fonts', () => { const url = buildGoogleFontsUrl('en'); expect(url).toContain('Inter:wght@400;500;600;700'); expect(url).toContain('JetBrains+Mono:wght@400;500;600;700'); }); it('does not include weight specifier for single-weight fonts', () => { const url = buildGoogleFontsUrl('en'); // Press Start 2P has only weight 400, so no :wght@ parameter expect(url).toContain('family=Press+Start+2P&'); }); it('includes display=swap', () => { const url = buildGoogleFontsUrl('en'); expect(url).toContain('display=swap'); }); it('English locale includes latin subset', () => { const url = buildGoogleFontsUrl('en'); expect(url).toContain('subset=latin'); }); it('Turkish locale includes latin-ext subset', () => { const url = buildGoogleFontsUrl('tr'); expect(url).toContain('subset=latin,latin-ext'); }); it('Turkish URL differs from English URL', () => { const enUrl = buildGoogleFontsUrl('en'); const trUrl = buildGoogleFontsUrl('tr'); expect(enUrl).not.toBe(trUrl); }); it('defaults to English when no locale specified', () => { const defaultUrl = buildGoogleFontsUrl(); const enUrl = buildGoogleFontsUrl('en'); expect(defaultUrl).toBe(enUrl); }); }); /* ═══════════════════════════════════════════════════════════════════════════════ PXLKIT_FONTS configuration ═══════════════════════════════════════════════════════════════════════════════ */ describe('PXLKIT_FONTS', () => { it('defines pixel, sans, and mono font entries', () => { expect(PXLKIT_FONTS).toHaveProperty('pixel'); expect(PXLKIT_FONTS).toHaveProperty('sans'); expect(PXLKIT_FONTS).toHaveProperty('mono'); }); it('pixel font is Press Start 2P', () => { expect(PXLKIT_FONTS.pixel.googleFamily).toBe('Press Start 2P'); expect(PXLKIT_FONTS.pixel.family).toContain('Press Start 2P'); expect(PXLKIT_FONTS.pixel.cssVar).toBe('--font-pixel'); expect(PXLKIT_FONTS.pixel.weights).toEqual([400]); }); it('sans font is Inter', () => { expect(PXLKIT_FONTS.sans.googleFamily).toBe('Inter'); expect(PXLKIT_FONTS.sans.family).toContain('Inter'); expect(PXLKIT_FONTS.sans.cssVar).toBe('--font-sans'); expect(PXLKIT_FONTS.sans.weights).toEqual([400, 500, 600, 700]); }); it('mono font is JetBrains Mono', () => { expect(PXLKIT_FONTS.mono.googleFamily).toBe('JetBrains Mono'); expect(PXLKIT_FONTS.mono.family).toContain('JetBrains Mono'); expect(PXLKIT_FONTS.mono.cssVar).toBe('--font-mono'); expect(PXLKIT_FONTS.mono.weights).toEqual([400, 500, 600, 700]); }); it('all fonts include fallback families', () => { expect(PXLKIT_FONTS.pixel.family).toContain('monospace'); expect(PXLKIT_FONTS.sans.family).toContain('sans-serif'); expect(PXLKIT_FONTS.mono.family).toContain('monospace'); }); }); /* ═══════════════════════════════════════════════════════════════════════════════ TURKISH_CHARACTERS constant ═══════════════════════════════════════════════════════════════════════════════ */ describe('TURKISH_CHARACTERS', () => { it('contains all six Turkish-specific lowercase letters', () => { expect(TURKISH_CHARACTERS.lowercase).toContain('ç'); expect(TURKISH_CHARACTERS.lowercase).toContain('ğ'); expect(TURKISH_CHARACTERS.lowercase).toContain('ı'); expect(TURKISH_CHARACTERS.lowercase).toContain('ö'); expect(TURKISH_CHARACTERS.lowercase).toContain('ş'); expect(TURKISH_CHARACTERS.lowercase).toContain('ü'); }); it('contains all six Turkish-specific uppercase letters', () => { expect(TURKISH_CHARACTERS.uppercase).toContain('Ç'); expect(TURKISH_CHARACTERS.uppercase).toContain('Ğ'); expect(TURKISH_CHARACTERS.uppercase).toContain('İ'); expect(TURKISH_CHARACTERS.uppercase).toContain('Ö'); expect(TURKISH_CHARACTERS.uppercase).toContain('Ş'); expect(TURKISH_CHARACTERS.uppercase).toContain('Ü'); }); it('sample text contains Turkish characters', () => { expect(TURKISH_CHARACTERS.sample).toContain('İ'); expect(TURKISH_CHARACTERS.sample).toContain('ş'); expect(TURKISH_CHARACTERS.sample).toContain('ü'); }); it('pangram contains multiple Turkish-specific characters', () => { const p = TURKISH_CHARACTERS.pangram; // Should contain: ı, ğ, ş, ö, ç, ü expect(p).toContain('ı'); expect(p).toContain('ğ'); expect(p).toContain('ş'); expect(p).toContain('ö'); expect(p).toContain('ç'); expect(p).toContain('ü'); }); }); /* PxlKitLocaleProvider & usePxlKitLocale component tests moved to __tests__/overlay-foundation/PxlKitLocaleProvider.test.tsx (mirrored per-component test layout). This file keeps the pure utility coverage. */ /* ═══════════════════════════════════════════════════════════════════════════════ Edge cases & Unicode boundary testing ═══════════════════════════════════════════════════════════════════════════════ */ describe('edge cases', () => { it('handles strings with only Turkish special chars', () => { expect(toLocaleUpper('çğışöü', 'tr')).toBe('ÇĞIŞÖÜ'); expect(toLocaleLower('ÇĞIŞÖÜİ', 'tr')).toContain('ç'); }); it('handles numbers and punctuation (no change)', () => { expect(toLocaleUpper('123!@#', 'tr')).toBe('123!@#'); expect(toLocaleLower('123!@#', 'tr')).toBe('123!@#'); }); it('handles strings with emoji', () => { expect(toLocaleUpper('merhaba 🇹🇷', 'tr')).toBe('MERHABA 🇹🇷'); }); it('handles single character Turkish ı correctly', () => { expect(toLocaleUpper('ı', 'tr')).toBe('I'); expect(toLocaleLower('I', 'tr')).toBe('ı'); }); it('handles Unicode code points correctly', () => { // İ = U+0130, ı = U+0131 expect(toLocaleUpper('i', 'tr').charCodeAt(0)).toBe(0x0130); // İ expect(toLocaleUpper('ı', 'tr').charCodeAt(0)).toBe(0x0049); // I expect(toLocaleLower('İ', 'tr').charCodeAt(0)).toBe(0x0069); // i expect(toLocaleLower('I', 'tr').charCodeAt(0)).toBe(0x0131); // ı }); it('correctly differentiates between Turkish and English for same input', () => { // The letter 'i' in English uppercases to 'I' (U+0049) // The letter 'i' in Turkish uppercases to 'İ' (U+0130) const enResult = toLocaleUpper('i', 'en'); const trResult = toLocaleUpper('i', 'tr'); expect(enResult).not.toBe(trResult); expect(enResult).toBe('I'); expect(trResult).toBe('İ'); }); });