diff --git a/CHANGELOG.md b/CHANGELOG.md index c119ed78..2898eaee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Thanks to: @dathbe. - [refactor] Add new file `js/module_functions.js` to move code used in several modules to one place (#3837) - [tests] refactor: simplify jest config file (#3844) - [tests] refactor: extract constants for weather electron tests (#3845) +- [tests] refactor: add `setupDOMEnvironment` helper function to eliminate repetitive JSDOM setup code (#3860) - [tests] replace `console` with `Log` in calendar `debug.js` to avoid exception in eslint config (#3846) - [tests] speed up e2e tests, cleanup and stabilize weather e2e tests (#3847, #3848) diff --git a/tests/e2e/translations_spec.js b/tests/e2e/translations_spec.js index 2c43e6a1..9ea46205 100644 --- a/tests/e2e/translations_spec.js +++ b/tests/e2e/translations_spec.js @@ -6,6 +6,21 @@ const express = require("express"); const sinon = require("sinon"); const translations = require("../../translations/translations"); +/** + * Helper function to setup DOM environment. + * @returns {object} The JSDOM window object + */ +function setupDOMEnvironment () { + const dom = new JSDOM("", { runScripts: "dangerously", resources: "usable" }); + + dom.window.Log = { log: jest.fn(), error: jest.fn() }; + const translatorJs = fs.readFileSync(path.join(__dirname, "..", "..", "js", "translator.js"), "utf-8"); + dom.window.translations = translations; + dom.window.eval(translatorJs); + + return dom.window; +} + describe("translations", () => { let server; @@ -38,10 +53,10 @@ describe("translations", () => { beforeEach(() => { // Create a new JSDOM instance for each test - dom = new JSDOM("", { runScripts: "dangerously", resources: "usable" }); + const window = setupDOMEnvironment(); + dom = { window }; - // Mock the necessary global objects - dom.window.Log = { log: jest.fn(), error: jest.fn() }; + // Additional setup for loadTranslations tests dom.window.Translator = {}; dom.window.config = { language: "de" }; @@ -132,21 +147,16 @@ describe("translations", () => { } }; - const translatorJs = fs.readFileSync(path.join(__dirname, "..", "..", "js", "translator.js"), "utf-8"); - describe("parsing language files through the Translator class", () => { for (const language in translations) { it(`should parse ${language}`, async () => { - const dom = new JSDOM("", { runScripts: "dangerously", resources: "usable" }); - dom.window.Log = { log: jest.fn() }; - dom.window.translations = translations; - dom.window.eval(translatorJs); + const window = setupDOMEnvironment(); await new Promise((resolve) => { - dom.window.onload = resolve; + window.onload = resolve; }); - const { Translator } = dom.window; + const { Translator } = window; await Translator.load(mmm, translations[language], false); expect(typeof Translator.translations[mmm.name]).toBe("object"); @@ -178,14 +188,11 @@ describe("translations", () => { // Function to initialize JSDOM and load translations const initializeTranslationDOM = (language) => { - const dom = new JSDOM("", { runScripts: "dangerously", resources: "usable" }); - dom.window.Log = { log: jest.fn() }; - dom.window.translations = translations; - dom.window.eval(translatorJs); + const window = setupDOMEnvironment(); return new Promise((resolve) => { - dom.window.onload = async () => { - const { Translator } = dom.window; + window.onload = async () => { + const { Translator } = window; await Translator.load(mmm, translations[language], false); resolve(Translator.translations[mmm.name]); }; diff --git a/tests/unit/classes/translator_spec.js b/tests/unit/classes/translator_spec.js index a84742fe..a12c7c6a 100644 --- a/tests/unit/classes/translator_spec.js +++ b/tests/unit/classes/translator_spec.js @@ -4,6 +4,21 @@ const helmet = require("helmet"); const { JSDOM } = require("jsdom"); const express = require("express"); +/** + * Helper function to setup DOM environment. + * @param {string} scriptContent - The script content to evaluate + * @returns {Promise} The JSDOM window object + */ +async function setupDOMEnvironment (scriptContent) { + const dom = new JSDOM("", { runScripts: "outside-only" }); + + dom.window.eval(scriptContent); + dom.window.Log = { log: jest.fn(), error: jest.fn() }; + + await new Promise((resolve) => dom.window.onload = resolve); + return dom.window; +} + describe("Translator", () => { let server; const sockets = new Set(); @@ -81,12 +96,8 @@ describe("Translator", () => { }; it("should return custom module translation", async () => { - const dom = new JSDOM("", { runScripts: "outside-only" }); - dom.window.eval(translatorJsScriptContent); - - await new Promise((resolve) => dom.window.onload = resolve); - - const { Translator } = dom.window; + const window = await setupDOMEnvironment(translatorJsScriptContent); + const { Translator } = window; setTranslations(Translator); let translation = Translator.translate({ name: "MMM-Module" }, "Hello"); @@ -97,12 +108,8 @@ describe("Translator", () => { }); it("should return core translation", async () => { - const dom = new JSDOM("", { runScripts: "outside-only" }); - dom.window.eval(translatorJsScriptContent); - - await new Promise((resolve) => dom.window.onload = resolve); - - const { Translator } = dom.window; + const window = await setupDOMEnvironment(translatorJsScriptContent); + const { Translator } = window; setTranslations(Translator); let translation = Translator.translate({ name: "MMM-Module" }, "FOO"); expect(translation).toBe("Foo"); @@ -111,48 +118,32 @@ describe("Translator", () => { }); it("should return custom module translation fallback", async () => { - const dom = new JSDOM("", { runScripts: "outside-only" }); - dom.window.eval(translatorJsScriptContent); - - await new Promise((resolve) => dom.window.onload = resolve); - - const { Translator } = dom.window; + const window = await setupDOMEnvironment(translatorJsScriptContent); + const { Translator } = window; setTranslations(Translator); const translation = Translator.translate({ name: "MMM-Module" }, "A key"); expect(translation).toBe("A translation"); }); it("should return core translation fallback", async () => { - const dom = new JSDOM("", { runScripts: "outside-only" }); - dom.window.eval(translatorJsScriptContent); - - await new Promise((resolve) => dom.window.onload = resolve); - - const { Translator } = dom.window; + const window = await setupDOMEnvironment(translatorJsScriptContent); + const { Translator } = window; setTranslations(Translator); const translation = Translator.translate({ name: "MMM-Module" }, "Fallback"); expect(translation).toBe("core fallback"); }); it("should return translation with placeholder for missing variables", async () => { - const dom = new JSDOM("", { runScripts: "outside-only" }); - dom.window.eval(translatorJsScriptContent); - - await new Promise((resolve) => dom.window.onload = resolve); - - const { Translator } = dom.window; + const window = await setupDOMEnvironment(translatorJsScriptContent); + const { Translator } = window; setTranslations(Translator); const translation = Translator.translate({ name: "MMM-Module" }, "Hello {username}"); expect(translation).toBe("Hallo {username}"); }); it("should return key if no translation was found", async () => { - const dom = new JSDOM("", { runScripts: "outside-only" }); - dom.window.eval(translatorJsScriptContent); - - await new Promise((resolve) => dom.window.onload = resolve); - - const { Translator } = dom.window; + const window = await setupDOMEnvironment(translatorJsScriptContent); + const { Translator } = window; setTranslations(Translator); const translation = Translator.translate({ name: "MMM-Module" }, "MISSING"); expect(translation).toBe("MISSING"); @@ -168,12 +159,8 @@ describe("Translator", () => { }; it("should load translations", async () => { - const dom = new JSDOM("", { runScripts: "outside-only" }); - dom.window.eval(translatorJsScriptContent); - dom.window.Log = { log: jest.fn() }; - await new Promise((resolve) => dom.window.onload = resolve); - - const { Translator } = dom.window; + const window = await setupDOMEnvironment(translatorJsScriptContent); + const { Translator } = window; const file = "translation_test.json"; await Translator.load(mmm, file, false); @@ -182,30 +169,20 @@ describe("Translator", () => { }); it("should load translation fallbacks", async () => { - const dom = new JSDOM("", { runScripts: "outside-only" }); - dom.window.eval(translatorJsScriptContent); - - await new Promise((resolve) => dom.window.onload = resolve); - - const { Translator } = dom.window; + const window = await setupDOMEnvironment(translatorJsScriptContent); + const { Translator } = window; const file = "translation_test.json"; - dom.window.Log = { log: jest.fn() }; await Translator.load(mmm, file, true); const json = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "..", "..", "tests", "mocks", file), "utf8")); expect(Translator.translationsFallback[mmm.name]).toEqual(json); }); it("should not load translations, if module fallback exists", async () => { - const dom = new JSDOM("", { runScripts: "outside-only" }); - dom.window.eval(translatorJsScriptContent); - await new Promise((resolve) => dom.window.onload = resolve); - - const { Translator } = dom.window; + const window = await setupDOMEnvironment(translatorJsScriptContent); + const { Translator } = window; const file = "translation_test.json"; - - dom.window.Log = { log: jest.fn() }; Translator.translationsFallback[mmm.name] = { Hello: "Hallo" }; @@ -220,13 +197,9 @@ describe("Translator", () => { describe("loadCoreTranslations", () => { it("should load core translations and fallback", async () => { - const dom = new JSDOM("", { runScripts: "outside-only" }); - dom.window.eval(translatorJsScriptContent); - dom.window.translations = { en: "http://localhost:3000/translations/translation_test.json" }; - dom.window.Log = { log: jest.fn() }; - await new Promise((resolve) => dom.window.onload = resolve); - - const { Translator } = dom.window; + const window = await setupDOMEnvironment(translatorJsScriptContent); + window.translations = { en: "http://localhost:3000/translations/translation_test.json" }; + const { Translator } = window; await Translator.loadCoreTranslations("en"); const en = translationTestData; @@ -238,13 +211,9 @@ describe("Translator", () => { }); it("should load core fallback if language cannot be found", async () => { - const dom = new JSDOM("", { runScripts: "outside-only" }); - dom.window.eval(translatorJsScriptContent); - dom.window.translations = { en: "http://localhost:3000/translations/translation_test.json" }; - dom.window.Log = { log: jest.fn() }; - await new Promise((resolve) => dom.window.onload = resolve); - - const { Translator } = dom.window; + const window = await setupDOMEnvironment(translatorJsScriptContent); + window.translations = { en: "http://localhost:3000/translations/translation_test.json" }; + const { Translator } = window; await Translator.loadCoreTranslations("MISSINGLANG"); const en = translationTestData; @@ -258,13 +227,9 @@ describe("Translator", () => { describe("loadCoreTranslationsFallback", () => { it("should load core translations fallback", async () => { - const dom = new JSDOM("", { runScripts: "outside-only" }); - dom.window.eval(translatorJsScriptContent); - dom.window.translations = { en: "http://localhost:3000/translations/translation_test.json" }; - dom.window.Log = { log: jest.fn() }; - await new Promise((resolve) => dom.window.onload = resolve); - - const { Translator } = dom.window; + const window = await setupDOMEnvironment(translatorJsScriptContent); + window.translations = { en: "http://localhost:3000/translations/translation_test.json" }; + const { Translator } = window; await Translator.loadCoreTranslationsFallback(); const en = translationTestData; @@ -275,14 +240,9 @@ describe("Translator", () => { }); it("should load core fallback if language cannot be found", async () => { - const dom = new JSDOM("", { runScripts: "outside-only" }); - dom.window.eval(translatorJsScriptContent); - dom.window.translations = {}; - dom.window.Log = { log: jest.fn() }; - - await new Promise((resolve) => dom.window.onload = resolve); - - const { Translator } = dom.window; + const window = await setupDOMEnvironment(translatorJsScriptContent); + window.translations = {}; + const { Translator } = window; await Translator.loadCoreTranslations(); await new Promise((resolve) => setTimeout(resolve, 500));