diff --git a/frontend/__tests__/components/modals/WordFilterModal.spec.ts b/frontend/__tests__/components/modals/WordFilterModal.spec.ts new file mode 100644 index 000000000000..9667cc5f1c12 --- /dev/null +++ b/frontend/__tests__/components/modals/WordFilterModal.spec.ts @@ -0,0 +1,49 @@ +import { describe, expect, it } from "vitest"; +import { filterWordList } from "../../../src/ts/components/modals/WordFilterModal"; + +const exactMatchFilter = { + include: "a r s t d h n e i o '", + exclude: "", + minLength: "", + maxLength: "", + regex: "", + exactMatch: true, +}; + +describe("filterWordList", () => { + it("filters against the British spelling", () => { + expect(filterWordList(exactMatchFilter, ["tire"], true)).toEqual({ + words: [], + }); + }); + + it("keeps the American spelling when British English is disabled", () => { + expect(filterWordList(exactMatchFilter, ["tire"])).toEqual({ + words: ["tire"], + }); + }); + + it("keeps the original word when its British spelling matches", () => { + expect( + filterWordList( + { ...exactMatchFilter, include: "m a t h s" }, + ["math"], + true, + ), + ).toEqual({ words: ["math"] }); + }); + + it("applies length filters to the British spelling", () => { + expect( + filterWordList( + { + ...exactMatchFilter, + include: "m a t h s", + maxLength: "4", + }, + ["math"], + true, + ), + ).toEqual({ words: [] }); + }); +}); diff --git a/frontend/__tests__/test/british-english.spec.ts b/frontend/__tests__/test/british-english.spec.ts index e52bf74131f6..724aa2aea81a 100644 --- a/frontend/__tests__/test/british-english.spec.ts +++ b/frontend/__tests__/test/british-english.spec.ts @@ -6,67 +6,61 @@ describe("british-english", () => { describe("replace", () => { beforeEach(() => (Config.mode = "time")); - it("should not replace words with no rule", async () => { - await expect(replace("test", "")).resolves.toEqual("test"); - await expect(replace("Test", "")).resolves.toEqual("Test"); + it("should not replace words with no rule", () => { + expect(replace("test", "")).toEqual("test"); + expect(replace("Test", "")).toEqual("Test"); }); - it("should replace words", async () => { - await expect(replace("math", "")).resolves.toEqual("maths"); - await expect(replace("Math", "")).resolves.toEqual("Maths"); + it("should replace words", () => { + expect(replace("math", "")).toEqual("maths"); + expect(replace("Math", "")).toEqual("Maths"); }); - it("should replace words with non-word characters around", async () => { - await expect(replace(" :math-. ", "")).resolves.toEqual(" :maths-. "); - await expect(replace(" :Math-. ", "")).resolves.toEqual(" :Maths-. "); + it("should replace words with non-word characters around", () => { + expect(replace(" :math-. ", "")).toEqual(" :maths-. "); + expect(replace(" :Math-. ", "")).toEqual(" :Maths-. "); }); - it("should not replace in quote mode if previousWord matches excepted words", async () => { + it("should not replace in quote mode if previousWord matches excepted words", () => { //GIVEN Config.mode = "quote"; //WHEN/THEN - await expect(replace("tire", "will")).resolves.toEqual("tire"); - await expect(replace("tire", "")).resolves.toEqual("tyre"); + expect(replace("tire", "will")).toEqual("tire"); + expect(replace("tire", "")).toEqual("tyre"); }); - it("should replace hyphenated words", async () => { - await expect(replace("cream-colored", "")).resolves.toEqual( - "cream-coloured", - ); - await expect(replace("armor-flavoring", "")).resolves.toEqual( - "armour-flavouring", - ); + it("should replace hyphenated words", () => { + expect(replace("cream-colored", "")).toEqual("cream-coloured"); + expect(replace("armor-flavoring", "")).toEqual("armour-flavouring"); }); - it("should convert double quotes to single quotes", async () => { - await expect(replace('"hello"', "")).resolves.toEqual("'hello'"); - await expect(replace('"test"', "")).resolves.toEqual("'test'"); - await expect(replace('"Hello World"', "")).resolves.toEqual( - "'Hello World'", - ); + it("should convert double quotes to single quotes", () => { + expect(replace('"hello"', "")).toEqual("'hello'"); + expect(replace('"test"', "")).toEqual("'test'"); + expect(replace('"Hello World"', "")).toEqual("'Hello World'"); }); - it("should convert double quotes and replace words", async () => { - await expect(replace('"color"', "")).resolves.toEqual("'colour'"); - await expect(replace('"math"', "")).resolves.toEqual("'maths'"); - await expect(replace('"Color"', "")).resolves.toEqual("'Colour'"); + it("should convert double quotes and replace words", () => { + expect(replace('"color"', "")).toEqual("'colour'"); + expect(replace('"math"', "")).toEqual("'maths'"); + expect(replace('"Color"', "")).toEqual("'Colour'"); }); - it("should handle multiple double quotes in a word", async () => { - await expect( - replace('He said "hello" and "goodbye"', ""), - ).resolves.toEqual("He said 'hello' and 'goodbye'"); + it("should handle multiple double quotes in a word", () => { + expect(replace('He said "hello" and "goodbye"', "")).toEqual( + "He said 'hello' and 'goodbye'", + ); }); - it("should not affect words without double quotes", async () => { - await expect(replace("'hello'", "")).resolves.toEqual("'hello'"); - await expect(replace("test", "")).resolves.toEqual("test"); + it("should not affect words without double quotes", () => { + expect(replace("'hello'", "")).toEqual("'hello'"); + expect(replace("test", "")).toEqual("test"); }); - it("ignores prototype-related property names (e.g. constructor, __proto__)", async () => { - await expect(replace("constructor", "")).resolves.toEqual("constructor"); - await expect(replace("__proto__", "")).resolves.toEqual("__proto__"); + it("ignores prototype-related property names (e.g. constructor, __proto__)", () => { + expect(replace("constructor", "")).toEqual("constructor"); + expect(replace("__proto__", "")).toEqual("__proto__"); }); }); }); diff --git a/frontend/src/ts/components/modals/WordFilterModal.tsx b/frontend/src/ts/components/modals/WordFilterModal.tsx index 4380c82cc815..a2130295d2b8 100644 --- a/frontend/src/ts/components/modals/WordFilterModal.tsx +++ b/frontend/src/ts/components/modals/WordFilterModal.tsx @@ -11,6 +11,7 @@ import { Setter, } from "solid-js"; +import { Config } from "../../config/store"; import { LanguageList } from "../../constants/languages"; import { LayoutsList } from "../../constants/layouts"; import { createDebouncedSignal } from "../../hooks/createDebouncedSignal"; @@ -20,6 +21,7 @@ import { showNoticeNotification, showErrorNotification, } from "../../states/notifications"; +import * as BritishEnglish from "../../test/british-english"; import * as JSONData from "../../utils/json-data"; import * as Misc from "../../utils/misc"; import { AnimatedModal } from "../common/AnimatedModal"; @@ -122,9 +124,10 @@ type FilterFormValues = { type FilterResult = { words: string[] } | { error: string }; -function filterWordList( +export function filterWordList( value: FilterFormValues, words: string[], + useBritishEnglish = false, ): FilterResult { const exactMatchOnly = value.exactMatch; @@ -162,16 +165,19 @@ function filterWordList( const filteredWords: string[] = []; for (const word of words) { - const testincl = regincl.test(word); + const wordToTest = useBritishEnglish + ? BritishEnglish.replace(word, undefined) + : word; + const testincl = regincl.test(wordToTest); const testexcl = - exactMatchOnly || filterout === "" ? false : regexcl.test(word); - const testlit = exactMatchOnly ? true : reglit.test(word); + exactMatchOnly || filterout === "" ? false : regexcl.test(wordToTest); + const testlit = exactMatchOnly ? true : reglit.test(wordToTest); if ( testincl && !testexcl && testlit && - word.length <= max && - word.length >= min + wordToTest.length <= max && + wordToTest.length >= min ) { filteredWords.push(word); } @@ -189,6 +195,9 @@ export function WordFilterModal(props: { let submitAction: "set" | "add" = "set"; + const useBritishEnglish = () => + Config.britishEnglish && Config.language.includes("english"); + const form = createForm(() => ({ defaultValues: { include: "", @@ -211,7 +220,11 @@ export function WordFilterModal(props: { return; } - const result = filterWordList(value, languageWordList.words); + const result = filterWordList( + value, + languageWordList.words, + useBritishEnglish(), + ); if ("error" in result) { showNoticeNotification(result.error); return; @@ -248,7 +261,7 @@ export function WordFilterModal(props: { const matchResult = createMemo(() => { const words = languageWords(); if (words === null || words === undefined) return null; - return filterWordList(debouncedValues(), words); + return filterWordList(debouncedValues(), words, useBritishEnglish()); }); const applyPreset = async () => { diff --git a/frontend/src/ts/test/british-english.ts b/frontend/src/ts/test/british-english.ts index 00b32b5ea5e1..3b2e294698d7 100644 --- a/frontend/src/ts/test/british-english.ts +++ b/frontend/src/ts/test/british-english.ts @@ -2,10 +2,10 @@ import { Config } from "../config/store"; import britishEnglishReplacements from "../constants/british-english"; import { capitalizeFirstLetterOfEachWord } from "../utils/strings"; -export async function replace( +export function replace( word: string, previousWord: string | undefined, -): Promise { +): string { // Convert American-style double quotes to British-style single quotes if (word.includes('"')) { word = word.replace(/"/g, "'"); @@ -14,11 +14,10 @@ export async function replace( if (word.includes("-")) { //this handles hyphenated words (for example "cream-colored") to make sure //we don't have to add every possible combination to the list - return ( - await Promise.all( - word.split("-").map(async (w) => replace(w, previousWord)), - ) - ).join("-"); + return word + .split("-") + .map((wordPart) => replace(wordPart, previousWord)) + .join("-"); } else { const cleanedWord = word.replace(/^[\W]+|[\W]+$/g, "").toLowerCase(); if ( diff --git a/frontend/src/ts/test/words-generator.ts b/frontend/src/ts/test/words-generator.ts index e0786537b26d..695318a4091a 100644 --- a/frontend/src/ts/test/words-generator.ts +++ b/frontend/src/ts/test/words-generator.ts @@ -372,10 +372,10 @@ function applyFunboxesToWord( return word; } -async function applyBritishEnglishToWord( +function applyBritishEnglishToWord( word: string, previousWord: string | undefined, -): Promise { +): string { if (!Config.britishEnglish) return word; if (!Config.language.includes("english")) return word; const currentQuote = getCurrentQuote(); @@ -387,7 +387,7 @@ async function applyBritishEnglishToWord( return word; } - return await BritishEnglish.replace(word, previousWord); + return BritishEnglish.replace(word, previousWord); } function applyLazyModeToWord(word: string, language: LanguageObject): string { @@ -952,7 +952,7 @@ export async function getNextWord( ); } - randomWord = await applyBritishEnglishToWord(randomWord, previousWordRaw); + randomWord = applyBritishEnglishToWord(randomWord, previousWordRaw); if (Config.numbers) { if (random() < 0.1) {