From 5e161e7ea9472ea2803c3be7ae2d716a543fe89a Mon Sep 17 00:00:00 2001 From: veeck Date: Tue, 21 Feb 2023 19:06:07 +0100 Subject: [PATCH 1/4] Convert translator from callback to async --- js/loader.js | 2 +- js/main.js | 3 +-- js/module.js | 20 ++++++---------- js/translator.js | 62 ++++++++++++++++++++++-------------------------- 4 files changed, 37 insertions(+), 50 deletions(-) diff --git a/js/loader.js b/js/loader.js index 70ed32c5fe..d48188bce4 100644 --- a/js/loader.js +++ b/js/loader.js @@ -160,7 +160,7 @@ const Loader = (function () { Log.log("Scripts loaded for: " + module.name); mObj.loadStyles(function () { Log.log("Styles loaded for: " + module.name); - mObj.loadTranslations(function () { + mObj.loadTranslations().then(() => { Log.log("Translations loaded for: " + module.name); moduleObjects.push(mObj); callback(); diff --git a/js/main.js b/js/main.js index 3eed494bff..d310d3cab5 100644 --- a/js/main.js +++ b/js/main.js @@ -485,8 +485,7 @@ const MM = (function () { Log.setLogLevel(config.logLevel); - Translator.loadCoreTranslations(config.language); - Loader.loadModules(); + Translator.loadCoreTranslations(config.language).then(() => Loader.loadModules()); }, /** diff --git a/js/module.js b/js/module.js index 6d15452994..0c5445c3fc 100644 --- a/js/module.js +++ b/js/module.js @@ -302,10 +302,8 @@ const Module = Class.extend({ /** * Load all translations. - * - * @param {Function} callback Function called when done. */ - loadTranslations(callback) { + async loadTranslations() { const translations = this.getTranslations() || {}; const language = config.language.toLowerCase(); @@ -313,7 +311,6 @@ const Module = Class.extend({ const fallbackLanguage = languages[0]; if (languages.length === 0) { - callback(); return; } @@ -321,17 +318,14 @@ const Module = Class.extend({ const translationsFallbackFile = translations[fallbackLanguage]; if (!translationFile) { - Translator.load(this, translationsFallbackFile, true, callback); - return; + return Translator.load(this, translationsFallbackFile, true); } - Translator.load(this, translationFile, false, () => { - if (translationFile !== translationsFallbackFile) { - Translator.load(this, translationsFallbackFile, true, callback); - } else { - callback(); - } - }); + await Translator.load(this, translationFile, false); + + if (translationFile !== translationsFallbackFile) { + return Translator.load(this, translationsFallbackFile, true); + } }, /** diff --git a/js/translator.js b/js/translator.js index 77d4b8f0da..8830a8a4e6 100644 --- a/js/translator.js +++ b/js/translator.js @@ -11,26 +11,28 @@ const Translator = (function () { * Load a JSON file via XHR. * * @param {string} file Path of the file we want to load. - * @param {Function} callback Function called when done. + * @returns {Promise} the translations in the specified file */ - function loadJSON(file, callback) { + async function loadJSON(file) { const xhr = new XMLHttpRequest(); - xhr.overrideMimeType("application/json"); - xhr.open("GET", file, true); - xhr.onreadystatechange = function () { - if (xhr.readyState === 4 && xhr.status === 200) { - // needs error handler try/catch at least - let fileinfo = null; - try { - fileinfo = JSON.parse(xhr.responseText); - } catch (exception) { - // nothing here, but don't die - Log.error(" loading json file =" + file + " failed"); + return new Promise(function (resolve, reject) { + xhr.overrideMimeType("application/json"); + xhr.open("GET", file, true); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4 && xhr.status === 200) { + // needs error handler try/catch at least + let fileinfo = null; + try { + fileinfo = JSON.parse(xhr.responseText); + } catch (exception) { + // nothing here, but don't die + Log.error(" loading json file =" + file + " failed"); + } + resolve(fileinfo); } - callback(fileinfo); - } - }; - xhr.send(null); + }; + xhr.send(null); + }); } return { @@ -101,21 +103,17 @@ const Translator = (function () { * @param {Module} module The module to load the translation file for. * @param {string} file Path of the file we want to load. * @param {boolean} isFallback Flag to indicate fallback translations. - * @param {Function} callback Function called when done. */ - load(module, file, isFallback, callback) { + async load(module, file, isFallback) { Log.log(`${module.name} - Load translation${isFallback ? " fallback" : ""}: ${file}`); if (this.translationsFallback[module.name]) { - callback(); return; } - loadJSON(module.file(file), (json) => { - const property = isFallback ? "translationsFallback" : "translations"; - this[property][module.name] = json; - callback(); - }); + const json = await loadJSON(module.file(file)); + const property = isFallback ? "translationsFallback" : "translations"; + this[property][module.name] = json; }, /** @@ -123,30 +121,26 @@ const Translator = (function () { * * @param {string} lang The language identifier of the core language. */ - loadCoreTranslations: function (lang) { + loadCoreTranslations: async function (lang) { if (lang in translations) { Log.log("Loading core translation file: " + translations[lang]); - loadJSON(translations[lang], (translations) => { - this.coreTranslations = translations; - }); + this.coreTranslations = await loadJSON(translations[lang]); } else { Log.log("Configured language not found in core translations."); } - this.loadCoreTranslationsFallback(); + await this.loadCoreTranslationsFallback(); }, /** * Load the core translations fallback. * The first language defined in translations.js will be used. */ - loadCoreTranslationsFallback: function () { + loadCoreTranslationsFallback: async function () { let first = Object.keys(translations)[0]; if (first) { Log.log("Loading core translation fallback file: " + translations[first]); - loadJSON(translations[first], (translations) => { - this.coreTranslationsFallback = translations; - }); + this.coreTranslationsFallback = await loadJSON(translations[first]); } } }; From e7c02d30b112ab8af1e03fc2f523a7839423ae1e Mon Sep 17 00:00:00 2001 From: veeck Date: Tue, 21 Feb 2023 19:06:30 +0100 Subject: [PATCH 2/4] Adjust translator tests to async --- tests/e2e/translations_spec.js | 63 +++++++++++---------------- tests/unit/classes/translator_spec.js | 56 +++++++++++------------- 2 files changed, 52 insertions(+), 67 deletions(-) diff --git a/tests/e2e/translations_spec.js b/tests/e2e/translations_spec.js index 4d2d1c1e9d..f389f9a415 100644 --- a/tests/e2e/translations_spec.js +++ b/tests/e2e/translations_spec.js @@ -21,8 +21,8 @@ describe("Translations", () => { server = app.listen(3000); }); - afterAll(() => { - server.close(); + afterAll(async () => { + await server.close(); }); it("should have a translation file in the specified path", () => { @@ -48,17 +48,15 @@ describe("Translations", () => { dom.window.onload = async () => { const { Translator, Module, config } = dom.window; config.language = "en"; - Translator.load = sinon.stub().callsFake((_m, _f, _fb, callback) => callback()); + Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); Module.register("name", { getTranslations: () => translations }); const MMM = Module.create("name"); - const loaded = sinon.stub(); - MMM.loadTranslations(loaded); + await MMM.loadTranslations(); - expect(loaded.callCount).toBe(1); expect(Translator.load.args.length).toBe(1); - expect(Translator.load.calledWith(MMM, "translations/en.json", false, sinon.match.func)).toBe(true); + expect(Translator.load.calledWith(MMM, "translations/en.json", false)).toBe(true); done(); }; @@ -67,18 +65,16 @@ describe("Translations", () => { it("should load translation + fallback file", (done) => { dom.window.onload = async () => { const { Translator, Module } = dom.window; - Translator.load = sinon.stub().callsFake((_m, _f, _fb, callback) => callback()); + Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); Module.register("name", { getTranslations: () => translations }); const MMM = Module.create("name"); - const loaded = sinon.stub(); - MMM.loadTranslations(loaded); + await MMM.loadTranslations(); - expect(loaded.callCount).toBe(1); expect(Translator.load.args.length).toBe(2); - expect(Translator.load.calledWith(MMM, "translations/de.json", false, sinon.match.func)).toBe(true); - expect(Translator.load.calledWith(MMM, "translations/en.json", true, sinon.match.func)).toBe(true); + expect(Translator.load.calledWith(MMM, "translations/de.json", false)).toBe(true); + expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true); done(); }; @@ -88,17 +84,15 @@ describe("Translations", () => { dom.window.onload = async () => { const { Translator, Module, config } = dom.window; config.language = "--"; - Translator.load = sinon.stub().callsFake((_m, _f, _fb, callback) => callback()); + Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); Module.register("name", { getTranslations: () => translations }); const MMM = Module.create("name"); - const loaded = sinon.stub(); - MMM.loadTranslations(loaded); + await MMM.loadTranslations(); - expect(loaded.callCount).toBe(1); expect(Translator.load.args.length).toBe(1); - expect(Translator.load.calledWith(MMM, "translations/en.json", true, sinon.match.func)).toBe(true); + expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true); done(); }; @@ -112,10 +106,8 @@ describe("Translations", () => { Module.register("name", {}); const MMM = Module.create("name"); - const loaded = sinon.stub(); - MMM.loadTranslations(loaded); + await MMM.loadTranslations(); - expect(loaded.callCount).toBe(1); expect(Translator.load.callCount).toBe(0); done(); @@ -138,14 +130,13 @@ describe("Translations", () => {