import { describe, it, expect } from 'vitest' import { slugifyBeforeChange } from '@/hooks/slugify' // Minimal args satisfying CollectionBeforeChangeHook signature function makeArgs( data: Record, operation: 'create' | 'update' ): Parameters[0] { return { data, operation, req: {} as never, originalDoc: undefined, context: {}, collection: {} as never, } } describe('slugifyBeforeChange', () => { describe('create operation', () => { it('generates a latin slug from a Cyrillic title', () => { const result = slugifyBeforeChange(makeArgs({ title: 'Шумиленд' }, 'create')) expect((result as { slug: string }).slug).toBe('shumilend') }) it('generates a slug for a multi-word Cyrillic title', () => { const result = slugifyBeforeChange(makeArgs({ title: 'Диво Ліс' }, 'create')) expect((result as { slug: string }).slug).toBe('divo-ls') }) it('generates a slug for a Ukrainian common noun', () => { const result = slugifyBeforeChange(makeArgs({ title: 'Новини' }, 'create')) expect((result as { slug: string }).slug).toBe('novini') }) it('produces only lowercase alphanumeric and hyphen characters', () => { const result = slugifyBeforeChange(makeArgs({ title: 'Шумиленд' }, 'create')) const slug = (result as { slug: string }).slug expect(slug).toMatch(/^[a-z0-9-]+$/) }) it('overwrites any slug value supplied on create', () => { // On create, slug is always regenerated from title const result = slugifyBeforeChange(makeArgs({ title: 'Новини', slug: 'ignore-me' }, 'create')) expect((result as { slug: string }).slug).toBe('novini') }) it('returns data unchanged when title is absent on create', () => { const data = { status: 'draft' } const result = slugifyBeforeChange(makeArgs(data, 'create')) expect(result).toEqual(data) }) it('returns data unchanged when title is not a string', () => { const data = { title: 42 } const result = slugifyBeforeChange(makeArgs(data, 'create')) expect(result).toEqual(data) expect((result as Record)['slug']).toBeUndefined() }) }) describe('update operation', () => { it('does not overwrite a non-empty existing slug on update', () => { const result = slugifyBeforeChange( makeArgs({ title: 'New Title', slug: 'existing-slug' }, 'update') ) expect((result as { slug: string }).slug).toBe('existing-slug') }) it('generates slug from title when slug is empty string on update', () => { const result = slugifyBeforeChange(makeArgs({ title: 'Новини', slug: '' }, 'update')) expect((result as { slug: string }).slug).toBe('novini') }) it('generates slug from title when slug is undefined on update', () => { const result = slugifyBeforeChange(makeArgs({ title: 'Шумиленд' }, 'update')) expect((result as { slug: string }).slug).toBe('shumilend') }) it('returns data unchanged when slug is missing and no title on update', () => { const data = { status: 'published' } const result = slugifyBeforeChange(makeArgs(data, 'update')) expect(result).toEqual(data) }) }) describe('slug format', () => { it('collapses multiple consecutive hyphens', () => { // A title that produces consecutive hyphens after stripping special chars const result = slugifyBeforeChange(makeArgs({ title: 'A B' }, 'create')) const slug = (result as { slug: string }).slug expect(slug).not.toMatch(/--/) }) it('does not start or end with a hyphen', () => { const result = slugifyBeforeChange(makeArgs({ title: 'Шумиленд' }, 'create')) const slug = (result as { slug: string }).slug expect(slug).not.toMatch(/^-/) expect(slug).not.toMatch(/-$/) }) it('produces a non-empty slug for any non-empty Cyrillic title', () => { const result = slugifyBeforeChange(makeArgs({ title: 'Я' }, 'create')) const slug = (result as { slug: string }).slug expect(typeof slug).toBe('string') expect(slug.length).toBeGreaterThan(0) }) }) })