Skip to content

Commit efc64cd

Browse files
[core] refactor: replace XMLHttpRequest with fetch
1 parent 034f3c4 commit efc64cd

File tree

6 files changed

+59
-31
lines changed

6 files changed

+59
-31
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ planned for 2026-01-01
2525
- [tests] migrate from `jest` to `vitest` (#3940, #3941)
2626
- [ci] Add concurrency to automated tests workflow to cancel outdated runs (#3943)
2727
- [tests] replace `node-libgpiod` with `serialport` in electron-rebuild workflow (#3945)
28+
- [core] refactor: replace `XMLHttpRequest` with `fetch` in `translator.js`
2829

2930
### Fixed
3031

js/loader.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,36 @@ const Loader = (function () {
1010

1111
/* Private Methods */
1212

13+
/**
14+
* Get environment variables from config.
15+
* @returns {object} Env vars with modulesDir and customCss paths from config.
16+
*/
17+
const getEnvVarsFromConfig = function () {
18+
return {
19+
modulesDir: config.foreignModulesDir || "modules",
20+
customCss: config.customCss || "css/custom.css"
21+
};
22+
};
23+
1324
/**
1425
* Retrieve object of env variables.
1526
* @returns {object} with key: values as assembled in js/server_functions.js
1627
*/
1728
const getEnvVars = async function () {
18-
const res = await fetch(`${location.protocol}//${location.host}${config.basePath}env`);
19-
return JSON.parse(await res.text());
29+
// In test mode, skip server fetch and use config values directly
30+
if (typeof process !== "undefined" && process.env && process.env.mmTestMode === "true") {
31+
return getEnvVarsFromConfig();
32+
}
33+
34+
// In production, fetch env vars from server
35+
try {
36+
const res = await fetch(`${config.basePath}env`);
37+
return JSON.parse(await res.text());
38+
} catch (error) {
39+
// Fallback to config values if server fetch fails
40+
Log.error("Unable to retrieve env configuration", error);
41+
return getEnvVarsFromConfig();
42+
}
2043
};
2144

2245
/**

js/translator.js

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,24 @@
33
const Translator = (function () {
44

55
/**
6-
* Load a JSON file via XHR.
6+
* Load a JSON file via fetch.
77
* @param {string} file Path of the file we want to load.
88
* @returns {Promise<object>} the translations in the specified file
99
*/
1010
async function loadJSON (file) {
11-
const xhr = new XMLHttpRequest();
12-
return new Promise(function (resolve) {
13-
xhr.overrideMimeType("application/json");
14-
xhr.open("GET", file, true);
15-
xhr.onreadystatechange = function () {
16-
if (xhr.readyState === 4 && xhr.status === 200) {
17-
// needs error handler try/catch at least
18-
let fileInfo = null;
19-
try {
20-
fileInfo = JSON.parse(xhr.responseText);
21-
} catch (exception) {
22-
// nothing here, but don't die
23-
Log.error(`[translator] loading json file =${file} failed`);
24-
}
25-
resolve(fileInfo);
26-
}
27-
};
28-
xhr.send(null);
29-
});
11+
const baseHref = document.baseURI;
12+
const url = new URL(file, baseHref);
13+
14+
try {
15+
const response = await fetch(url);
16+
if (!response.ok) {
17+
throw new Error(`Unexpected response status: ${response.status}`);
18+
}
19+
return await response.json();
20+
} catch (exception) {
21+
Log.error(`Loading json file =${file} failed`);
22+
return null;
23+
}
3024
}
3125

3226
return {

tests/e2e/helpers/global-setup.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,40 +64,48 @@ exports.startApplication = async (configFilename, exec) => {
6464
};
6565

6666
exports.stopApplication = async (waitTime = 100) => {
67-
if (global.window) {
68-
// no closing causes test errors and memory leaks
69-
global.window.close();
70-
delete global.window;
71-
}
72-
7367
if (!global.app) {
68+
if (global.window) {
69+
global.window.close();
70+
delete global.window;
71+
}
7472
delete global.testPort;
7573
return Promise.resolve();
7674
}
7775

76+
// Stop server first
7877
await global.app.stop();
7978
delete global.app;
8079
delete global.testPort;
8180

82-
// Small delay to ensure clean shutdown
81+
// Wait for any pending async operations to complete before closing DOM
8382
await new Promise((resolve) => setTimeout(resolve, waitTime));
83+
84+
if (global.window) {
85+
// Close window after async operations have settled
86+
global.window.close();
87+
delete global.window;
88+
delete global.document;
89+
}
8490
};
8591

8692
exports.getDocument = () => {
8793
return new Promise((resolve) => {
8894
const port = global.testPort || config.port || 8080;
89-
const url = `http://${config.address || "localhost"}:${port}`;
95+
// JSDOM requires localhost instead of 0.0.0.0 for URL resolution
96+
const address = config.address === "0.0.0.0" ? "localhost" : config.address || "localhost";
97+
const url = `http://${address}:${port}`;
9098
jsdom.JSDOM.fromURL(url, { resources: "usable", runScripts: "dangerously" }).then((dom) => {
9199
dom.window.name = "jsdom";
92100
global.window = dom.window;
101+
global.document = dom.window.document;
93102
// Following fixes `navigator is not defined` errors in e2e tests, found here
94103
// https://www.appsloveworld.com/reactjs/100/37/mocha-react-navigator-is-not-defined
95104
global.navigator = {
96105
useragent: "node.js"
97106
};
98107
dom.window.fetch = fetch;
99108
dom.window.onload = () => {
100-
global.document = dom.window.document;
101109
resolve();
102110
};
103111
});

tests/e2e/translations_spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ function createTranslationTestEnvironment () {
1616

1717
dom.window.Log = { log: vi.fn(), error: vi.fn() };
1818
dom.window.translations = translations;
19+
dom.window.fetch = fetch;
1920
dom.window.eval(translatorJs);
2021

2122
const window = dom.window;

tests/unit/classes/translator_spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ function createTranslationTestEnvironment () {
1313
const dom = new JSDOM("", { url: "http://localhost:3001", runScripts: "outside-only" });
1414

1515
dom.window.Log = { log: vi.fn(), error: vi.fn() };
16+
dom.window.fetch = fetch;
1617
dom.window.eval(translatorJs);
1718

1819
return { window: dom.window, Translator: dom.window.Translator };

0 commit comments

Comments
 (0)