275 lines
9.5 KiB
TypeScript
275 lines
9.5 KiB
TypeScript
import {
|
||
createSlug,
|
||
createUidSlug,
|
||
extractUidFromSlug,
|
||
createProjectUrl,
|
||
createNoteUrl,
|
||
createTagUrl,
|
||
} from '../slugUtils';
|
||
|
||
describe('createSlug - International Languages', () => {
|
||
const testCases = [
|
||
// Greek - slugify handles this well
|
||
{ input: 'νέο έργο', expected: 'neo-ergo', language: 'Greek' },
|
||
{
|
||
input: 'Αρχιτεκτονικό Πρόγραμμα',
|
||
expected: 'arxitektoniko-programma',
|
||
language: 'Greek',
|
||
},
|
||
|
||
// Arabic - slugify handles this
|
||
{ input: 'مشروع جديد', expected: 'mshrwa-jdyd', language: 'Arabic' },
|
||
{ input: 'خطة العمل', expected: 'khth-alaml', language: 'Arabic' },
|
||
|
||
// Chinese - falls back to empty (non-Latin script not supported by slugify)
|
||
{ input: '新项目', expected: '', language: 'Chinese' },
|
||
{ input: '工作计划', expected: '', language: 'Chinese' },
|
||
|
||
// Japanese - falls back to empty
|
||
{
|
||
input: 'あたらしい プロジェクト',
|
||
expected: '',
|
||
language: 'Japanese',
|
||
},
|
||
{ input: 'さぎょう けいかく', expected: '', language: 'Japanese' },
|
||
|
||
// Russian - slugify handles Cyrillic
|
||
{
|
||
input: 'новый проект',
|
||
expected: 'novyj-proekt',
|
||
language: 'Russian',
|
||
},
|
||
{ input: 'План работы', expected: 'plan-raboty', language: 'Russian' },
|
||
|
||
// Hebrew - falls back to empty
|
||
{ input: 'פרויקט חדש', expected: '', language: 'Hebrew' },
|
||
{ input: 'תכנית עבודה', expected: '', language: 'Hebrew' },
|
||
|
||
// Korean - falls back to empty
|
||
{ input: '새로운 프로젝트', expected: '', language: 'Korean' },
|
||
{ input: '작업 계획', expected: '', language: 'Korean' },
|
||
|
||
// Thai - falls back to empty
|
||
{ input: 'โครงการใหม่', expected: '', language: 'Thai' },
|
||
{ input: 'แผนงาน', expected: '', language: 'Thai' },
|
||
|
||
// Hindi - falls back to empty
|
||
{ input: 'नया प्रोजेक्ट', expected: '', language: 'Hindi' },
|
||
{ input: 'कार्य योजना', expected: '', language: 'Hindi' },
|
||
|
||
// Vietnamese - Latin script, slugify handles diacritics
|
||
{ input: 'dự án mới', expected: 'du-an-moi', language: 'Vietnamese' },
|
||
{
|
||
input: 'kế hoạch làm việc',
|
||
expected: 'ke-hoach-lam-viec',
|
||
language: 'Vietnamese',
|
||
},
|
||
];
|
||
|
||
testCases.forEach(({ input, expected, language }) => {
|
||
it(`should create proper slug for ${language}: "${input}"`, () => {
|
||
const result = createSlug(input);
|
||
expect(result).toBe(expected);
|
||
// Only ASCII letters, numbers, and hyphens after transliteration (or empty)
|
||
expect(result).toMatch(/^[a-z0-9-]*$/);
|
||
expect(result).not.toMatch(/^-|-$/); // No leading or trailing hyphens
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('createSlug - European Languages with Diacritics', () => {
|
||
const testCases = [
|
||
// French
|
||
{
|
||
input: 'Café Français',
|
||
expected: 'cafe-francais',
|
||
language: 'French',
|
||
},
|
||
{
|
||
input: 'Résumé Élégant',
|
||
expected: 'resume-elegant',
|
||
language: 'French',
|
||
},
|
||
|
||
// Spanish
|
||
{
|
||
input: 'Niño Español',
|
||
expected: 'nino-espanol',
|
||
language: 'Spanish',
|
||
},
|
||
{ input: 'Año Nuevo', expected: 'ano-nuevo', language: 'Spanish' },
|
||
|
||
// German - slugify handles umlauts and ß
|
||
{ input: 'Größe Weiß', expected: 'grosse-weiss', language: 'German' },
|
||
{
|
||
input: 'Fußball Müller',
|
||
expected: 'fussball-muller',
|
||
language: 'German',
|
||
},
|
||
|
||
// Portuguese
|
||
{
|
||
input: 'João Coração',
|
||
expected: 'joao-coracao',
|
||
language: 'Portuguese',
|
||
},
|
||
{
|
||
input: 'Ação Comunicação',
|
||
expected: 'acao-comunicacao',
|
||
language: 'Portuguese',
|
||
},
|
||
|
||
// Italian
|
||
{
|
||
input: 'Città Università',
|
||
expected: 'citta-universita',
|
||
language: 'Italian',
|
||
},
|
||
{ input: 'Perché Così', expected: 'perche-cosi', language: 'Italian' },
|
||
|
||
// Polish - slugify handles special characters
|
||
{ input: 'Łódź Kraków', expected: 'lodz-krakow', language: 'Polish' },
|
||
{
|
||
input: 'Młody Człowiek',
|
||
expected: 'mlody-czlowiek',
|
||
language: 'Polish',
|
||
},
|
||
|
||
// Czech
|
||
{ input: 'Háček Ústí', expected: 'hacek-usti', language: 'Czech' },
|
||
{ input: 'Středa Škola', expected: 'streda-skola', language: 'Czech' },
|
||
|
||
// Turkish - slugify handles special characters
|
||
{
|
||
input: 'Türkiye İstanbul',
|
||
expected: 'turkiye-istanbul',
|
||
language: 'Turkish',
|
||
},
|
||
{
|
||
input: 'Çalışma Güneş',
|
||
expected: 'calisma-gunes',
|
||
language: 'Turkish',
|
||
},
|
||
];
|
||
|
||
testCases.forEach(({ input, expected, language }) => {
|
||
it(`should handle ${language} diacritics: "${input}"`, () => {
|
||
const result = createSlug(input);
|
||
expect(result).toBe(expected);
|
||
expect(result).toMatch(/^[a-z0-9-]*$/);
|
||
expect(result).not.toMatch(/^-|-$/);
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('createSlug - Edge Cases', () => {
|
||
it('should handle empty string', () => {
|
||
expect(createSlug('')).toBe('');
|
||
});
|
||
|
||
it('should handle string with only special characters', () => {
|
||
expect(createSlug('!@#$%^&*()')).toBe('dollarpercentand');
|
||
});
|
||
|
||
it('should handle mixed languages', () => {
|
||
const result = createSlug('English 中文 العربية');
|
||
expect(result).toBe('english-alarbyh'); // Arabic gets transliterated, Chinese gets removed
|
||
expect(result).toMatch(/^[a-z0-9-]*$/);
|
||
});
|
||
|
||
it('should respect maxLength parameter', () => {
|
||
const longText =
|
||
'This is a very long project name that exceeds the default limit';
|
||
const result = createSlug(longText, 20);
|
||
expect(result.length).toBeLessThanOrEqual(20);
|
||
expect(result).not.toMatch(/-$/); // Should not end with hyphen after truncation
|
||
});
|
||
|
||
it('should handle numbers and letters', () => {
|
||
expect(createSlug('Project 2024 Version 1.0')).toBe(
|
||
'project-2024-version-10'
|
||
);
|
||
});
|
||
|
||
it('should handle multiple consecutive spaces and hyphens', () => {
|
||
expect(createSlug('Multiple Spaces --- And--Hyphens')).toBe(
|
||
'multiple-spaces-and-hyphens'
|
||
);
|
||
});
|
||
});
|
||
|
||
describe('createUidSlug - International Project Names', () => {
|
||
const testUid = '1a2b3c4d5e6f7g8';
|
||
|
||
it('should create uid-slug for Greek project', () => {
|
||
const result = createUidSlug(testUid, 'νέο έργο');
|
||
expect(result).toBe('1a2b3c4d5e6f7g8-neo-ergo');
|
||
});
|
||
|
||
it('should create uid-slug for Arabic project', () => {
|
||
const result = createUidSlug(testUid, 'مشروع جديد');
|
||
expect(result).toBe('1a2b3c4d5e6f7g8-mshrwa-jdyd');
|
||
});
|
||
|
||
it('should create uid-slug for Chinese project', () => {
|
||
const result = createUidSlug(testUid, '新项目');
|
||
expect(result).toBe(testUid); // Falls back to UID only when slug is empty
|
||
});
|
||
|
||
it('should return only uid when name results in empty slug', () => {
|
||
const result = createUidSlug(testUid, '!@#$%');
|
||
expect(result).toBe('1a2b3c4d5e6f7g8-dollarpercent'); // slugify transliterates these
|
||
});
|
||
});
|
||
|
||
describe('URL Creation Functions - International Names', () => {
|
||
const testUid = '1a2b3c4d5e6f7g8';
|
||
|
||
describe('createProjectUrl', () => {
|
||
it('should create URL for Greek project', () => {
|
||
const project = { uid: testUid, name: 'νέο έργο' };
|
||
const result = createProjectUrl(project);
|
||
expect(result).toBe('/project/1a2b3c4d5e6f7g8-neo-ergo');
|
||
});
|
||
|
||
it('should create URL for French project with accents', () => {
|
||
const project = { uid: testUid, name: 'Café Français' };
|
||
const result = createProjectUrl(project);
|
||
expect(result).toBe('/project/1a2b3c4d5e6f7g8-cafe-francais');
|
||
});
|
||
});
|
||
|
||
describe('createNoteUrl', () => {
|
||
it('should create URL for Chinese note', () => {
|
||
const note = { uid: testUid, title: '工作计划' };
|
||
const result = createNoteUrl(note);
|
||
expect(result).toBe('/note/1a2b3c4d5e6f7g8'); // Falls back to UID only
|
||
});
|
||
});
|
||
|
||
describe('createTagUrl', () => {
|
||
it('should create URL for Arabic tag', () => {
|
||
const tag = { uid: testUid, name: 'خطة العمل' };
|
||
const result = createTagUrl(tag);
|
||
expect(result).toBe('/tag/1a2b3c4d5e6f7g8-khth-alaml');
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('extractUidFromSlug - International Slugs', () => {
|
||
const testUid = '1a2b3c4d5e6f7g8';
|
||
|
||
it('should extract UID from Greek project slug', () => {
|
||
const slug = '1a2b3c4d5e6f7g8-neo-ergo';
|
||
expect(extractUidFromSlug(slug)).toBe(testUid);
|
||
});
|
||
|
||
it('should extract UID from Arabic project slug', () => {
|
||
const slug = '1a2b3c4d5e6f7g8-mshrow-gdyd';
|
||
expect(extractUidFromSlug(slug)).toBe(testUid);
|
||
});
|
||
|
||
it('should extract UID when no slug part exists', () => {
|
||
expect(extractUidFromSlug(testUid)).toBe(testUid);
|
||
});
|
||
});
|