import { beforeEach, describe, expect, test } from 'vitest'; import type { Translations } from '~/locale/translations.ts'; import { MergedPronounGroup, PronounGroup, PronounLibrary } from '~/src/classes.ts'; import { Example } from '~/src/language/examples.ts'; import { Translator } from '~/src/translator.ts'; import { configWithPronouns } from '~/test/fixtures/config.ts'; import pronounsFactory, { generated as generatedPronouns } from '~/test/fixtures/pronouns.ts'; const translations: Translations = { pronouns: { any: { short: 'any', }, slashes: { plural: 'plural', pluralHonorific: 'plural-honorific', description: 'description', }, }, }; const translator = new Translator(translations, translations, configWithPronouns); let config = structuredClone(configWithPronouns); const pronouns = Object.fromEntries(Object.entries(pronounsFactory) .map(([name, pronounFactory]) => [name, pronounFactory(config)])); beforeEach(() => { config = structuredClone(configWithPronouns); }); const inlineMorphemeExample = new Example([ 'This house is ', { type: 'morpheme', morpheme: 'possessive_pronoun' }, ]); const capitalizedMorphemeExample = new Example([ { type: 'morpheme', morpheme: 'pronoun_subject', capitalise: true }, ' is very nice.', ]); 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.areRequiredExampleValuesPresent(pronouns.they.toExampleValues())) .toBe(true); }); test('are present even if one morpheme is empty', () => { const pronoun = generatedPronouns.aerWithEmptyPossessivePronoun(config); expect(inlineMorphemeExample.areRequiredExampleValuesPresent(pronoun.toExampleValues())) .toBe(true); }); test('are missing if one morpheme is null', () => { const pronoun = generatedPronouns.aerWithUnsetPossessivePronoun(config); expect(inlineMorphemeExample.areRequiredExampleValuesPresent(pronoun.toExampleValues())) .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'); }); });