From 2809ed17501ecd4729b9cf31aa926a03f00ae7e3 Mon Sep 17 00:00:00 2001 From: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com> Date: Sat, 21 Jun 2025 00:37:15 +0200 Subject: [PATCH] [tests] Review and refactor translation tests (#3792) I have refactored the translations tests, they should now be clearer and easier to understand. There should be no functional impact. I have discarded the original approach of also replacing `XMLHttpRequest` with `fetch` in the file `js/translator.js`. I had managed to get it to work functionally, but I couldn't get the tests to work. --- CHANGELOG.md | 3 +- tests/e2e/translations_spec.js | 186 +++++++------- tests/unit/classes/translator_spec.js | 350 ++++++++++++-------------- 3 files changed, 262 insertions(+), 277 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d16efdda..66ec171f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,8 @@ planned for 2025-07-01 - Removed as many of the date conversions as possible - Use `moment-timezone` when calculating recurring events, this will fix problems from the past with offsets and DST not being handled properly - Added some tests to test the behavior of the refactored methods to make sure the correct event dates are returned -- [linter] Enable ESLint rule `no-console` and replace `console` with `Log` in some files +- [linter] Enable ESLint rule `no-console` and replace `console` with `Log` in some files (#3810) +- [tests] Review and refactor translation tests (#3792) ### Fixed diff --git a/tests/e2e/translations_spec.js b/tests/e2e/translations_spec.js index 4f7f01a3..2c43e6a1 100644 --- a/tests/e2e/translations_spec.js +++ b/tests/e2e/translations_spec.js @@ -6,7 +6,7 @@ const express = require("express"); const sinon = require("sinon"); const translations = require("../../translations/translations"); -describe("Translations", () => { +describe("translations", () => { let server; beforeAll(() => { @@ -26,8 +26,9 @@ describe("Translations", () => { }); it("should have a translation file in the specified path", () => { - for (let language in translations) { + for (const language in translations) { const file = fs.statSync(translations[language]); + expect(file.isFile()).toBe(true); } }); @@ -36,90 +37,91 @@ describe("Translations", () => { let dom; beforeEach(() => { - dom = new JSDOM( - `\ - \ - `, - { runScripts: "dangerously", resources: "usable" } - ); + // Create a new JSDOM instance for each test + dom = new JSDOM("", { runScripts: "dangerously", resources: "usable" }); + + // Mock the necessary global objects + dom.window.Log = { log: jest.fn(), error: jest.fn() }; + dom.window.Translator = {}; + dom.window.config = { language: "de" }; + + // Load class.js and module.js content directly + const classJs = fs.readFileSync(path.join(__dirname, "..", "..", "js", "class.js"), "utf-8"); + const moduleJs = fs.readFileSync(path.join(__dirname, "..", "..", "js", "module.js"), "utf-8"); + + // Execute the scripts in the JSDOM context + dom.window.eval(classJs); + dom.window.eval(moduleJs); }); - it("should load translation file", () => { - return new Promise((done) => { - dom.window.onload = async () => { - const { Translator, Module, config } = dom.window; - config.language = "en"; - Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); - - Module.register("name", { getTranslations: () => translations }); - const MMM = Module.create("name"); - - await MMM.loadTranslations(); - - expect(Translator.load.args).toHaveLength(1); - expect(Translator.load.calledWith(MMM, "translations/en.json", false)).toBe(true); - - done(); - }; + it("should load translation file", async () => { + await new Promise((resolve) => { + dom.window.onload = resolve; }); + + const { Translator, Module, config } = dom.window; + config.language = "en"; + Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); + + Module.register("name", { getTranslations: () => translations }); + const MMM = Module.create("name"); + + await MMM.loadTranslations(); + + expect(Translator.load.args).toHaveLength(1); + expect(Translator.load.calledWith(MMM, "translations/en.json", false)).toBe(true); }); - it("should load translation + fallback file", () => { - return new Promise((done) => { - dom.window.onload = async () => { - const { Translator, Module } = dom.window; - Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); - - Module.register("name", { getTranslations: () => translations }); - const MMM = Module.create("name"); - - await MMM.loadTranslations(); - - expect(Translator.load.args).toHaveLength(2); - expect(Translator.load.calledWith(MMM, "translations/de.json", false)).toBe(true); - expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true); - - done(); - }; + it("should load translation + fallback file", async () => { + await new Promise((resolve) => { + dom.window.onload = resolve; }); + + const { Translator, Module } = dom.window; + Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); + + Module.register("name", { getTranslations: () => translations }); + const MMM = Module.create("name"); + + await MMM.loadTranslations(); + + expect(Translator.load.args).toHaveLength(2); + expect(Translator.load.calledWith(MMM, "translations/de.json", false)).toBe(true); + expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true); }); - it("should load translation fallback file", () => { - return new Promise((done) => { - dom.window.onload = async () => { - const { Translator, Module, config } = dom.window; - config.language = "--"; - Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); - - Module.register("name", { getTranslations: () => translations }); - const MMM = Module.create("name"); - - await MMM.loadTranslations(); - - expect(Translator.load.args).toHaveLength(1); - expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true); - - done(); - }; + it("should load translation fallback file", async () => { + await new Promise((resolve) => { + dom.window.onload = resolve; }); + + const { Translator, Module, config } = dom.window; + config.language = "--"; + Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); + + Module.register("name", { getTranslations: () => translations }); + const MMM = Module.create("name"); + + await MMM.loadTranslations(); + + expect(Translator.load.args).toHaveLength(1); + expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true); }); - it("should load no file", () => { - return new Promise((done) => { - dom.window.onload = async () => { - const { Translator, Module } = dom.window; - Translator.load = sinon.stub(); - - Module.register("name", {}); - const MMM = Module.create("name"); - - await MMM.loadTranslations(); - - expect(Translator.load.callCount).toBe(0); - - done(); - }; + it("should load no file", async () => { + await new Promise((resolve) => { + dom.window.onload = resolve; }); + + const { Translator, Module } = dom.window; + Translator.load = sinon.stub(); + + Module.register("name", {}); + const MMM = Module.create("name"); + + await MMM.loadTranslations(); + + expect(Translator.load.callCount).toBe(0); }); }); @@ -130,29 +132,30 @@ describe("Translations", () => { } }; - describe("Parsing language files through the Translator class", () => { - for (let language in translations) { - it(`should parse ${language}`, () => { - return new Promise((done) => { - const dom = new JSDOM( - `\ - \ - \ - \ - \ -