From edda87c7ee60dabe67764aed680834ee48de6241 Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Mon, 20 Nov 2023 18:03:34 +0300 Subject: [PATCH] Refactor and test language API --- .../frontend/src/dialogs/DlgEditWordForms.tsx | 6 +- .../frontend/src/models/languageAPI.test.ts | 110 +++++++++++++++++- rsconcept/frontend/src/models/languageAPI.ts | 20 +--- rsconcept/frontend/src/utils/selectors.ts | 4 +- 4 files changed, 116 insertions(+), 24 deletions(-) diff --git a/rsconcept/frontend/src/dialogs/DlgEditWordForms.tsx b/rsconcept/frontend/src/dialogs/DlgEditWordForms.tsx index 42deda4b..4e90b1fa 100644 --- a/rsconcept/frontend/src/dialogs/DlgEditWordForms.tsx +++ b/rsconcept/frontend/src/dialogs/DlgEditWordForms.tsx @@ -11,7 +11,7 @@ import { ArrowLeftIcon, ArrowRightIcon, CheckIcon, ChevronDoubleDownIcon, CrossI import { useConceptTheme } from '../context/ThemeContext'; import useConceptText from '../hooks/useConceptText'; import { Grammeme, ITextRequest, IWordForm, IWordFormPlain } from '../models/language'; -import { getCompatibleGrams, matchWordForm, parseGrammemes } from '../models/languageAPI'; +import { getCompatibleGrams, wordFormEquals, parseGrammemes } from '../models/languageAPI'; import { IConstituenta, TermForm } from '../models/rsform'; import { colorfgGrammeme } from '../utils/color'; import { labelGrammeme } from '../utils/labels'; @@ -81,7 +81,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps) }; setForms(forms => [ newForm, - ...forms.filter(value => !matchWordForm(value, newForm)) + ...forms.filter(value => !wordFormEquals(value, newForm)) ]); } @@ -143,7 +143,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps) text: form.text, grams: parseGrammemes(form.grams).filter(gram => SelectorGrammemesList.find(item => item === gram as Grammeme)) } - if (newForm.grams.length === 2 && !lexeme.some(test => matchWordForm(test, newForm))) { + if (newForm.grams.length === 2 && !lexeme.some(test => wordFormEquals(test, newForm))) { lexeme.push(newForm); } }); diff --git a/rsconcept/frontend/src/models/languageAPI.test.ts b/rsconcept/frontend/src/models/languageAPI.test.ts index d3f6786e..ac8cff5b 100644 --- a/rsconcept/frontend/src/models/languageAPI.test.ts +++ b/rsconcept/frontend/src/models/languageAPI.test.ts @@ -1,13 +1,96 @@ -import { Grammeme } from './language'; -import { parseEntityReference, parseGrammemes, parseSyntacticReference } from './languageAPI'; +import { Case, Grammeme, NounGrams, Plurality, VerbGrams } from './language'; +import { + getCompatibleGrams, grammemeCompare, + parseEntityReference, parseGrammemes, parseSyntacticReference, + wordFormEquals +} from './languageAPI'; + + +describe('Testing wordform equality', () => { + test('empty input', + () => { + expect(wordFormEquals({text: '', grams: []}, {text: '', grams: []})).toEqual(true); + expect(wordFormEquals({text: '', grams: []}, {text: '11', grams: []})).toEqual(true); + expect(wordFormEquals({text: '11', grams: []}, {text: '', grams: []})).toEqual(true); + expect(wordFormEquals({text: '11', grams: []}, {text: '42', grams: []})).toEqual(true); + expect(wordFormEquals({text: '', grams: ['nomn']}, {text: '', grams: []})).toEqual(false); + expect(wordFormEquals({text: '11', grams: ['nomn']}, {text: '11', grams: []})).toEqual(false); + expect(wordFormEquals({text: '', grams: []}, {text: '', grams: ['nomn']})).toEqual(false); + expect(wordFormEquals({text: '11', grams: []}, {text: '11', grams: ['nomn']})).toEqual(false); + }); + + test('regular grammemes', + () => { + expect(wordFormEquals({text: '', grams: ['nomn']}, {text: '', grams: ['nomn']})).toEqual(true); + expect(wordFormEquals({text: '', grams: ['nomn']}, {text: '', grams: ['sing']})).toEqual(false); + expect(wordFormEquals({text: '', grams: ['nomn', 'sing']}, {text: '', grams: ['nomn', 'sing']})).toEqual(true); + expect(wordFormEquals({text: '', grams: ['nomn', 'sing']}, {text: '11', grams: ['nomn', 'sing']})).toEqual(true); + expect(wordFormEquals({text: '11', grams: ['nomn', 'sing']}, {text: '', grams: ['nomn', 'sing']})).toEqual(true); + expect(wordFormEquals({text: '11', grams: ['nomn', 'sing']}, {text: '11', grams: ['nomn', 'sing']})).toEqual(true); + expect(wordFormEquals({text: '42', grams: ['nomn', 'sing']}, {text: '11', grams: ['nomn', 'sing']})).toEqual(true); + expect(wordFormEquals({text: '', grams: ['nomn', 'sing']}, {text: '', grams: ['sing', 'nomn']})).toEqual(false); + expect(wordFormEquals({text: '', grams: ['nomn', 'sing']}, {text: '', grams: ['nomn']})).toEqual(false); + expect(wordFormEquals({text: '', grams: ['nomn', 'nomn']}, {text: '', grams: ['nomn']})).toEqual(false); + }); + + test('custom grammemes', + () => { + expect(wordFormEquals({text: '', grams: ['с1']}, {text: '', grams: ['с1']})).toEqual(true); + expect(wordFormEquals({text: '', grams: ['с1']}, {text: '', grams: ['с2']})).toEqual(false); + expect(wordFormEquals({text: '', grams: ['sing']}, {text: '', grams: ['с1']})).toEqual(false); + }); +}); + + +describe('Testing grammeme ordering', () => { + test('empty input', + () => { + expect(grammemeCompare('', '')).toEqual(0); + expect(grammemeCompare(' ', ' ')).toEqual(0); + expect(grammemeCompare('', '123')).toBeLessThan(0); + expect(grammemeCompare('123', '')).toBeGreaterThan(0); + }); + + test('regular grammemes', + () => { + expect(grammemeCompare('NOUN', 'NOUN')).toEqual(0); + expect(grammemeCompare('NOUN', Grammeme.NOUN)).toEqual(0); + + expect(grammemeCompare(Grammeme.sing, Grammeme.plur)).toBeLessThan(0); + expect(grammemeCompare('sing', 'plur')).toBeLessThan(0); + expect(grammemeCompare('plur', 'sing')).toBeGreaterThan(0); + + expect(grammemeCompare('NOUN', 'ADJF')).toBeLessThan(0); + expect(grammemeCompare('ADJF', 'NOUN')).toBeGreaterThan(0); + expect(grammemeCompare('ADJS', 'NOUN')).toBeGreaterThan(0); + expect(grammemeCompare('ADJS', 'ADJF')).toBeGreaterThan(0); + + expect(grammemeCompare('loct', 'ablt')).toBeGreaterThan(0); + expect(grammemeCompare('ablt', 'accs')).toBeGreaterThan(0); + expect(grammemeCompare('accs', 'datv')).toBeGreaterThan(0); + expect(grammemeCompare('datv', 'gent')).toBeGreaterThan(0); + expect(grammemeCompare('gent', 'nomn')).toBeGreaterThan(0); + }); + + test('custom grammemes', + () => { + expect(grammemeCompare('noun', 'noun')).toEqual(0); + expect(grammemeCompare('NOUN', 'noun')).toBeLessThan(0); + expect(grammemeCompare('PRTF', 'noun')).toBeLessThan(0); + expect(grammemeCompare('noun', 'NOUN')).toBeGreaterThan(0); + expect(grammemeCompare('aab', 'aaa')).toBeGreaterThan(0); + expect(grammemeCompare('aaa', 'aab')).toBeLessThan(0); + expect(grammemeCompare('test', 'abcd')).toBeGreaterThan(0); + }); +}); describe('Testing grammeme parsing', () => { test('empty input', () => { - expect(parseGrammemes('').length).toBe(0); - expect(parseGrammemes(' ').length).toBe(0); - expect(parseGrammemes(' , ').length).toBe(0); + expect(parseGrammemes('')).toStrictEqual([]); + expect(parseGrammemes(' ')).toStrictEqual([]); + expect(parseGrammemes(' , ')).toStrictEqual([]); }); test('regular grammemes', @@ -15,12 +98,29 @@ describe('Testing grammeme parsing', () => { expect(parseGrammemes('NOUN')).toStrictEqual([Grammeme.NOUN]); expect(parseGrammemes('sing,nomn')).toStrictEqual([Grammeme.sing, Grammeme.nomn]); expect(parseGrammemes('nomn,sing')).toStrictEqual([Grammeme.sing, Grammeme.nomn]); + }); + + test('custom grammemes', + () => { expect(parseGrammemes('nomn,invalid,sing')).toStrictEqual([Grammeme.sing, Grammeme.nomn, 'invalid']); expect(parseGrammemes('invalid,test')).toStrictEqual(['invalid', 'test']); }); }); +describe('Testing grammeme compatibility', () => { + test('empty input', + () => { + expect(getCompatibleGrams([])).toStrictEqual([...VerbGrams, ...NounGrams]); + }); + + test('regular grammemes', + () => { + expect(getCompatibleGrams([Grammeme.NOUN])).toStrictEqual([...Case, ...Plurality]); + }); +}); + + describe('Testing reference parsing', () => { test('entity reference', () => { diff --git a/rsconcept/frontend/src/models/languageAPI.ts b/rsconcept/frontend/src/models/languageAPI.ts index e3ca6760..eb8c5a7a 100644 --- a/rsconcept/frontend/src/models/languageAPI.ts +++ b/rsconcept/frontend/src/models/languageAPI.ts @@ -7,7 +7,7 @@ import { GramData, Grammeme, GrammemeGroups, IEntityReference, ISyntacticReferen /** * Equality comparator for {@link IWordForm}. Compares a set of Grammemes attached to wordforms */ -export function matchWordForm(left: IWordForm, right: IWordForm): boolean { +export function wordFormEquals(left: IWordForm, right: IWordForm): boolean { if (left.grams.length !== right.grams.length) { return false; } @@ -19,18 +19,10 @@ export function matchWordForm(left: IWordForm, right: IWordForm): boolean { return true; } -function parseSingleGrammeme(text: string): GramData { - if (Object.values(Grammeme).includes(text as Grammeme)) { - return text as Grammeme; - } else { - return text; - } -} - /** * Compares {@link GramData} based on Grammeme enum and alpha order for strings. */ -export function compareGrammemes(left: GramData, right: GramData): number { +export function grammemeCompare(left: GramData, right: GramData): number { const indexLeft = Object.values(Grammeme).findIndex(gram => gram === left as Grammeme); const indexRight = Object.values(Grammeme).findIndex(gram => gram === right as Grammeme); if (indexLeft === -1 && indexRight === -1) { @@ -51,12 +43,12 @@ export function parseGrammemes(termForm: string): GramData[] { const result: GramData[] = []; const chunks = termForm.split(','); chunks.forEach(chunk => { - chunk = chunk.trim(); - if (chunk !== '') { - result.push(parseSingleGrammeme(chunk)); + const gram = chunk.trim(); + if (gram !== '') { + result.push(gram); } }); - return result.sort(compareGrammemes); + return result.sort(grammemeCompare); } /** diff --git a/rsconcept/frontend/src/utils/selectors.ts b/rsconcept/frontend/src/utils/selectors.ts index 09ccaf32..8d749f9d 100644 --- a/rsconcept/frontend/src/utils/selectors.ts +++ b/rsconcept/frontend/src/utils/selectors.ts @@ -4,7 +4,7 @@ import { LayoutTypes } from 'reagraph'; import { type GramData, Grammeme, ReferenceType } from '../models/language'; -import { compareGrammemes } from '../models/languageAPI'; +import { grammemeCompare } from '../models/languageAPI'; import { CstType } from '../models/rsform'; import { ColoringScheme } from '../pages/RSFormPage/EditorTermGraph'; import { labelGrammeme, labelReferenceType } from './labels'; @@ -61,7 +61,7 @@ export interface IGrammemeOption { * Compares {@link IGrammemeOption} based on Grammeme comparison. */ export function compareGrammemeOptions(left: IGrammemeOption, right: IGrammemeOption): number { - return compareGrammemes(left.value, right.value); + return grammemeCompare(left.value, right.value); } /**