feat: added support for adding plurality to ingredients

This commit is contained in:
Leyla Becker 2026-02-22 19:53:18 -06:00
parent 750bfb912f
commit 8855d6ba19
9 changed files with 468 additions and 8 deletions

View file

@ -0,0 +1,106 @@
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);
}
});
});