From fe666d5250bfd98809759c4db53f1edfb3981ff7 Mon Sep 17 00:00:00 2001 From: Noel Chou Date: Mon, 16 Feb 2026 11:13:17 -0500 Subject: [PATCH] BL-15882_reopen_to_macrolanguage --- .../find-language/languageSearch.spec.ts | 61 +++- .../find-language/languageTagUtils.spec.ts | 13 + .../common/find-language/searchForLanguage.ts | 56 +--- .../searchResultModifiers.spec.ts | 290 +++++++++--------- .../find-language/searchResultModifiers.ts | 10 +- .../e2e/languageSearch.e2e.ts | 22 ++ .../e2e/languageTag.e2e.ts | 32 ++ 7 files changed, 275 insertions(+), 209 deletions(-) diff --git a/components/language-chooser/common/find-language/languageSearch.spec.ts b/components/language-chooser/common/find-language/languageSearch.spec.ts index 4f910b6d..5a7955bc 100644 --- a/components/language-chooser/common/find-language/languageSearch.spec.ts +++ b/components/language-chooser/common/find-language/languageSearch.spec.ts @@ -376,20 +376,20 @@ describe("getLanguageBySubtag", () => { it("should find languages by valid languageSubtag field", () => { expect(getLanguageBySubtag("aaa")?.exonym).toEqual("Ghotuo"); expect(getLanguageBySubtag("ab")?.exonym).toEqual("Abkhaz"); - expect(getLanguageBySubtag("uz")?.exonym).toEqual("Uzbek"); - expect(getLanguageBySubtag("mg")?.iso639_3_code).toEqual("plt"); + expect(getLanguageBySubtag("uz")?.iso639_3_code).toEqual("uzb"); + expect(getLanguageBySubtag("mg")?.iso639_3_code).toEqual("mlg"); expect(getLanguageBySubtag("zh")?.exonym).toEqual("Chinese"); expect(getLanguageBySubtag("za")?.exonym).toEqual("Zhuang"); - expect(getLanguageBySubtag("bnc")?.iso639_3_code).toEqual("lbk"); + expect(getLanguageBySubtag("bnc")?.iso639_3_code).toEqual("bnc"); expect(getLanguageBySubtag("no")?.exonym).toEqual("Norwegian"); expect(getLanguageBySubtag("sh")?.iso639_3_code).toEqual("hbs"); + expect(getLanguageBySubtag("hbs")?.iso639_3_code).toEqual("hbs"); expect(getLanguageBySubtag("sa")?.exonym).toEqual("Sanskrit"); expect(getLanguageBySubtag("zap")?.exonym).toEqual("Zapotec"); - expect(getLanguageBySubtag("ik")?.iso639_3_code).toEqual("esk"); + expect(getLanguageBySubtag("ik")?.iso639_3_code).toEqual("ipk"); expect(getLanguageBySubtag("id")?.exonym).toEqual("Indonesian"); expect(getLanguageBySubtag("ja")?.exonym).toEqual("Japanese"); - expect(getLanguageBySubtag("yi")?.autonym).toEqual("יידיש"); - expect(getLanguageBySubtag("luy")?.iso639_3_code).toEqual("bxk"); + expect(getLanguageBySubtag("luy")?.iso639_3_code).toEqual("luy"); }); it("should find languages using the defaultSearchResultModifier", () => { expect( @@ -401,19 +401,25 @@ describe("getLanguageBySubtag", () => { // The exonym for uz gets demarcated as [Uz]bek, so have to check different field expect( getLanguageBySubtag("uz", defaultSearchResultModifier)?.iso639_3_code - ).toEqual("uzn"); + ).toEqual("uzb"); expect( getLanguageBySubtag("mg", defaultSearchResultModifier)?.iso639_3_code - ).toEqual("plt"); + ).toEqual("mlg"); + const chineseResult = getLanguageBySubtag( + "zh", + defaultSearchResultModifier + ); + expect(chineseResult?.exonym).toEqual("Chinese"); + expect(chineseResult?.names.length).toBeGreaterThan(3); expect( - getLanguageBySubtag("zh", defaultSearchResultModifier)?.exonym - ).toEqual("Chinese"); + getLanguageBySubtag("es", defaultSearchResultModifier)?.exonym + ).toEqual("Spanish"); expect( getLanguageBySubtag("za", defaultSearchResultModifier)?.exonym ).toEqual("Zhuang"); expect( getLanguageBySubtag("bnc", defaultSearchResultModifier)?.iso639_3_code - ).toEqual("lbk"); + ).toEqual("bnc"); expect( getLanguageBySubtag("no", defaultSearchResultModifier)?.exonym ).toEqual("Norwegian"); @@ -421,14 +427,14 @@ describe("getLanguageBySubtag", () => { getLanguageBySubtag("sh", defaultSearchResultModifier)?.iso639_3_code ).toEqual("hbs"); expect( - getLanguageBySubtag("sa", defaultSearchResultModifier)?.exonym - ).toEqual("Sanskrit"); + getLanguageBySubtag("hbs", defaultSearchResultModifier)?.iso639_3_code + ).toEqual("hbs"); expect( getLanguageBySubtag("zap", defaultSearchResultModifier)?.exonym ).toEqual("Zapotec"); expect( getLanguageBySubtag("ik", defaultSearchResultModifier)?.iso639_3_code - ).toEqual("esk"); + ).toEqual("ipk"); expect( getLanguageBySubtag("id", defaultSearchResultModifier)?.exonym ).toEqual("Indonesian"); @@ -436,11 +442,34 @@ describe("getLanguageBySubtag", () => { getLanguageBySubtag("ja", defaultSearchResultModifier)?.exonym ).toEqual("Japanese"); expect( - getLanguageBySubtag("yi", defaultSearchResultModifier)?.autonym + getLanguageBySubtag("ydd", defaultSearchResultModifier)?.autonym ).toEqual("יידיש"); + expect( + getLanguageBySubtag("yi", defaultSearchResultModifier)?.exonym + ).toEqual("Yiddish"); expect( getLanguageBySubtag("luy", defaultSearchResultModifier)?.iso639_3_code - ).toEqual("bxk"); + ).toEqual("luy"); + expect( + getLanguageBySubtag("ak", defaultSearchResultModifier)?.exonym + ).toEqual("Akan"); + expect( + getLanguageBySubtag("sa", defaultSearchResultModifier)?.exonym + ).toEqual("Sanskrit"); + + // Because we only demarcate one part of the card, it just so happens that only in these couple cases do we find + // demarcation on the field we are testing. + // Enhance: strip demarcation in all the tests in this group + expect( + stripDemarcation( + getLanguageBySubtag("aka", defaultSearchResultModifier)?.exonym + ) + ).toEqual("Akan"); + expect( + stripDemarcation( + getLanguageBySubtag("san", defaultSearchResultModifier)?.exonym + ) + ).toEqual("Sanskrit"); }); it("should use searchResultModifier if provided", () => { const foobar = "foobar"; diff --git a/components/language-chooser/common/find-language/languageTagUtils.spec.ts b/components/language-chooser/common/find-language/languageTagUtils.spec.ts index 55081957..9eae6721 100644 --- a/components/language-chooser/common/find-language/languageTagUtils.spec.ts +++ b/components/language-chooser/common/find-language/languageTagUtils.spec.ts @@ -414,6 +414,19 @@ describe("Tag parsing", () => { expect(uznResult?.language?.iso639_3_code).toEqual("uzn"); expect(uznResult?.script?.name).toEqual("Latin"); }); + + it("should handle normal tags for Chinese correctly", () => { + const zhCnResult = parseLangtagFromLangChooser("zh-CN"); + expect(zhCnResult?.language?.exonym).toEqual("Chinese"); + expect(zhCnResult?.language?.names.length).toBeGreaterThan(3); + expect(zhCnResult?.script?.code).toEqual("Hans"); + + // should be case insensitive + const zhTwResult = parseLangtagFromLangChooser("zH-TW"); + expect(zhTwResult?.language?.exonym).toEqual("Chinese"); + expect(zhTwResult?.language?.names.length).toBeGreaterThan(3); + expect(zhTwResult?.script?.code).toEqual("Hant"); + }); }); describe("defaultRegionForLangTag", () => { diff --git a/components/language-chooser/common/find-language/searchForLanguage.ts b/components/language-chooser/common/find-language/searchForLanguage.ts index add05059..61b76dd1 100644 --- a/components/language-chooser/common/find-language/searchForLanguage.ts +++ b/components/language-chooser/common/find-language/searchForLanguage.ts @@ -98,53 +98,23 @@ export function getLanguageBySubtag( searchString: string ) => ILanguage[] ): ILanguage | undefined { - const languages = rawLanguages as ILanguage[]; - /* If the code is used for both a macrolanguage and the representative language (macrolanguageNotes.md), - return the representative language by default (BL-14824). */ - const macrolanguageRepFuse = new Fuse(languages as ILanguage[], { - keys: [ - "parentMacrolanguage.languageSubtag", - "parentMacrolanguage.iso639_3_code", - "isRepresentativeForMacrolanguage", - ], + // For the Chinese special case, we actually want the card with iso639_3_code cmn (the individual language card) + // because it has all the data on it. At this stage it doesn't have the tag zh which we actually want; that will be + // handled + // by the searchResultModifier + const correctedCode = code.toLowerCase() === "zh" ? "cmn" : code; + const fuse = new Fuse(rawLanguages as ILanguage[], { + keys: ["languageSubtag", "iso639_3_code"], threshold: 0, // exact matches only useExtendedSearch: true, }); - let rawResults = macrolanguageRepFuse.search({ - $and: [ - { - $or: [ - { "parentMacrolanguage.languageSubtag": "=" + code }, - { "parentMacrolanguage.iso639_3_code": "=" + code }, - ], - }, - { isRepresentativeForMacrolanguage: "=true" }, - ], - }); - - if (rawResults.length > 1) - console.error( - "Unexpectedly found multiple representative languages for " + - code + - ": " + - rawResults.map((r) => r.item.iso639_3_code).join(", ") - ); - - /* If search for code didn't find exactly one representative language for a macrolanguage, - do normal language search instead */ - if (rawResults.length !== 1) { - const fuse = new Fuse(languages as ILanguage[], { - keys: ["languageSubtag", "iso639_3_code"], - threshold: 0, // exact matches only - useExtendedSearch: true, - }); - rawResults = fuse.search("=" + code); // exact match - } - - const result = rawResults[0]?.item; + const rawResults = fuse.search(`="${correctedCode}"`); return searchResultModifier - ? searchResultModifier([result], code)[0] - : result; + ? searchResultModifier( + rawResults.map((r) => r.item), + code + )[0] + : rawResults[0]?.item; } // This is not a comprehensive language tag parser. It's just built to parse the diff --git a/components/language-chooser/common/find-language/searchResultModifiers.spec.ts b/components/language-chooser/common/find-language/searchResultModifiers.spec.ts index 214870be..4a76739c 100644 --- a/components/language-chooser/common/find-language/searchResultModifiers.spec.ts +++ b/components/language-chooser/common/find-language/searchResultModifiers.spec.ts @@ -171,162 +171,162 @@ describe("reordering entries to prioritize desired language when keywords are se ); expect(rawIsoCode(reorderedResults[0])).toEqual("tpi"); }); +}); - describe("Chinese should be handled reasonably", () => { - let chineseResults: ILanguage[]; - beforeAll(async () => { - const chineseSearchString = "chinese"; - chineseResults = defaultSearchResultModifier( - (await asyncGetAllLanguageResults(chineseSearchString)) as ILanguage[], - chineseSearchString - ); - }); - it("top chinese result should have language subtag zh", () => { - expect(chineseResults[0].languageSubtag).toEqual("zh"); - }); - it("should only have one zh result", () => { - expect( - chineseResults.filter((r) => r.languageSubtag === "zh").length - ).toEqual(1); - }); - it("zh result should have many alternative names listed", () => { - expect( - chineseResults.find((r) => r.languageSubtag === "zh")?.names.length - ).toBeGreaterThan(10); - }); +describe("Chinese should be handled reasonably", () => { + let chineseResults: ILanguage[]; + beforeAll(async () => { + const chineseSearchString = "chinese"; + chineseResults = defaultSearchResultModifier( + (await asyncGetAllLanguageResults(chineseSearchString)) as ILanguage[], + chineseSearchString + ); + }); + it("top chinese result should have language subtag zh", () => { + expect(chineseResults[0].languageSubtag).toEqual("zh"); }); + it("should only have one zh result", () => { + expect( + chineseResults.filter((r) => r.languageSubtag === "zh").length + ).toEqual(1); + }); + it("zh result should have many alternative names listed", () => { + expect( + chineseResults.find((r) => r.languageSubtag === "zh")?.names.length + ).toBeGreaterThan(10); + }); +}); - describe("Spanish name listings should be handled as desired", () => { - it("finds spanish", async () => { - const spanishSearchString = "spanish"; - const spanishResult = defaultSearchResultModifier( - (await asyncGetAllLanguageResults(spanishSearchString)) as ILanguage[], - spanishSearchString - )[0]; - expect(rawIsoCode(spanishResult)).toEqual("spa"); +describe("Spanish name listings should be handled as desired", () => { + it("finds spanish", async () => { + const spanishSearchString = "spanish"; + const spanishResult = defaultSearchResultModifier( + (await asyncGetAllLanguageResults(spanishSearchString)) as ILanguage[], + spanishSearchString + )[0]; + expect(rawIsoCode(spanishResult)).toEqual("spa"); - // Español should be the autonym and not in the names list - expect(spanishResult?.autonym).toEqual("español"); - // Castellano should be in the names list - expect( - spanishResult?.names.filter((n) => stripDemarcation(n) === "castellano") - .length - ).toEqual(1); - // make sure we didn't accidentally prepend an empty name instead of castellano - expect(spanishResult?.names[0]).toBeTruthy(); - }); + // Español should be the autonym and not in the names list + expect(spanishResult?.autonym).toEqual("español"); + // Castellano should be in the names list + expect( + spanishResult?.names.filter((n) => stripDemarcation(n) === "castellano") + .length + ).toEqual(1); + // make sure we didn't accidentally prepend an empty name instead of castellano + expect(spanishResult?.names[0]).toBeTruthy(); + }); +}); + +// I'm not sure if this is the behavior we want to stick with, but putting it here to document it +// and so we notice if we accidentally change it. See macrolanguageNotes.md for more details. +describe("Anomalous special case handling", () => { + it("should handle Akan as expected", async () => { + // one "aka" card, is macrolanguage, has multiple names + const akaResults = defaultSearchResultModifier( + (await asyncGetAllLanguageResults("Akan")) as ILanguage[], + "Akan" + ); + const akaResult = akaResults.find((result) => + codeMatches(result.iso639_3_code, "aka") + ); + expect(akaResult).toBeDefined(); + // Not marking Akan as a macrolanguage, so we don't show the "better to pick an individual language" warning + // when there are no relevant individual language options + expect(akaResult?.isMacrolanguage).toBe(false); + // language subtag should be "ak" + expect(akaResult?.languageSubtag).toBe("ak"); + // should have more than one script option + expect(akaResult?.scripts.length).toBeGreaterThan(1); + // Currently we don't have enough info for any of the akan individual languages + expect( + akaResults.some( + (result) => + codeMatches(result.iso639_3_code, "twi") || + codeMatches(result.iso639_3_code, "fat") || + result.languageSubtag === "tw" + ) + ).toBe(false); }); - // I'm not sure if this is the behavior we want to stick with, but putting it here to document it - // and so we notice if we accidentally change it. See macrolanguageNotes.md for more details. - describe("Anomalous special case handling", () => { - it("should handle Akan as expected", async () => { - // one "aka" card, is macrolanguage, has multiple names - const akaResults = defaultSearchResultModifier( - (await asyncGetAllLanguageResults("Akan")) as ILanguage[], - "Akan" - ); - const akaResult = akaResults.find((result) => - codeMatches(result.iso639_3_code, "aka") - ); - expect(akaResult).toBeDefined(); - // Not marking Akan as a macrolanguage, so we don't show the "better to pick an individual language" warning - // when there are no relevant individual language options - expect(akaResult?.isMacrolanguage).toBe(false); - // language subtag should be "ak" - expect(akaResult?.languageSubtag).toBe("ak"); - // should have more than one script option - expect(akaResult?.scripts.length).toBeGreaterThan(1); - // Currently we don't have enough info for any of the akan individual languages + it("should handle Serbo-Croatian as expected", async () => { + // one "hbs" card, is macrolanguage, has multiple names + const hbsResults = defaultSearchResultModifier( + (await asyncGetAllLanguageResults("Serbo-Croatian")) as ILanguage[], + "Serbo-Croatian" + ); + const hbsResult = hbsResults.find((result) => + codeMatches(result.iso639_3_code, "hbs") + ); + expect(hbsResult).toBeDefined(); + expect(hbsResult?.isMacrolanguage).toBe(true); + expect(hbsResult?.names.length).toBe(0); + // All of the child languages should be present + for (const childCode of ["bos", "cnr", "hrv", "srp"]) { expect( - akaResults.some( + hbsResults.some( (result) => - codeMatches(result.iso639_3_code, "twi") || - codeMatches(result.iso639_3_code, "fat") || - result.languageSubtag === "tw" + codeMatches(result.iso639_3_code, childCode) && + !result.isMacrolanguage ) - ).toBe(false); - }); - - it("should handle Serbo-Croatian as expected", async () => { - // one "hbs" card, is macrolanguage, has multiple names - const hbsResults = defaultSearchResultModifier( - (await asyncGetAllLanguageResults("Serbo-Croatian")) as ILanguage[], - "Serbo-Croatian" - ); - const hbsResult = hbsResults.find((result) => - codeMatches(result.iso639_3_code, "hbs") - ); - expect(hbsResult).toBeDefined(); - expect(hbsResult?.isMacrolanguage).toBe(true); - expect(hbsResult?.names.length).toBe(0); - // All of the child languages should be present - for (const childCode of ["bos", "cnr", "hrv", "srp"]) { - expect( - hbsResults.some( - (result) => - codeMatches(result.iso639_3_code, childCode) && - !result.isMacrolanguage - ) - ).toBe(true); - } - }); - - it("should handle Norwegian as expected", async () => { - // one macrolanguage "nor" card, plus one for Bokmål and one for Nynorsk - const norResults = defaultSearchResultModifier( - (await asyncGetAllLanguageResults("Norwegian")) as ILanguage[], - "Norwegian" - ); - const norResult = norResults.find((result) => - codeMatches(result.iso639_3_code, "nor") - ); - expect(norResult).toBeDefined(); - expect(norResult?.isMacrolanguage).toBe(true); - // Both of the child languages should be present - for (const childCode of ["nob", "nno"]) { - expect( - norResults.some( - (result) => - codeMatches(result.iso639_3_code, childCode) && - !result.isMacrolanguage - ) - ).toBe(true); - } - }); - - it("should handle Sanskrit as expected", async () => { - // we don't have enough info for the individual languages, so just one "san" card - const sanResults = defaultSearchResultModifier( - (await asyncGetAllLanguageResults("Sanskrit")) as ILanguage[], - "Sanskrit" - ); - const sanResult = sanResults.find((result) => - codeMatches(result.iso639_3_code, "san") - ); - expect(sanResult).toBeDefined(); - expect(sanResult?.isMacrolanguage).toBe(false); - // Make sure it has the list of names - expect(sanResult?.names.length).toBeGreaterThan(3); - }); + ).toBe(true); + } + }); - it("should handle Zapotec as expected", async () => { - // in addition to all the individual zapotec languages, there should be one macrolanguage zap card, - // and its list of names should not contain "Isthmus Zapotec" nor "Las Delicias Zapotec" as that could be confusing - const zapResults = defaultSearchResultModifier( - (await asyncGetAllLanguageResults("Zapotec")) as ILanguage[], - "Zapotec" - ); - const zapResult = zapResults.find((result) => - codeMatches(result.iso639_3_code, "zap") - ); - expect(zapResult).toBeDefined(); - expect(zapResult?.isMacrolanguage).toBe(true); + it("should handle Norwegian as expected", async () => { + // one macrolanguage "nor" card, plus one for Bokmål and one for Nynorsk + const norResults = defaultSearchResultModifier( + (await asyncGetAllLanguageResults("Norwegian")) as ILanguage[], + "Norwegian" + ); + const norResult = norResults.find((result) => + codeMatches(result.iso639_3_code, "nor") + ); + expect(norResult).toBeDefined(); + expect(norResult?.isMacrolanguage).toBe(true); + // Both of the child languages should be present + for (const childCode of ["nob", "nno"]) { expect( - zapResult?.names.some((n) => - ["Isthmus Zapotec", "Las Delicias Zapotec"].includes(n) + norResults.some( + (result) => + codeMatches(result.iso639_3_code, childCode) && + !result.isMacrolanguage ) - ).toBe(false); - }); + ).toBe(true); + } + }); + + it("should handle Sanskrit as expected", async () => { + // we don't have enough info for the individual languages, so just one "san" card + const sanResults = defaultSearchResultModifier( + (await asyncGetAllLanguageResults("Sanskrit")) as ILanguage[], + "Sanskrit" + ); + const sanResult = sanResults.find((result) => + codeMatches(result.iso639_3_code, "san") + ); + expect(sanResult).toBeDefined(); + expect(sanResult?.isMacrolanguage).toBe(false); + // Make sure it has the list of names + expect(sanResult?.names.length).toBeGreaterThan(3); + }); + + it("should handle Zapotec as expected", async () => { + // in addition to all the individual zapotec languages, there should be one macrolanguage zap card, + // and its list of names should not contain "Isthmus Zapotec" nor "Las Delicias Zapotec" as that could be confusing + const zapResults = defaultSearchResultModifier( + (await asyncGetAllLanguageResults("Zapotec")) as ILanguage[], + "Zapotec" + ); + const zapResult = zapResults.find((result) => + codeMatches(result.iso639_3_code, "zap") + ); + expect(zapResult).toBeDefined(); + expect(zapResult?.isMacrolanguage).toBe(true); + expect( + zapResult?.names.some((n) => + ["Isthmus Zapotec", "Las Delicias Zapotec"].includes(n) + ) + ).toBe(false); }); }); diff --git a/components/language-chooser/common/find-language/searchResultModifiers.ts b/components/language-chooser/common/find-language/searchResultModifiers.ts index cf3829c7..32551b6d 100644 --- a/components/language-chooser/common/find-language/searchResultModifiers.ts +++ b/components/language-chooser/common/find-language/searchResultModifiers.ts @@ -153,13 +153,13 @@ function simplifySpanishResult(results: ILanguage[]): ILanguage[] { return substituteInModifiedEntry("spa", getSimplifiedSpanishResult, results); } -function simplifyChineseResult(results: ILanguage[]): ILanguage[] { - function getSimplifiedChineseResult(result: ILanguage) { +function modifyChineseResult(results: ILanguage[]): ILanguage[] { + function getModifiedChineseResult(result: ILanguage) { return { ...result, languageSubtag: "zh", // otherwise would be "cmn". For Chinese in particular we remove the macrolanguage card and have just 1 card, and give it tag zh autonym: "中文", - regionNamesForDisplay: "", // clear the long and confusing list of region names + regionNamesForDisplay: "", // don't include the long and confusing list of region names regionNamesForSearch: [], names: result.names.filter( (name) => name !== "中文" && name !== "繁體中文" @@ -181,7 +181,7 @@ function simplifyChineseResult(results: ILanguage[]): ILanguage[] { ], } as ILanguage; } - return substituteInModifiedEntry("cmn", getSimplifiedChineseResult, results); + return substituteInModifiedEntry("cmn", getModifiedChineseResult, results); } export function makeIntoMacrolanguageEntry( @@ -313,7 +313,7 @@ export function defaultSearchResultModifier( ); modifiedResults = simplifyEnglishResult(modifiedResults); modifiedResults = simplifyFrenchResult(modifiedResults); - modifiedResults = simplifyChineseResult(modifiedResults); + modifiedResults = modifyChineseResult(modifiedResults); modifiedResults = simplifySpanishResult(modifiedResults); // The following are necessary because rare anomalies in langtags.json prevent us from mapping them in the usual way diff --git a/components/language-chooser/react/language-chooser-react-mui/e2e/languageSearch.e2e.ts b/components/language-chooser/react/language-chooser-react-mui/e2e/languageSearch.e2e.ts index 2e07c0d7..e466fbe4 100644 --- a/components/language-chooser/react/language-chooser-react-mui/e2e/languageSearch.e2e.ts +++ b/components/language-chooser/react/language-chooser-react-mui/e2e/languageSearch.e2e.ts @@ -103,6 +103,28 @@ test.describe("Search", () => { await expect(sanCard).not.toContainText("vsn"); }); + test("Chinese special case", async () => { + await search(page, "chinese"); + + const chineseCard = page.getByTestId(languageCardTestId("cmn")); + await chineseCard.scrollIntoViewIfNeeded(); + await expect(chineseCard).toBeVisible(); + await expect(chineseCard).toContainText(/chinese/i); + await expect(chineseCard).not.toContainText(/macrolanguage/i); + await chineseCard.click(); + + const zhCard = page.getByTestId(languageCardTestId("zho")); + await expect(zhCard).not.toBeVisible(); + + // Make sure chinese comes up when searching "zh", "cmn", "中文", and "huayu" + for (const searchTerm of ["zh", "cmn", "中文", "huayu"]) { + await search(page, searchTerm); + const chineseCard = page.getByTestId(languageCardTestId("cmn")); + await chineseCard.scrollIntoViewIfNeeded(); + await expect(chineseCard).toBeVisible(); + } + }); + test("X button clears search and results", async () => { await search(page, "tok pisin"); // At least one result is visible diff --git a/components/language-chooser/react/language-chooser-react-mui/e2e/languageTag.e2e.ts b/components/language-chooser/react/language-chooser-react-mui/e2e/languageTag.e2e.ts index 59facad9..cc35a419 100644 --- a/components/language-chooser/react/language-chooser-react-mui/e2e/languageTag.e2e.ts +++ b/components/language-chooser/react/language-chooser-react-mui/e2e/languageTag.e2e.ts @@ -188,4 +188,36 @@ test.describe("Language tag creation and preview", () => { await expect(saLatnTagPreview).toBeVisible(); await expect(saLatnTagPreview).toContainText("sa-Latn"); }); + + test("Chinese special case", async () => { + await search(page, "chinese"); + + const chineseCard = page.getByTestId(languageCardTestId("cmn")); + await chineseCard.scrollIntoViewIfNeeded(); + await expect(chineseCard).toBeVisible(); + await expect(chineseCard).toContainText(/chinese/i); + await chineseCard.click(); + + const noScriptTagPreview = page.getByTestId("right-panel-langtag-preview"); + await expect(noScriptTagPreview).toBeVisible(); + await expect(noScriptTagPreview).toContainText("zh"); + + // select Chinese (Traditional) script + const tradCard = page.getByTestId(scriptCardTestId("Hant")); + await expect(tradCard).toBeVisible(); + await tradCard.click(); + + const tradTagPreview = page.getByTestId("right-panel-langtag-preview"); + await expect(tradTagPreview).toBeVisible(); + await expect(tradTagPreview).toContainText("zh-TW"); + + // select Chinese (Simplified) script + const simpCard = page.getByTestId(scriptCardTestId("Hans")); + await expect(simpCard).toBeVisible(); + await simpCard.click(); + + const simpTagPreview = page.getByTestId("right-panel-langtag-preview"); + await expect(simpTagPreview).toBeVisible(); + await expect(simpTagPreview).toContainText("zh-CN"); + }); });