import { beforeEach, describe, expect, test } from 'vitest'; import { Example, ExamplePart, MergedPronounGroup, PronounGroup, PronounLibrary } from '../src/classes.ts'; import { Translator } from '../src/translator.ts'; import type { Translations } from '../locale/translations.ts'; import { configWithPronouns } from './fixtures/config.ts'; const translations: Translations = { pronouns: { any: { short: 'any', }, slashes: { plural: 'plural', pluralHonorific: 'plural-honorific', description: 'description', }, }, }; const translator = new Translator(translations, translations, configWithPronouns); const { default: pronounsFactory, generated: generatedPronouns } = await import('./fixtures/pronouns.ts'); let config = structuredClone(configWithPronouns); const pronouns = Object.fromEntries(Object.entries(pronounsFactory) .map(([name, pronounFactory]) => [name, pronounFactory(config)])); beforeEach(() => { config = structuredClone(configWithPronouns); }); const inlineMorphemeExampleParts = [ new ExamplePart(false, 'This house is '), new ExamplePart(true, 'possessive_pronoun'), ]; const inlineMorphemeExample = new Example(inlineMorphemeExampleParts, inlineMorphemeExampleParts); const capitalizedMorphemeExampleParts = [ new ExamplePart(true, '\'pronoun_subject'), new ExamplePart(false, ' is very nice.'), ]; const capitalizedMorphemeExample = new Example(capitalizedMorphemeExampleParts, capitalizedMorphemeExampleParts); describe('morphemes of examples', () => { test('are contained when present as variable', () => { expect(inlineMorphemeExample.hasMorpheme('possessive_pronoun')).toBe(true); }); test('are contained when present as capitalized variable', () => { expect(capitalizedMorphemeExample.hasMorpheme('pronoun_subject')).toBe(true); }); test('are not contained when missing as variable', () => { expect(inlineMorphemeExample.hasMorpheme('pronoun_subject')).toBe(false); }); }); describe('required morphemes for an example', () => { test('are present if all morphemes are present', () => { expect(inlineMorphemeExample.requiredMorphemesPresent(pronouns.they)).toBe(true); }); test('are present even if one morpheme is empty', () => { expect(inlineMorphemeExample.requiredMorphemesPresent(generatedPronouns.aerWithEmptyPossessivePronoun(config))) .toBe(true); }); test('are missing if one morpheme is null', () => { expect(inlineMorphemeExample.requiredMorphemesPresent(generatedPronouns.aerWithUnsetPossessivePronoun(config))) .toBe(false); }); }); describe('formatting a pronoun with slashes', () => { describe('when slashes are not configured', () => { test('yields no result', () => { expect(generatedPronouns.aer(config).toStringSlashes(translator)).toBeNull(); }); }); describe('when configured that slashes contain all morphemes', () => { beforeEach(() => { config.pronouns.generator!.slashes = true; }); test('chunks contain all morphemes', () => { expect(generatedPronouns.aer(config).toStringSlashes(translator)) .toEqual('ae/aer/aer/aers/aerself'); }); test('morphemes with control symbols gets escaped', () => { expect(generatedPronouns.sSlashHe(config).toStringSlashes(translator)) .toEqual('s`/he/hir/hir/hirs/hirself'); }); test('empty morphemes receive space as placeholder', () => { expect(generatedPronouns.aerWithEmptyPossessivePronoun(config).toStringSlashes(translator)) .toEqual('ae/aer/aer/ /aerself'); }); test('empty morphemes at end receive url-encoded space as placeholder', () => { expect(generatedPronouns.aerWithEmptyReflexive(config).toStringSlashes(translator)) .toEqual('ae/aer/aer/aers/%20'); }); test('unset morphemes receive tilde as placeholder', () => { expect(generatedPronouns.aerWithUnsetPossessivePronoun(config).toStringSlashes(translator)) .toEqual('ae/aer/aer/~/aerself'); }); test('adds plural modifier if necessary', () => { expect(generatedPronouns.aerPlural(config).toStringSlashes(translator)) .toEqual('ae/aer/aer/aers/aerselves/:plural'); }); test('adds plural honorific modifier if necessary', () => { expect(generatedPronouns.aerPluralHonorific(config).toStringSlashes(translator)) .toEqual('ae/aer/aer/aers/aerselves/:plural-honorific'); }); test('adds escaped description if necessary', () => { expect(generatedPronouns.aerWithDescription(config).toStringSlashes(translator)) .toEqual('ae/aer/aer/aers/aerself/:description=Neopronoun “ae” `/ “æ”'); }); test('adds multiple modifiers if necessary', () => { expect(generatedPronouns.aerPluralWithDescription(config).toStringSlashes(translator)) .toEqual('ae/aer/aer/aers/aerselves/:plural/:description=Neopronoun “ae” `/ “æ”'); }); }); describe('when configured that slashes contain some morphemes', () => { beforeEach(() => { config.pronouns.generator!.slashes = ['pronoun_subject', 'pronoun_object', 'possessive_determiner', 'reflexive']; }); test('chunks contain configured morphemes', () => { expect(generatedPronouns.aer(config).toStringSlashes(translator)).toEqual('ae/aer/aer/aerself'); }); }); }); describe('when merging pronoun groups by key', () => { test('groups without keys are not assigned a merged group', () => { const groups = [ new PronounGroup('Normative-ish forms', ['they']), ]; const library = new PronounLibrary(config, groups, pronouns); expect(library.byKey()).toEqual({}); }); test('groups with different keys are assigned different merged groups', () => { const groups = [ new PronounGroup('Binary forms', ['he', 'she'], null, 'normative'), new PronounGroup('Normative-ish forms', ['they'], null, 'normative-ish'), ]; const library = new PronounLibrary(config, groups, pronouns); expect(library.byKey()).toEqual({ normative: new MergedPronounGroup('normative', [ { group: groups[0], groupPronouns: { he: pronouns.he, she: pronouns.she }, }, ]), 'normative-ish': new MergedPronounGroup('normative-ish', [ { group: groups[1], groupPronouns: { they: pronouns.they }, }, ]), }); }); test('groups with same key are assigned same merged group', () => { const groups = [ new PronounGroup('Binary forms', ['he', 'she'], null, 'normative-ish'), new PronounGroup('Normative-ish forms', ['they'], null, 'normative-ish'), ]; const library = new PronounLibrary(config, groups, pronouns); expect(library.byKey()).toEqual({ 'normative-ish': new MergedPronounGroup('normative-ish', [ { group: groups[0], groupPronouns: { he: pronouns.he, she: pronouns.she }, }, { group: groups[1], groupPronouns: { they: pronouns.they }, }, ]), }); }); }); describe('when displaying merged groups', () => { test('and no group specific translation is available, the key is used', () => { const group = new PronounGroup('Binary forms', ['he', 'she'], null, 'normative-ish'); const merged = new MergedPronounGroup('normative', [ { group, groupPronouns: { he: pronouns.he, she: pronouns.she }, }, ]); expect(merged.short(translator)).toBe('any normative'); }); test('and a group specific translation is available, the translation is used', () => { translations.pronouns.any.group = { normative: { short: 'both binaries', }, }; const group = new PronounGroup('Binary forms', ['he', 'she'], null, 'normative-ish'); const merged = new MergedPronounGroup('normative', [ { group, groupPronouns: { he: pronouns.he, she: pronouns.she }, }, ]); expect(merged.short(translator)).toBe('both binaries'); }); });