mirror of
https://github.com/MichMich/MagicMirror.git
synced 2026-07-22 14:23:51 -07:00
4b4a59534f
## Release Notes Thanks to: @angeldeejay, @egeekial, @khassel, @KristjanESPERANTO, @MikeBishop, @rejas > ⚠️ This release needs nodejs version >=22.21.1 <23 || >=24 (no change to previous release) [Compare to previous Release v2.36.0](https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.36.0...develop) ### [core] - Prepare Release 2.37.0 (#4193) - fix(electron): map IPv6 :: wildcard to localhost (#4188) - refactor(main): modernize DOM update flow with async/await (#4186) - refactor(main): simplify _updateDom with async/await (#4185) - fix(security): prevent unauthorized secret expansion in socket payloads (#4184) - refactor(main): simplify updateDomWithContent async flow (#4182) - fix: modules losing data after HTTP 304 responses (#4180) - chore: add missing core defaults (#4181) - fix(server): enforce ipWhitelist for Socket.IO too (#4169) - feat(systeminfo): include Git hash and branch in system information log (#4167) - feat(electron): support object-based electronSwitches (#4161) - systeminformation thread not ending: move error handling from utils to app (#4160) - fix systeminformation thread not ending (#4155) - refactor: use ES module imports in browser core (#4158) - refactor(core): remove old Object.assign polyfill (#4157) - refactor: rewrite Module as an ES6 class (#4151) - refactor: rewrite NodeHelper as an ES6 class (#4147) - update eletron to v42 (#4144) - refactor(utils): drop ajv dependency (#4142) - fix(systeminformation): output right 'used node' version (from parent process) (#4141) - fix: skip postinstall git clean when not in a git repository (#4139) - Remove unnecessary conditionals and fix falsy property check in imperial conversion (#4135) - update version in package.json ### [dependencies] - update dependencies (#4191) - Bump actions/checkout from 6 to 7 (#4190) - chore: update dependencies and adjust import path for SunCalc (#4189) - update dependencies incl. electron and revert yauzl-electron-install-fix (#4183) - update dependencies, add electron fix in package.json (#4175) - chore: update dependencies (#4162) - Bump actions/dependency-review-action from 4 to 5 (#4152) - Unify linting: replace Stylelint and markdownlint with ESLint (#4148) - update dependencies and workflows to node v26 (#4140) ### [modules/alert] - CodeQL cleanup for alerts #18, #19, #20 (#4153) - fix: resolve CodeQL alerts #24 and #26 (#4145) - fix(electron): resolve CodeQL alerts #22 and #25 in electron.js (#4136) ### [modules/calendar] - perf(calendar): pre-filter ICS data before parsing (#4168) - perf(calendar): use async ICS parsing to avoid blocking event loop (#4143) ### [modules/newsfeed] - [newsfeed] add allowBasicHtmlTags option for basic emphasis (#4176) ### [modules/updatenotification] - fix(updatenotification): don't spawn a child process when running under PM2 (#4166) - fix(updatenotification): use process.argv[0] as restart binary (#4163) - fix(updatenotification): preserve start mode on restart (#4156) - fix(updatenotification): fix ref diff parsing for fetch --dry-run (#4138) - refactor(updatenotification): replace pm2 usage with node logic (#4134) ### [modules/weather] - feat(weather): add Buienradar provider (#4164) ### [testing] - remove warning in unit tests (for nodejs >= v25) (#4149) - polish HTTP 304 docs/test/handling (#4129) --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com> Co-authored-by: BugHaver <43462320+bughaver@users.noreply.github.com> Co-authored-by: BugHaver <43462320+lsaadeh@users.noreply.github.com> Co-authored-by: Karsten Hassel <hassel@gmx.de> Co-authored-by: Magnus <34011212+MagMar94@users.noreply.github.com> Co-authored-by: Koen Konst <koenspero@gmail.com> Co-authored-by: Koen Konst <c.h.konst@avisi.nl> Co-authored-by: Bugsounet - Cédric <github@bugsounet.fr> Co-authored-by: dathbe <github@beffa.us> Co-authored-by: veeck <gitkraken@veeck.de> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Marcel <m-idler@users.noreply.github.com> Co-authored-by: sam detweiler <sdetweil@gmail.com> Co-authored-by: Kevin G. <crazylegstoo@gmail.com> Co-authored-by: Jboucly <33218155+jboucly@users.noreply.github.com> Co-authored-by: Jboucly <contact@jboucly.fr> Co-authored-by: Jarno <54169345+jarnoml@users.noreply.github.com> Co-authored-by: Jordan Welch <JordanHWelch@gmail.com> Co-authored-by: Blackspirits <blackspirits@gmail.com> Co-authored-by: Samed Ozdemir <samed@xsor.io> Co-authored-by: in-voker <58696565+in-voker@users.noreply.github.com> Co-authored-by: Andrés Vanegas Jiménez <142350+angeldeejay@users.noreply.github.com> Co-authored-by: cgillinger <christian.gillinger@gmail.com> Co-authored-by: Sonny B <43247590+sonnyb9@users.noreply.github.com> Co-authored-by: sonnyb9 <sonnyb9@users.noreply.github.com> Co-authored-by: Morgan McBee <egeekial@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Mike Bishop <mbishop@evequefou.be>
189 lines
4.9 KiB
JavaScript
189 lines
4.9 KiB
JavaScript
const path = require("node:path");
|
|
const os = require("node:os");
|
|
const fs = require("node:fs");
|
|
const { chromium } = require("playwright");
|
|
|
|
// global absolute root path
|
|
global.root_path = path.resolve(`${__dirname}/../../../`);
|
|
|
|
const indexFile = `${global.root_path}/index.html`;
|
|
const cssFile = `${global.root_path}/config/custom.css`;
|
|
const sampleCss = [
|
|
".region.row3 {",
|
|
" top: 0;",
|
|
"}",
|
|
".region.row3.left {",
|
|
" top: 100%;",
|
|
"}"
|
|
];
|
|
let indexData = "";
|
|
let cssData = "";
|
|
|
|
let browser;
|
|
let context;
|
|
let page;
|
|
|
|
/**
|
|
* Ensure Playwright browser and context are available.
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function ensureContext () {
|
|
if (!browser) {
|
|
// Additional args for CI stability to prevent crashes
|
|
const launchOptions = {
|
|
headless: true,
|
|
args: [
|
|
"--disable-dev-shm-usage", // Overcome limited resource problems in Docker/CI
|
|
"--disable-gpu", // Disable GPU hardware acceleration
|
|
"--no-sandbox", // Required for running as root in some CI environments
|
|
"--disable-setuid-sandbox",
|
|
"--single-process" // Run in single process mode for better stability in CI
|
|
]
|
|
};
|
|
browser = await chromium.launch(launchOptions);
|
|
}
|
|
if (!context) {
|
|
context = await browser.newContext();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Open a fresh page pointing to the provided url.
|
|
* @param {string} url target url
|
|
* @returns {Promise<import('playwright').Page>} initialized page instance
|
|
*/
|
|
async function openPage (url) {
|
|
await ensureContext();
|
|
if (page) {
|
|
await page.close();
|
|
}
|
|
page = await context.newPage();
|
|
await page.goto(url, { waitUntil: "load" });
|
|
return page;
|
|
}
|
|
|
|
/**
|
|
* Close page, context and browser if they exist.
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function closeBrowser () {
|
|
if (page) {
|
|
await page.close();
|
|
page = null;
|
|
}
|
|
if (context) {
|
|
await context.close();
|
|
context = null;
|
|
}
|
|
if (browser) {
|
|
await browser.close();
|
|
browser = null;
|
|
}
|
|
}
|
|
|
|
exports.getPage = () => {
|
|
if (!page) {
|
|
throw new Error("Playwright page is not initialized. Call getDocument() first.");
|
|
}
|
|
return page;
|
|
};
|
|
|
|
exports.startApplication = async (configFilename) => {
|
|
vi.resetModules();
|
|
|
|
// Clear Node's require cache for config and app files to prevent stale configs and middlewares
|
|
Object.keys(require.cache).forEach((key) => {
|
|
if (
|
|
key.includes("/tests/configs/")
|
|
|| key.includes("/config/config")
|
|
|| key.includes("/js/app.js")
|
|
|| key.includes("/js/server.js")
|
|
) {
|
|
delete require.cache[key];
|
|
}
|
|
});
|
|
|
|
if (global.app) {
|
|
await exports.stopApplication();
|
|
}
|
|
|
|
// Use MM_PORT if preset by a test, otherwise default to 8080.
|
|
const port = Number(process.env.MM_PORT) || 8080;
|
|
global.testPort = port;
|
|
|
|
// Set config sample for use in test
|
|
let configPath;
|
|
if (configFilename === "") {
|
|
configPath = "config/config.js";
|
|
} else {
|
|
configPath = configFilename;
|
|
}
|
|
|
|
process.env.MM_CONFIG_FILE = configPath;
|
|
|
|
// Ensure MM_PORT is set before app loads
|
|
process.env.MM_PORT = port.toString();
|
|
|
|
process.env.mmTestMode = "true";
|
|
process.setMaxListeners(0);
|
|
global.app = require(`${global.root_path}/js/app`);
|
|
|
|
return global.app.start();
|
|
};
|
|
|
|
exports.stopApplication = async (waitTime = 100) => {
|
|
await closeBrowser();
|
|
|
|
if (!global.app) {
|
|
delete global.testPort;
|
|
return Promise.resolve();
|
|
}
|
|
|
|
await global.app.stop();
|
|
delete global.app;
|
|
delete global.testPort;
|
|
delete process.env.MM_PORT;
|
|
|
|
// Wait for any pending async operations to complete before closing DOM
|
|
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
};
|
|
|
|
exports.getDocument = async () => {
|
|
const port = global.testPort || config.port || 8080;
|
|
const address = config.address === "0.0.0.0" ? "localhost" : config.address || "localhost";
|
|
const url = `http://${address}:${port}`;
|
|
|
|
await openPage(url);
|
|
};
|
|
|
|
exports.fixupIndex = async () => {
|
|
// read and save the git level index file
|
|
indexData = (await fs.promises.readFile(indexFile)).toString();
|
|
// make lines of the content
|
|
const workIndexLines = indexData.split(os.EOL);
|
|
// loop thru the lines to find place to insert new region
|
|
for (let l in workIndexLines) {
|
|
if (workIndexLines[l].includes("region top right")) {
|
|
// insert a new line with new region definition
|
|
workIndexLines.splice(l, 0, " <div class=\"region row3 left\"><div class=\"container\"></div></div>");
|
|
break;
|
|
}
|
|
}
|
|
// write out the new index.html file, not append
|
|
await fs.promises.writeFile(indexFile, workIndexLines.join(os.EOL), { flush: true });
|
|
// read in the current custom.css
|
|
cssData = (await fs.promises.readFile(cssFile)).toString();
|
|
// write out the custom.css for this testcase, matching the new region name
|
|
await fs.promises.writeFile(cssFile, sampleCss.join(os.EOL), { flush: true });
|
|
};
|
|
|
|
exports.restoreIndex = async () => {
|
|
// if we read in data
|
|
if (indexData.length > 0) {
|
|
//write out saved index.html
|
|
await fs.promises.writeFile(indexFile, indexData, { flush: true });
|
|
// write out saved custom.css
|
|
await fs.promises.writeFile(cssFile, cssData, { flush: true });
|
|
}
|
|
};
|