tududi/frontend/utils/__tests__/slugUtils.test.ts
2025-08-08 23:14:40 +03:00

275 lines
9.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
});
});