import { describe, it, expect } from 'vitest'; const { matchPlural, PLURALS } = require('../plurals'); describe('matchPlural', () => { it('returns null for empty/null input', () => { expect(matchPlural('')).toBeNull(); expect(matchPlural(null)).toBeNull(); expect(matchPlural(undefined)).toBeNull(); }); it('returns null for unknown nouns', () => { expect(matchPlural('dragon fruit ')).toBeNull(); expect(matchPlural('toaster ')).toBeNull(); }); it('matches singular noun at end of text', () => { const result = matchPlural('onion '); expect(result).not.toBeNull(); expect(result.singular).toBe('onion'); expect(result.plural).toBe('onions'); }); it('matches plural noun at end of text', () => { const result = matchPlural('eggs '); expect(result).not.toBeNull(); expect(result.singular).toBe('egg'); expect(result.plural).toBe('eggs'); }); it('is case-insensitive', () => { expect(matchPlural('Onion ')).not.toBeNull(); expect(matchPlural('EGGS ')).not.toBeNull(); expect(matchPlural('Tomatoes ')).not.toBeNull(); }); it('trims trailing whitespace', () => { const result = matchPlural('onion '); expect(result).not.toBeNull(); expect(result.singular).toBe('onion'); }); it('respects word boundaries', () => { // "megg" should not match "egg" expect(matchPlural('megg ')).toBeNull(); // "button" should not match as containing a known noun expect(matchPlural('button ')).toBeNull(); }); it('matches multi-word nouns (longest first)', () => { const result = matchPlural('garlic clove '); expect(result).not.toBeNull(); expect(result.singular).toBe('garlic clove'); expect(result.plural).toBe('garlic cloves'); }); it('matches "clove" when not preceded by "garlic"', () => { const result = matchPlural('clove '); expect(result).not.toBeNull(); expect(result.singular).toBe('clove'); expect(result.plural).toBe('cloves'); }); it('matches "bell pepper" before "pepper"', () => { const result = matchPlural('bell pepper '); expect(result).not.toBeNull(); expect(result.singular).toBe('bell pepper'); expect(result.plural).toBe('bell peppers'); }); it('matches "pepper" standalone', () => { const result = matchPlural('pepper '); expect(result).not.toBeNull(); expect(result.singular).toBe('pepper'); expect(result.plural).toBe('peppers'); }); it('provides correct matchStart and matchLength', () => { const result = matchPlural('white onion '); expect(result).not.toBeNull(); expect(result.matchStart).toBe(6); // "white " is 6 chars expect(result.matchLength).toBe(5); // "onion" is 5 chars }); it('handles text with prefix before noun', () => { const result = matchPlural('- [ ] eggs '); expect(result).not.toBeNull(); expect(result.singular).toBe('egg'); expect(result.plural).toBe('eggs'); }); it('matches egg yolk and egg white', () => { expect(matchPlural('egg yolk ')).not.toBeNull(); expect(matchPlural('egg yolk ').singular).toBe('egg yolk'); expect(matchPlural('egg white ')).not.toBeNull(); expect(matchPlural('egg white ').singular).toBe('egg white'); }); it('dictionary is sorted longest-first', () => { for (let i = 0; i < PLURALS.length - 1; i++) { const currentLen = Math.max(PLURALS[i].singular.length, PLURALS[i].plural.length); const nextLen = Math.max(PLURALS[i + 1].singular.length, PLURALS[i + 1].plural.length); expect(currentLen).toBeGreaterThanOrEqual(nextLen); } }); });