From 4893ef0d7072beec10cf173ad88127b7c924f73f Mon Sep 17 00:00:00 2001 From: adueck Date: Wed, 9 Aug 2023 16:04:36 +0400 Subject: [PATCH] more towards parsing vps --- package.json | 3 +- src/lib/package.json | 2 +- src/lib/src/parsing/lookup.tsx | 30 +- src/lib/src/parsing/parse-verb.test.ts | 413 +++++++++++++++++++++++++ src/lib/src/parsing/parse-verb.ts | 210 ++++++++++--- src/lib/src/parsing/parse-vp.ts | 256 +++++++++++---- src/setupTests.js | 6 +- tsconfig.json | 3 +- yarn.lock | 54 ++++ 9 files changed, 860 insertions(+), 117 deletions(-) create mode 100644 src/lib/src/parsing/parse-verb.test.ts diff --git a/package.json b/package.json index b8efe62..630565a 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@types/react": "^17.0.3", "@types/react-dom": "^17.0.2", "bootstrap": "^4.6.0", + "jest-extended": "^4.0.1", "node-fetch": "^3.2.10", "node-fetch-commonjs": "^3.2.4", "pbf": "^3.2.1", @@ -76,4 +77,4 @@ ] }, "dependencies": {} -} +} \ No newline at end of file diff --git a/src/lib/package.json b/src/lib/package.json index 1fc9680..c61e238 100644 --- a/src/lib/package.json +++ b/src/lib/package.json @@ -33,4 +33,4 @@ "@types/pbf": "^3.0.2", "rollup": "^2.79.1" } -} +} \ No newline at end of file diff --git a/src/lib/src/parsing/lookup.tsx b/src/lib/src/parsing/lookup.tsx index b8b1d96..bd97888 100644 --- a/src/lib/src/parsing/lookup.tsx +++ b/src/lib/src/parsing/lookup.tsx @@ -31,10 +31,32 @@ export function lookup(s: Partial): T.DictionaryEntry[] { return nounsAdjs.filter((e) => e[key] === value) as T.DictionaryEntry[]; } -export function verbLookup( - s: (e: T.VerbDictionaryEntry) => boolean -): T.VerbEntry[] { - return verbs.filter(({ entry }) => s(entry)); +export function verbLookup(input: string): T.VerbEntry[] { + const s = input.slice(0, -1); + if (s.endsWith("ېږ")) { + return verbs.filter( + ({ entry }) => + entry.p.slice(0, -1) === s || + entry.p === s.slice(0, -1) + "دل" || + entry.p === s || + entry.psp === s || + entry.prp === s || + entry.ssp === s + ); + } + return verbs.filter( + ({ entry }) => + entry.p.slice(0, -1) === s || + // for short intransitive forms + entry.p.slice(0, -3) === s || + entry.p === s || + entry.psp === s || + entry.prp === s || + entry.ssp === s || + (entry.separationAtP && + (entry.p.slice(entry.separationAtP) === s || + entry.psp?.slice(entry.separationAtP) === s)) + ); } export function wordQuery(word: string, type: "adj"): T.AdjectiveEntry; diff --git a/src/lib/src/parsing/parse-verb.test.ts b/src/lib/src/parsing/parse-verb.test.ts new file mode 100644 index 0000000..f38f553 --- /dev/null +++ b/src/lib/src/parsing/parse-verb.test.ts @@ -0,0 +1,413 @@ +/* eslint-disable jest/valid-title */ +import * as T from "../../../types"; +import { verbLookup, wordQuery } from "./lookup"; +import { parseVerb } from "./parse-verb"; +import { tokenizer } from "./tokenizer"; + +const wahul = wordQuery("وهل", "verb"); +const leekul = wordQuery("لیکل", "verb"); +const manul = wordQuery("منل", "verb"); +const rasedul = wordQuery("رسېدل", "verb"); +const leedul = wordQuery("لیدل", "verb"); +const khorul = wordQuery("خوړل", "verb"); +const kenaastul = wordQuery("کېناستل", "verb"); +const prexodul = wordQuery("پرېښودل", "verb"); +const xodul = wordQuery("ښودل", "verb"); +const kexodul = wordQuery("کېښودل", "verb"); + +const tests: { + label: string; + cases: { + input: string; + output: { + root?: { + persons: T.Person[]; + aspects: T.Aspect[]; + }; + stem?: { + persons: T.Person[]; + aspects: T.Aspect[]; + }; + verb: T.VerbEntry; + }[]; + }[]; +}[] = [ + { + label: "with regular simple verbs", + cases: [ + { + input: "وهلم", + output: [ + { + root: { + persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: wahul, + }, + ], + }, + { + input: "وهم", + output: [ + { + root: { + persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + aspects: ["imperfective", "perfective"], + }, + stem: { + persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: wahul, + }, + ], + }, + { + input: "وهې", + output: [ + { + root: { + persons: [ + T.Person.SecondSingMale, + T.Person.SecondSingFemale, + T.Person.ThirdPlurFemale, + ], + aspects: ["imperfective", "perfective"], + }, + stem: { + persons: [T.Person.SecondSingMale, T.Person.SecondSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: wahul, + }, + ], + }, + { + input: "لیکم", + output: [ + { + root: { + persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + aspects: ["imperfective", "perfective"], + }, + stem: { + persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: leekul, + }, + ], + }, + { + input: "لیکلو", + output: [ + { + root: { + persons: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale], + aspects: ["imperfective", "perfective"], + }, + verb: leekul, + }, + ], + }, + { + input: "لیکل", + output: [ + { + root: { + persons: [T.Person.ThirdPlurMale], + aspects: ["imperfective", "perfective"], + }, + verb: leekul, + }, + ], + }, + { + input: "لیکلل", + output: [], + }, + { + input: "منله", + output: [ + { + root: { + persons: [T.Person.ThirdSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: manul, + }, + ], + }, + { + input: "مني", + output: [ + { + stem: { + persons: [ + T.Person.ThirdSingMale, + T.Person.ThirdSingFemale, + T.Person.ThirdPlurMale, + T.Person.ThirdPlurFemale, + ], + aspects: ["imperfective", "perfective"], + }, + verb: manul, + }, + ], + }, + { + input: "منئ", + output: [ + { + stem: { + persons: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], + aspects: ["imperfective", "perfective"], + }, + root: { + persons: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], + aspects: ["imperfective", "perfective"], + }, + verb: manul, + }, + ], + }, + ], + }, + { + label: "with regular intransitive verbs", + cases: [ + { + input: "رسېدلم", + output: [ + { + root: { + persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: rasedul, + }, + ], + }, + { + input: "رسېدم", + output: [ + { + root: { + persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: rasedul, + }, + ], + }, + { + input: "رسېږې", + output: [ + { + stem: { + persons: [T.Person.SecondSingMale, T.Person.SecondSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: rasedul, + }, + ], + }, + // short version of intransitive as well + { + input: "رسئ", + output: [ + { + stem: { + persons: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], + aspects: ["imperfective", "perfective"], + }, + verb: rasedul, + }, + ], + }, + ], + }, + { + label: "verbs with irregular stems", + cases: [ + { + input: "وینم", + output: [ + { + stem: { + persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: leedul, + }, + ], + }, + { + input: "وینم", + output: [ + { + stem: { + persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: leedul, + }, + ], + }, + { + input: "لیده", + output: [ + { + root: { + persons: [T.Person.ThirdSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: leedul, + }, + ], + }, + { + input: "خورې", + output: [ + { + stem: { + persons: [T.Person.SecondSingMale, T.Person.SecondSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: khorul, + }, + ], + }, + { + input: "خوړي", + output: [], + }, + { + input: "خوړم", + output: [ + { + root: { + persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: khorul, + }, + ], + }, + ], + }, + { + label: "verbs with seperating prefix", + cases: [ + { + input: "کېني", + output: [ + { + stem: { + persons: [ + T.Person.ThirdSingMale, + T.Person.ThirdSingFemale, + T.Person.ThirdPlurMale, + T.Person.ThirdPlurFemale, + ], + aspects: ["imperfective", "perfective"], + }, + verb: kenaastul, + }, + ], + }, + { + input: "نم", + output: [ + { + stem: { + persons: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + aspects: ["perfective"], + }, + verb: kenaastul, + }, + ], + }, + { + input: "پرېږدو", + output: [ + { + stem: { + persons: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale], + aspects: ["imperfective", "perfective"], + }, + verb: prexodul, + }, + ], + }, + { + input: "پرېښوده", + output: [ + { + root: { + persons: [T.Person.ThirdSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: prexodul, + }, + ], + }, + { + input: "ښودله", + output: [ + { + root: { + persons: [T.Person.ThirdSingFemale], + aspects: ["imperfective", "perfective"], + }, + verb: xodul, + }, + { + root: { + persons: [T.Person.ThirdSingFemale], + aspects: ["perfective"], + }, + verb: prexodul, + }, + { + root: { + persons: [T.Person.ThirdSingFemale], + aspects: ["perfective"], + }, + verb: kexodul, + }, + ], + }, + ], + }, +]; + +tests.forEach(({ label, cases }) => { + test(label, () => { + cases.forEach(({ input, output }) => { + const tokens = tokenizer(input); + const vbs = parseVerb(tokens, verbLookup).map((r) => r.body); + const madeVbsS = output.reduce[]>((acc, o) => { + return [ + ...acc, + ...(["root", "stem"] as const).flatMap((base) => + (o[base]?.aspects || []).flatMap((aspect) => + (o[base]?.persons || []).flatMap((person) => ({ + type: "VB" as const, + person, + info: { + type: "verb" as const, + aspect, + base, + verb: o.verb, + }, + })) + ) + ), + ]; + }, []); + expect(vbs).toIncludeSameMembers(madeVbsS); + }); + }); +}); diff --git a/src/lib/src/parsing/parse-verb.ts b/src/lib/src/parsing/parse-verb.ts index 8a6213d..01e3124 100644 --- a/src/lib/src/parsing/parse-verb.ts +++ b/src/lib/src/parsing/parse-verb.ts @@ -2,63 +2,183 @@ import * as T from "../../../types"; export function parseVerb( tokens: Readonly, - verbLookup: (s: (e: T.VerbDictionaryEntry) => boolean) => T.VerbEntry[] + verbLookup: (s: string) => T.VerbEntry[] ): T.ParseResult>[] { if (tokens.length === 0) { return []; } const [first, ...rest] = tokens; const people = getVerbEnding(first.s); - if (people.length === 0) { - return []; - } - const verbs = findByStem(first.s.slice(0, -1), verbLookup); - - return people.flatMap((person) => - verbs.map((verb) => ({ - tokens: rest, - body: { - type: "VB", - person, - info: { - type: "verb", - aspect: "imperfective", - base: "stem", - verb, - }, - }, - errors: [], - })) - ); + // TODO: can optimize this to not have to look for possible stems/roots if none + const verbs = verbLookup(first.s); + return matchVerbs(first.s, verbs, people).map((body) => ({ + tokens: rest, + body, + errors: [], + })); } -function getVerbEnding(p: string): T.Person[] { +function matchVerbs( + s: string, + entries: T.VerbEntry[], + people: { + root: T.Person[]; + stem: T.Person[]; + } +): Omit[] { + const w: Omit[] = []; + const base = s.endsWith("ل") ? s : s.slice(0, -1); + if (people.stem.length) { + const stemMatches = { + imperfective: entries.filter(({ entry: e }) => { + if (e.c.includes("comp")) { + return false; + } + if (e.psp) { + return e.psp === base; + } + if (e.c.includes("intrans.")) { + const miniRoot = e.p.slice(0, -3); + return miniRoot + "ېږ" === base || miniRoot === base; + } else { + return e.p.slice(0, -1) === base; + } + }), + perfective: entries.filter(({ entry: e }) => { + if (e.c.includes("comp")) { + return false; + } + if (e.ssp) { + const bSep = e.separationAtP ? e.ssp.slice(e.separationAtP) : ""; + return bSep === base || e.ssp === base; + } + if (e.psp) { + const bSep = e.separationAtP ? e.psp.slice(e.separationAtP) : ""; + return bSep === base || e.psp === base; + } + if (e.c.includes("intrans.")) { + const miniRoot = e.p.slice(0, -3); + return miniRoot + "ېږ" === base || miniRoot === base; + } else { + return e.p.slice(0, -1) === base; + } + }), + }; + Object.entries(stemMatches).forEach(([aspect, entries]) => { + entries.forEach((verb) => { + people.stem.forEach((person) => { + w.push({ + type: "VB", + person, + info: { + type: "verb", + aspect: aspect as T.Aspect, + base: "stem", + verb: verb, + }, + }); + }); + }); + }); + } + if (people.root.length) { + const rootMatches = { + imperfective: entries.filter( + ({ entry: e }) => + !e.c.includes("comp") && + (base === e.p || (!s.endsWith("ل") && base === e.p.slice(0, -1))) + ), + perfective: entries.filter(({ entry: e }) => { + if (e.c.includes("comp")) { + return false; + } + if (e.separationAtP) { + const bSep = e.p.slice(e.separationAtP); + return ( + base === bSep || + base === e.p || + (!s.endsWith("ل") && + (base === e.p.slice(0, -1) || base === bSep.slice(0, -1))) + ); + } else { + // TODO: perfective roots are so rare could optimize this with a couple of checks? + return e.prp + ? e.prp === base || e.prp.slice(0, -1) === base + : base === e.p || (!s.endsWith("ل") && base === e.p.slice(0, -1)); + } + }), + }; + Object.entries(rootMatches).forEach(([aspect, entries]) => { + entries.forEach((verb) => { + people.root.forEach((person) => { + w.push({ + type: "VB", + person, + info: { + type: "verb", + aspect: aspect as T.Aspect, + base: "root", + verb: verb, + }, + }); + }); + }); + }); + } + return w; +} + +function getVerbEnding(p: string): { + root: T.Person[]; + stem: T.Person[]; +} { if (p.endsWith("م")) { - return [T.Person.FirstSingMale, T.Person.FirstSingFemale]; + return { + root: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + stem: [T.Person.FirstSingMale, T.Person.FirstSingFemale], + }; } else if (p.endsWith("ې")) { - return [T.Person.SecondSingMale, T.Person.SecondSingFemale]; + return { + root: [ + T.Person.SecondSingMale, + T.Person.SecondSingFemale, + T.Person.ThirdPlurFemale, + ], + stem: [T.Person.SecondSingMale, T.Person.SecondSingFemale], + }; } else if (p.endsWith("ي")) { - return [ - T.Person.ThirdSingMale, - T.Person.ThirdSingFemale, - T.Person.ThirdPlurMale, - T.Person.ThirdPlurFemale, - ]; + return { + stem: [ + T.Person.ThirdSingMale, + T.Person.ThirdSingFemale, + T.Person.ThirdPlurMale, + T.Person.ThirdPlurFemale, + ], + root: [], + }; } else if (p.endsWith("و")) { - return [T.Person.FirstPlurMale, T.Person.FirstPlurFemale]; + return { + root: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale], + stem: [T.Person.FirstPlurMale, T.Person.FirstPlurFemale], + }; } else if (p.endsWith("ئ")) { - return [T.Person.SecondPlurMale, T.Person.SecondPlurFemale]; + return { + root: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], + stem: [T.Person.SecondPlurMale, T.Person.SecondPlurFemale], + }; + } else if (p.endsWith("ه")) { + return { + root: [T.Person.ThirdSingFemale], + stem: [], + }; + } else if (p.endsWith("ل")) { + return { + root: [T.Person.ThirdPlurMale], + stem: [], + }; } - return []; -} - -function findByStem( - stem: string, - verbLookup: (s: (e: T.VerbDictionaryEntry) => boolean) => T.VerbEntry[] -): T.VerbEntry[] { - return verbLookup( - (e) => - e.psp === stem || - (!e.psp && !e.c.includes("comp") && e.p.slice(0, -1) === stem) - ); + return { + root: [], + stem: [], + }; } diff --git a/src/lib/src/parsing/parse-vp.ts b/src/lib/src/parsing/parse-vp.ts index 3c24e66..7c3b41e 100644 --- a/src/lib/src/parsing/parse-vp.ts +++ b/src/lib/src/parsing/parse-vp.ts @@ -28,42 +28,167 @@ const kedulStat = vEntry({ ec: "become", }); +// cool examples: زه خوږې ماشومې وهم + export function parseVP( tokens: Readonly, lookup: (s: Partial) => T.DictionaryEntry[], - verbLookup: (s: (e: T.VerbDictionaryEntry) => boolean) => T.VerbEntry[] + verbLookup: (s: string) => T.VerbEntry[] ): T.ParseResult[] { if (tokens.length === 0) { return []; } // how to make this into a nice pipeline... 🤔 const NP1 = parseNP(tokens, lookup).filter(({ errors }) => !errors.length); - const NP2 = bindParseResult( - NP1, - (tokens) => parseNP(tokens, lookup), - true - ).filter(({ errors }) => !errors.length); - const vb = bindParseResult( - NP2, - (tokens) => parseVerb(tokens, verbLookup), - true - ).filter(({ errors }) => !errors.length); + const NP2 = bindParseResult< + { + inflected: boolean; + selection: T.NPSelection; + }, + { + np1: { + inflected: boolean; + selection: T.NPSelection; + }; + np2: + | { + inflected: boolean; + selection: T.NPSelection; + } + | undefined; + } + >(NP1, (tokens, np1) => { + const np2s = parseNP(tokens, lookup); + if (!np2s.length) { + const r: T.ParseResult<{ + np1: { + inflected: boolean; + selection: T.NPSelection; + }; + np2: undefined; + }>[] = [ + { + tokens, + body: { + np1, + np2: undefined, + }, + errors: [], + }, + ]; + return r; + } + return np2s.map((p) => ({ + tokens: p.tokens, + body: { + np1, + np2: p.body, + }, + errors: p.errors, + })); + }).filter(({ errors }) => !errors.length); + const vb = bindParseResult(NP2, (tokens, nps) => { + const vb = parseVerb(tokens, verbLookup); + // TODO make a nice functor that just maps or adds in the body + return vb.map((p) => ({ + tokens: p.tokens, + body: { + np2: nps.np2, + v: p.body, + np1: nps.np1, + }, + errors: p.errors, + })); + }).filter(({ errors }) => !errors.length); // TODO: be able to bind mulitple vals - return bindParseResult, T.VPSelectionComplete>( - vb, - (tokens, v) => { - const w: T.ParseResult[] = []; - NP1.forEach(({ body: np1 }) => { - NP2.forEach(({ body: np2 }) => { - // NICE TODO: IF there's an error in any of the NPS, don't try - // to make the VPS - just show them that error - // for that we probably need a different type of - [ - [np1, np2], - [np2, np1], - ].forEach(([s, o]) => { - const errors: T.ParseError[] = []; - const subjPerson = getPersonFromNP(s.selection); + return bindParseResult(vb, (tokens, { np1, np2, v }) => { + const w: T.ParseResult[] = []; + const isPast = v.info.type === "verb" && v.info.base === "root"; + const intransitive = + v.info.type === "verb" && v.info.verb.entry.c.includes("intrans."); + + if (!np2) { + const errors: T.ParseError[] = []; + const s = np1; + if (!intransitive) { + return []; + } + if (s.inflected) { + errors.push({ + message: "subject of intransitive verb should not be inflected", + }); + } + if (getPersonFromNP(s.selection) !== v.person) { + errors.push({ + message: "subject should agree with intransitive verb", + }); + } + const blocks: T.VPSBlockComplete[] = [ + { + key: 1, + block: makeSubjectSelectionComplete(s.selection), + }, + { + key: 2, + block: { + type: "objectSelection", + selection: "none", + }, + }, + ]; + const verb: T.VerbSelectionComplete = { + type: "verb", + verb: v.info.type === "verb" ? v.info.verb : kedulStat, + transitivity: "intransitive", + canChangeTransitivity: false, + canChangeStatDyn: false, + negative: false, + tense: isPast ? "imperfectivePast" : "presentVerb", + canChangeVoice: true, + isCompound: false, + voice: "active", + }; + w.push({ + tokens, + body: { + blocks, + verb, + externalComplement: undefined, + form: { + removeKing: false, + shrinkServant: false, + }, + }, + errors, + }); + } else { + [[np1, np2, false] as const, [np2, np1, true] as const].forEach( + ([s, o, reversed]) => { + const subjPerson = getPersonFromNP(s.selection); + const errors: T.ParseError[] = []; + if (intransitive) { + return []; + } + + if (isPast) { + if (getPersonFromNP(o.selection) !== v.person) { + errors.push({ + message: "transitive past tense verb does not match object", + }); + } else { + if (!s.inflected) { + errors.push({ + message: "transitive past tense subject should be inflected", + }); + } + if (o.inflected) { + errors.push({ + message: + "transitive past tense object should not be inflected", + }); + } + } + } else { if (getPersonFromNP(s.selection) !== v.person) { errors.push({ message: "verb does not match subject", @@ -83,46 +208,49 @@ export function parseVP( errors.push({ message: "object should not be inflected" }); } } + } - const blocks: T.VPSBlockComplete[] = [ - { - key: 1, - block: makeSubjectSelectionComplete(s.selection), + const blocks: T.VPSBlockComplete[] = [ + { + key: 1, + block: makeSubjectSelectionComplete(s.selection), + }, + { + key: 2, + block: makeObjectSelectionComplete(o.selection), + }, + ]; + if (reversed) { + blocks.reverse(); + } + const verb: T.VerbSelectionComplete = { + type: "verb", + verb: v.info.type === "verb" ? v.info.verb : kedulStat, + transitivity: "transitive", + canChangeTransitivity: false, + canChangeStatDyn: false, + negative: false, + tense: isPast ? "imperfectivePast" : "presentVerb", + canChangeVoice: true, + isCompound: false, + voice: "active", + }; + w.push({ + tokens, + body: { + blocks, + verb, + externalComplement: undefined, + form: { + removeKing: false, + shrinkServant: false, }, - { - key: 2, - block: makeObjectSelectionComplete(o.selection), - }, - ]; - const verb: T.VerbSelectionComplete = { - type: "verb", - verb: v.info.type === "verb" ? v.info.verb : kedulStat, - transitivity: "transitive", - canChangeTransitivity: false, - canChangeStatDyn: false, - negative: false, - tense: "presentVerb", - canChangeVoice: true, - isCompound: false, - voice: "active", - }; - w.push({ - tokens, - body: { - blocks, - verb, - externalComplement: undefined, - form: { - removeKing: false, - shrinkServant: false, - }, - }, - errors, - }); + }, + errors, }); - }); - }); - return w; + } + ); } - ); + return w; + }); } diff --git a/src/setupTests.js b/src/setupTests.js index 8f2609b..43e75a1 100644 --- a/src/setupTests.js +++ b/src/setupTests.js @@ -2,4 +2,8 @@ // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; +import "@testing-library/jest-dom"; + +// add all jest-extended matchers +import * as matchers from "jest-extended"; +expect.extend(matchers); diff --git a/tsconfig.json b/tsconfig.json index 72b911c..258e494 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,6 +22,7 @@ "jsx": "react-jsx" }, "include": [ - "src" + "src", + "testSetup.js" ] } diff --git a/yarn.lock b/yarn.lock index dcc530c..e5cfd5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1433,6 +1433,13 @@ optionalDependencies: node-notifier "^8.0.0" +"@jest/schemas@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" + integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== + dependencies: + "@sinclair/typebox" "^0.27.8" + "@jest/source-map@^26.6.2": version "26.6.2" resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz" @@ -1640,6 +1647,11 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz" @@ -4283,6 +4295,11 @@ diff-sequences@^27.0.6: resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.0.6.tgz" integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ== +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz" @@ -6555,6 +6572,16 @@ jest-diff@^27.0.0: jest-get-type "^27.0.6" pretty-format "^27.2.4" +jest-diff@^29.0.0: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.2.tgz#c36001e5543e82a0805051d3ceac32e6825c1c46" + integrity sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.6.2" + jest-docblock@^26.0.0: version "26.0.0" resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz" @@ -6598,6 +6625,14 @@ jest-environment-node@^26.6.2: jest-mock "^26.6.2" jest-util "^26.6.2" +jest-extended@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jest-extended/-/jest-extended-4.0.1.tgz#2315cb5914fc132e5acd07945bfaa01aac3947c2" + integrity sha512-KM6dwuBUAgy6QONuR19CGubZB9Hkjqvl/d5Yc/FXsdB8+gsGxB2VQ+NEdOrr95J4GMPeLnDoPOKyi6+mKCCnZQ== + dependencies: + jest-diff "^29.0.0" + jest-get-type "^29.0.0" + jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz" @@ -6608,6 +6643,11 @@ jest-get-type@^27.0.6: resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.0.6.tgz" integrity sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg== +jest-get-type@^29.0.0, jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== + jest-haste-map@^26.6.2: version "26.6.2" resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz" @@ -9092,6 +9132,15 @@ pretty-format@^27.0.0, pretty-format@^27.2.4: ansi-styles "^5.0.0" react-is "^17.0.1" +pretty-format@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.2.tgz#3d5829261a8a4d89d8b9769064b29c50ed486a47" + integrity sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg== + dependencies: + "@jest/schemas" "^29.6.0" + ansi-styles "^5.0.0" + react-is "^18.0.0" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" @@ -9396,6 +9445,11 @@ react-is@^17.0.1: resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz"