Refactor languageHandler to avoid import cycles (#33948)
* Refactor languageHandler to avoid import cycles Also move its tests to vitest * Small refactor * Iterate * Iterate
This commit is contained in:
@@ -9,8 +9,9 @@ Please see LICENSE files in the repository root for full details.
|
||||
import fetchMock from "@fetch-mock/jest";
|
||||
import { ModuleLoader } from "@element-hq/element-web-module-api";
|
||||
import { merge } from "lodash";
|
||||
import { setMissingEntryGenerator } from "@element-hq/web-shared-components";
|
||||
|
||||
import * as languageHandler from "../../src/languageHandler";
|
||||
import { setLanguage } from "../../src/i18n/settings";
|
||||
import enElementWeb from "../../src/i18n/strings/en_EN.json";
|
||||
import deElementWeb from "../../src/i18n/strings/de_DE.json";
|
||||
// Cheat and import relatively here as these aren't exported by the module (should they be?)
|
||||
@@ -64,8 +65,8 @@ afterEach(() => fetchMock.callHistory.flush());
|
||||
|
||||
// Initialise the fetchMock before the test starts so the languageHandler.setLanguage call below can function
|
||||
setupLanguageMock();
|
||||
languageHandler.setLanguage("en");
|
||||
languageHandler.setMissingEntryGenerator((key) => key.split("|", 2)[1]);
|
||||
setLanguage("en");
|
||||
setMissingEntryGenerator((key) => key.split("|", 2)[1]);
|
||||
|
||||
// Set up the module API (so the i18n API exists)
|
||||
const moduleLoader = new ModuleLoader(ModuleApi.instance);
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
import EventListSummary from "../../../../../src/components/views/elements/EventListSummary";
|
||||
import { Layout } from "../../../../../src/settings/enums/Layout";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import * as languageHandler from "../../../../../src/languageHandler";
|
||||
import * as languageSettings from "../../../../../src/i18n/settings";
|
||||
|
||||
describe("EventListSummary", function () {
|
||||
const roomId = "!room:server.org";
|
||||
@@ -130,7 +130,7 @@ describe("EventListSummary", function () {
|
||||
|
||||
beforeEach(function () {
|
||||
jest.clearAllMocks();
|
||||
jest.spyOn(languageHandler, "getUserLanguage").mockReturnValue("en-GB");
|
||||
jest.spyOn(languageSettings, "getUserLanguage").mockReturnValue("en-GB");
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import { type RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
|
||||
import { type MediaEventHelper } from "../../../../../src/utils/MediaEventHelper";
|
||||
import * as languageHandler from "../../../../../src/languageHandler";
|
||||
import * as languageSettings from "../../../../../src/i18n/settings";
|
||||
|
||||
const CHECKED = "mx_PollOption_checked";
|
||||
const userId = "@me:example.com";
|
||||
@@ -55,7 +55,7 @@ describe("MPollBody", () => {
|
||||
|
||||
mockClient.getRoom.mockReturnValue(null);
|
||||
mockClient.relations.mockResolvedValue({ events: [] });
|
||||
jest.spyOn(languageHandler, "getUserLanguage").mockReturnValue("en-GB");
|
||||
jest.spyOn(languageSettings, "getUserLanguage").mockReturnValue("en-GB");
|
||||
});
|
||||
|
||||
it("finds no votes if there are none", () => {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { type MatrixClient, type MatrixEvent, PushRuleKind, type Room } from "ma
|
||||
import { mocked, type MockedObject } from "jest-mock-vitest-adapter";
|
||||
import { act, render, waitFor } from "jest-matrix-react";
|
||||
import { PushProcessor } from "matrix-js-sdk/src/pushprocessor";
|
||||
import { setMissingEntryGenerator } from "@element-hq/web-shared-components";
|
||||
|
||||
import {
|
||||
getMockClientWithEventEmitter,
|
||||
@@ -19,7 +20,6 @@ import {
|
||||
mkStubRoom,
|
||||
mockClientPushProcessor,
|
||||
} from "../../../../test-utils";
|
||||
import * as languageHandler from "../../../../../src/languageHandler";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import { TextualBodyFactory as TextualBody } from "../../../../../src/components/views/messages/TextualBodyFactory";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
@@ -470,7 +470,7 @@ describe("<TextualBody />", () => {
|
||||
let matrixClient: MatrixClient;
|
||||
|
||||
beforeEach(() => {
|
||||
languageHandler.setMissingEntryGenerator((key) => key.split("|", 2)[1]);
|
||||
setMissingEntryGenerator((key) => key.split("|", 2)[1]);
|
||||
matrixClient = getMockClientWithEventEmitter({
|
||||
getRoom: jest.fn(),
|
||||
getUserId: jest.fn(),
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
ReadReceiptPerson,
|
||||
readReceiptTooltip,
|
||||
} from "../../../../../src/components/views/rooms/ReadReceiptGroup";
|
||||
import * as languageHandler from "../../../../../src/languageHandler";
|
||||
import * as languageSettings from "../../../../../src/i18n/settings";
|
||||
import { stubClient } from "../../../../test-utils";
|
||||
import dispatcher from "../../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
@@ -46,7 +46,7 @@ describe("ReadReceiptGroup", () => {
|
||||
expect(readReceiptTooltip([], 1)).toBe("");
|
||||
});
|
||||
it("returns a pretty list without hasMore", () => {
|
||||
jest.spyOn(languageHandler, "getUserLanguage").mockReturnValue("en-GB");
|
||||
jest.spyOn(languageSettings, "getUserLanguage").mockReturnValue("en-GB");
|
||||
expect(readReceiptTooltip(["Alice", "Bob", "Charlie", "Dan", "Eve"], 5)).toEqual(
|
||||
"Alice, Bob, Charlie, Dan and Eve",
|
||||
);
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
getMockClientWithEventEmitter,
|
||||
mockClientMethodsUser,
|
||||
} from "../../../../test-utils";
|
||||
import * as languageHandler from "../../../../../src/languageHandler";
|
||||
import * as languageSettings from "../../../../../src/i18n/settings";
|
||||
|
||||
describe("RoomKnocksBar", () => {
|
||||
const userId = "@alice:example.org";
|
||||
@@ -132,7 +132,7 @@ describe("RoomKnocksBar", () => {
|
||||
jest.spyOn(state, "hasSufficientPowerLevelFor").mockReturnValue(true);
|
||||
jest.spyOn(Modal, "createDialog");
|
||||
jest.spyOn(dis, "dispatch");
|
||||
jest.spyOn(languageHandler, "getUserLanguage").mockReturnValue("en-GB");
|
||||
jest.spyOn(languageSettings, "getUserLanguage").mockReturnValue("en-GB");
|
||||
});
|
||||
|
||||
it("does not render if user can neither approve nor deny", () => {
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ import {
|
||||
|
||||
import { FormattingButtons } from "../../../../../../../src/components/views/rooms/wysiwyg_composer/components/FormattingButtons";
|
||||
import * as LinkModal from "../../../../../../../src/components/views/rooms/wysiwyg_composer/components/LinkModal";
|
||||
import { setLanguage } from "../../../../../../../src/languageHandler";
|
||||
import { setLanguage } from "../../../../../../../src/i18n/settings";
|
||||
|
||||
const mockWysiwyg = {
|
||||
bold: jest.fn(),
|
||||
|
||||
@@ -1,414 +0,0 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import fetchMock from "@fetch-mock/jest";
|
||||
import { type Translation } from "matrix-web-i18n";
|
||||
import { type TranslationStringsObject } from "@matrix-org/react-sdk-module-api";
|
||||
|
||||
import SdkConfig from "../../src/SdkConfig";
|
||||
import {
|
||||
_t,
|
||||
_tDom,
|
||||
getAllLanguagesWithLabels,
|
||||
registerCustomTranslations,
|
||||
setLanguage,
|
||||
setMissingEntryGenerator,
|
||||
substitute,
|
||||
type TranslatedString,
|
||||
UserFriendlyError,
|
||||
type IVariables,
|
||||
type Tags,
|
||||
getLanguagesFromBrowser,
|
||||
} from "../../src/languageHandler";
|
||||
import { stubClient } from "../test-utils";
|
||||
|
||||
async function setupTranslationOverridesForTests(overrides: TranslationStringsObject) {
|
||||
const lookupUrl = "/translations.json";
|
||||
|
||||
SdkConfig.add({
|
||||
custom_translations_url: lookupUrl,
|
||||
});
|
||||
fetchMock.get(lookupUrl, overrides, { name: "i18n-override" });
|
||||
await registerCustomTranslations({
|
||||
testOnlyIgnoreCustomTranslationsCache: true,
|
||||
});
|
||||
}
|
||||
|
||||
describe("languageHandler", () => {
|
||||
beforeEach(async () => {
|
||||
await setLanguage("en");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
SdkConfig.reset();
|
||||
fetchMock.removeRoute("i18n-override");
|
||||
});
|
||||
|
||||
it("should support overriding translations", async () => {
|
||||
const str: TranslationKey = "power_level|default";
|
||||
const enOverride: Translation = "Visitor";
|
||||
const deOverride: Translation = "Besucher";
|
||||
|
||||
// First test that overrides aren't being used
|
||||
await setLanguage("en");
|
||||
expect(_t(str)).toMatchInlineSnapshot(`"Default"`);
|
||||
await setLanguage("de");
|
||||
expect(_t(str)).toMatchInlineSnapshot(`"Standard"`);
|
||||
|
||||
await setupTranslationOverridesForTests({
|
||||
[str]: {
|
||||
en: enOverride,
|
||||
de: deOverride,
|
||||
},
|
||||
});
|
||||
|
||||
// Now test that they *are* being used
|
||||
await setLanguage("en");
|
||||
expect(_t(str)).toEqual(enOverride);
|
||||
|
||||
await setLanguage("de");
|
||||
expect(_t(str)).toEqual(deOverride);
|
||||
});
|
||||
|
||||
it("should support overriding plural translations", async () => {
|
||||
const str: TranslationKey = "voip|n_people_joined";
|
||||
const enOverride: Translation = {
|
||||
other: "%(count)s people in the call",
|
||||
one: "%(count)s person in the call",
|
||||
};
|
||||
const deOverride: Translation = {
|
||||
other: "%(count)s Personen im Anruf",
|
||||
one: "%(count)s Person im Anruf",
|
||||
};
|
||||
|
||||
// First test that overrides aren't being used
|
||||
await setLanguage("en");
|
||||
expect(_t(str, { count: 1 })).toMatchInlineSnapshot(`"1 person joined"`);
|
||||
expect(_t(str, { count: 5 })).toMatchInlineSnapshot(`"5 people joined"`);
|
||||
await setLanguage("de");
|
||||
expect(_t(str, { count: 1 })).toMatchInlineSnapshot(`"1 Person beigetreten"`);
|
||||
expect(_t(str, { count: 5 })).toMatchInlineSnapshot(`"5 Personen beigetreten"`);
|
||||
|
||||
await setupTranslationOverridesForTests({
|
||||
[str]: {
|
||||
en: enOverride,
|
||||
de: deOverride,
|
||||
},
|
||||
});
|
||||
|
||||
// Now test that they *are* being used
|
||||
await setLanguage("en");
|
||||
expect(_t(str, { count: 1 })).toMatchInlineSnapshot(`"1 person in the call"`);
|
||||
expect(_t(str, { count: 5 })).toMatchInlineSnapshot(`"5 people in the call"`);
|
||||
|
||||
await setLanguage("de");
|
||||
expect(_t(str, { count: 1 })).toMatchInlineSnapshot(`"1 Person im Anruf"`);
|
||||
expect(_t(str, { count: 5 })).toMatchInlineSnapshot(`"5 Personen im Anruf"`);
|
||||
});
|
||||
|
||||
describe("UserFriendlyError", () => {
|
||||
const testErrorMessage = "This email address is already in use (%(email)s)" as TranslationKey;
|
||||
beforeEach(async () => {
|
||||
// Setup some strings with variable substituations that we can use in the tests.
|
||||
const deOverride = "Diese E-Mail-Adresse wird bereits verwendet (%(email)s)";
|
||||
await setupTranslationOverridesForTests({
|
||||
[testErrorMessage]: {
|
||||
en: testErrorMessage,
|
||||
de: deOverride,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("includes English message and localized translated message", async () => {
|
||||
await setLanguage("de");
|
||||
|
||||
const friendlyError = new UserFriendlyError(testErrorMessage, {
|
||||
email: "test@example.com",
|
||||
cause: undefined,
|
||||
});
|
||||
|
||||
// Ensure message is in English so it's readable in the logs
|
||||
expect(friendlyError.message).toStrictEqual("This email address is already in use (test@example.com)");
|
||||
// Ensure the translated message is localized appropriately
|
||||
expect(friendlyError.translatedMessage).toStrictEqual(
|
||||
"Diese E-Mail-Adresse wird bereits verwendet (test@example.com)",
|
||||
);
|
||||
});
|
||||
|
||||
it("includes underlying cause error", async () => {
|
||||
await setLanguage("de");
|
||||
|
||||
const underlyingError = new Error("Fake underlying error");
|
||||
const friendlyError = new UserFriendlyError(testErrorMessage, {
|
||||
email: "test@example.com",
|
||||
cause: underlyingError,
|
||||
});
|
||||
|
||||
expect(friendlyError.cause).toStrictEqual(underlyingError);
|
||||
});
|
||||
|
||||
it("ok to omit the substitution variables and cause object, there just won't be any cause", async () => {
|
||||
const friendlyError = new UserFriendlyError("foo error" as TranslationKey);
|
||||
expect(friendlyError.cause).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAllLanguagesWithLabels", () => {
|
||||
it("should handle unknown language sanely", async () => {
|
||||
fetchMock.modifyRoute("languages", {
|
||||
response: {
|
||||
en: "en_EN.json",
|
||||
de: "de_DE.json",
|
||||
qq: "qq.json",
|
||||
},
|
||||
});
|
||||
await expect(getAllLanguagesWithLabels()).resolves.toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"label": "English",
|
||||
"labelInTargetLanguage": "English",
|
||||
"value": "en",
|
||||
},
|
||||
{
|
||||
"label": "German",
|
||||
"labelInTargetLanguage": "Deutsch",
|
||||
"value": "de",
|
||||
},
|
||||
{
|
||||
"label": "qq",
|
||||
"labelInTargetLanguage": "qq",
|
||||
"value": "qq",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getLanguagesFromBrowser", () => {
|
||||
beforeEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("should return navigator.languages if available", () => {
|
||||
jest.spyOn(window.navigator, "languages", "get").mockReturnValue(["en", "de"]);
|
||||
expect(getLanguagesFromBrowser()).toEqual(["en", "de"]);
|
||||
});
|
||||
|
||||
it("should return navigator.language if available", () => {
|
||||
jest.spyOn(window.navigator, "languages", "get").mockReturnValue([]);
|
||||
jest.spyOn(window.navigator, "language", "get").mockReturnValue("de");
|
||||
expect(getLanguagesFromBrowser()).toEqual(["de"]);
|
||||
});
|
||||
|
||||
it("should return 'en' otherwise", () => {
|
||||
jest.spyOn(window.navigator, "languages", "get").mockReturnValue([]);
|
||||
jest.spyOn(window.navigator, "language", "get").mockReturnValue(undefined as any);
|
||||
expect(getLanguagesFromBrowser()).toEqual(["en"]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("languageHandler JSX", function () {
|
||||
// See setupLanguage.ts for how we are stubbing out translations to provide fixture data for these tests
|
||||
const basicString = "common|rooms";
|
||||
const selfClosingTagSub = "Accept <policyLink /> to continue:" as TranslationKey;
|
||||
const textInTagSub = "<a>Upgrade</a> to your own domain" as TranslationKey;
|
||||
const plurals = "common|and_n_others";
|
||||
const variableSub = "slash_command|ignore_dialog_description";
|
||||
|
||||
type TestCase = [string, TranslationKey, IVariables, Tags | undefined, TranslatedString];
|
||||
const testCasesEn: TestCase[] = [
|
||||
// description of the test case, translationString, variables, tags, expected result
|
||||
["translates a basic string", basicString, {}, undefined, "Rooms"],
|
||||
["handles plurals when count is 0", plurals, { count: 0 }, undefined, "and 0 others..."],
|
||||
["handles plurals when count is 1", plurals, { count: 1 }, undefined, "and one other..."],
|
||||
["handles plurals when count is not 1", plurals, { count: 2 }, undefined, "and 2 others..."],
|
||||
["handles simple variable substitution", variableSub, { userId: "foo" }, undefined, "You are now ignoring foo"],
|
||||
[
|
||||
"handles simple tag substitution",
|
||||
selfClosingTagSub,
|
||||
{},
|
||||
{ policyLink: () => "foo" },
|
||||
"Accept foo to continue:",
|
||||
],
|
||||
["handles text in tags", textInTagSub, {}, { a: (sub: string) => `x${sub}x` }, "xUpgradex to your own domain"],
|
||||
[
|
||||
"handles variable substitution with React function component",
|
||||
variableSub,
|
||||
{ userId: () => <i>foo</i> },
|
||||
undefined,
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
<span>
|
||||
You are now ignoring <i>foo</i>
|
||||
</span>,
|
||||
],
|
||||
[
|
||||
"handles variable substitution with react node",
|
||||
variableSub,
|
||||
{ userId: <i>foo</i> },
|
||||
undefined,
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
<span>
|
||||
You are now ignoring <i>foo</i>
|
||||
</span>,
|
||||
],
|
||||
[
|
||||
"handles tag substitution with React function component",
|
||||
selfClosingTagSub,
|
||||
{},
|
||||
{ policyLink: () => <i>foo</i> },
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
<span>
|
||||
Accept <i>foo</i> to continue:
|
||||
</span>,
|
||||
],
|
||||
];
|
||||
|
||||
let oldNodeEnv: string | undefined;
|
||||
beforeAll(() => {
|
||||
oldNodeEnv = process.env.NODE_ENV;
|
||||
process.env.NODE_ENV = "test";
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
process.env.NODE_ENV = oldNodeEnv;
|
||||
});
|
||||
|
||||
describe("when translations exist in language", () => {
|
||||
beforeEach(async () => {
|
||||
stubClient();
|
||||
|
||||
await setLanguage("en");
|
||||
setMissingEntryGenerator((key) => key.split("|", 2)[1]);
|
||||
});
|
||||
|
||||
it("translates a string to german", async () => {
|
||||
await setLanguage("de");
|
||||
const translated = _t(basicString);
|
||||
expect(translated).toBe("Chats");
|
||||
});
|
||||
|
||||
it.each(testCasesEn)("%s", (_d, translationString, variables, tags, result) => {
|
||||
expect(_t(translationString, variables, tags!)).toEqual(result);
|
||||
});
|
||||
|
||||
it("replacements in the wrong order", function () {
|
||||
const text = "%(var1)s %(var2)s" as TranslationKey;
|
||||
expect(_t(text, { var2: "val2", var1: "val1" })).toBe("val1 val2");
|
||||
});
|
||||
|
||||
it("multiple replacements of the same variable", function () {
|
||||
const text = "%(var1)s %(var1)s";
|
||||
expect(substitute(text, { var1: "val1" })).toBe("val1 val1");
|
||||
});
|
||||
|
||||
it("multiple replacements of the same tag", function () {
|
||||
const text = "<a>Click here</a> to join the discussion! <a>or here</a>";
|
||||
expect(substitute(text, {}, { a: (sub) => `x${sub}x` })).toBe(
|
||||
"xClick herex to join the discussion! xor herex",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("for a non-en language", () => {
|
||||
beforeEach(async () => {
|
||||
stubClient();
|
||||
await setLanguage("lv");
|
||||
// counterpart doesn't expose any way to restore default config
|
||||
// missingEntryGenerator is mocked in the root setup file
|
||||
// reset to default here
|
||||
const counterpartDefaultMissingEntryGen = function (key: string) {
|
||||
return "missing translation: " + key;
|
||||
};
|
||||
setMissingEntryGenerator(counterpartDefaultMissingEntryGen);
|
||||
});
|
||||
|
||||
// mocked lv has only `"Uploading %(filename)s and %(count)s others|one"`
|
||||
const lvExistingPlural = "room|upload|uploading_multiple_file";
|
||||
const lvNonExistingPlural = "%(spaceName)s and %(count)s others";
|
||||
|
||||
describe("pluralization", () => {
|
||||
const pluralCases = [
|
||||
[
|
||||
"falls back when plural string exists but not for for count",
|
||||
lvExistingPlural,
|
||||
{ count: 2, filename: "test.txt" },
|
||||
undefined,
|
||||
"Uploading test.txt and 2 others",
|
||||
],
|
||||
[
|
||||
"falls back when plural string does not exists at all",
|
||||
lvNonExistingPlural,
|
||||
{ count: 2, spaceName: "test" },
|
||||
undefined,
|
||||
"test and 2 others",
|
||||
],
|
||||
] as TestCase[];
|
||||
|
||||
describe("_t", () => {
|
||||
it("translated correctly when plural string exists for count", () => {
|
||||
expect(_t(lvExistingPlural, { count: 1, filename: "test.txt" })).toEqual(
|
||||
"Качване на test.txt и 1 друг",
|
||||
);
|
||||
});
|
||||
it.each(pluralCases)("%s", (_d, translationString, variables, tags, result) => {
|
||||
expect(_t(translationString, variables, tags!)).toEqual(result);
|
||||
});
|
||||
});
|
||||
|
||||
describe("_tDom()", () => {
|
||||
it("translated correctly when plural string exists for count", () => {
|
||||
expect(_tDom(lvExistingPlural, { count: 1, filename: "test.txt" })).toEqual(
|
||||
"Качване на test.txt и 1 друг",
|
||||
);
|
||||
});
|
||||
it.each(pluralCases)(
|
||||
"%s and translates with fallback locale, attributes fallback locale",
|
||||
(_d, translationString, variables, tags, result) => {
|
||||
expect(_tDom(translationString, variables, tags!)).toEqual(<span lang="en">{result}</span>);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when a translation string does not exist in active language", () => {
|
||||
describe("_t", () => {
|
||||
it.each(testCasesEn)(
|
||||
"%s and translates with fallback locale",
|
||||
(_d, translationString, variables, tags, result) => {
|
||||
expect(_t(translationString, variables, tags!)).toEqual(result);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("_tDom()", () => {
|
||||
it.each(testCasesEn)(
|
||||
"%s and translates with fallback locale, attributes fallback locale",
|
||||
(_d, translationString, variables, tags, result) => {
|
||||
expect(_tDom(translationString, variables, tags!)).toEqual(<span lang="en">{result}</span>);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when languages dont load", () => {
|
||||
it("_t", () => {
|
||||
const STRING_NOT_IN_THE_DICTIONARY = "a string that isn't in the translations dictionary" as TranslationKey;
|
||||
expect(_t(STRING_NOT_IN_THE_DICTIONARY, {})).toEqual(STRING_NOT_IN_THE_DICTIONARY);
|
||||
});
|
||||
|
||||
it("_tDom", () => {
|
||||
const STRING_NOT_IN_THE_DICTIONARY = "a string that isn't in the translations dictionary" as TranslationKey;
|
||||
expect(_tDom(STRING_NOT_IN_THE_DICTIONARY, {})).toEqual(
|
||||
<span lang="en">{STRING_NOT_IN_THE_DICTIONARY}</span>,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -17,7 +17,7 @@ import { type Mocked } from "jest-mock-vitest-adapter";
|
||||
|
||||
import { ProxiedModuleApi } from "../../../src/modules/ProxiedModuleApi";
|
||||
import { getMockClientWithEventEmitter, mkRoom, stubClient } from "../../test-utils";
|
||||
import { setLanguage } from "../../../src/languageHandler";
|
||||
import { setLanguage } from "../../../src/i18n/settings";
|
||||
import { ModuleRunner } from "../../../src/modules/ModuleRunner";
|
||||
import { registerMockModule } from "./MockModule";
|
||||
import defaultDispatcher from "../../../src/dispatcher/dispatcher";
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
DAY_MS,
|
||||
} from "../../../src/DateUtils";
|
||||
import { REPEATABLE_DATE, mockIntlDateTimeFormat, unmockIntlDateTimeFormat } from "../../test-utils";
|
||||
import * as languageHandler from "../../../src/languageHandler";
|
||||
import * as languageSettings from "../../../src/i18n/settings";
|
||||
|
||||
describe("getDaysArray", () => {
|
||||
it("should return Sunday-Saturday in long mode", () => {
|
||||
@@ -369,7 +369,7 @@ describe("formatLocalDateShort()", () => {
|
||||
});
|
||||
const timestamp = new Date("Fri Dec 17 2021 09:09:00 GMT+0100 (Central European Standard Time)").getTime();
|
||||
it("formats date correctly by locale", () => {
|
||||
const locale = jest.spyOn(languageHandler, "getUserLanguage");
|
||||
const locale = jest.spyOn(languageSettings, "getUserLanguage");
|
||||
mockIntlDateTimeFormat();
|
||||
|
||||
// format is DD/MM/YY
|
||||
|
||||
Reference in New Issue
Block a user