diff --git a/cspell.config.json b/cspell.config.json index dd2804cc43..a9d61d1386 100644 --- a/cspell.config.json +++ b/cspell.config.json @@ -109,6 +109,7 @@ "ezeholz", "Fadesteps", "Faizan", + "feeditem", "feedparser", "feelslike", "Fenner", @@ -116,10 +117,10 @@ "fewieden", "fixuppm", "flopp", + "fmi", "fmisid", "fontawesome", "fontface", - "fmi", "forecastday", "forecastweather", "fortawesome", @@ -136,8 +137,8 @@ "ghsas", "grenagit", "Halfclear", - "HARMONIE", "harmonie", + "HARMONIE", "heavyrain", "heavyrainandthunder", "heavyrainshowers", diff --git a/defaultmodules/newsfeed/feeditem.js b/defaultmodules/newsfeed/feeditem.js index 29dc159518..918a216686 100644 --- a/defaultmodules/newsfeed/feeditem.js +++ b/defaultmodules/newsfeed/feeditem.js @@ -1,6 +1,6 @@ -const crypto = require("node:crypto"); -const { htmlToText } = require("html-to-text"); -const Log = require("logger"); +import crypto from "node:crypto"; +import { htmlToText } from "html-to-text"; +import Log from "logger"; // html-to-text formatter that re-emits an allowed inline tag around its content, // so feeds that send real / elements keep their emphasis. `br` is a void @@ -115,7 +115,7 @@ const normalizeFeedItem = (item, { allowedBasicHtmlTags = [], logFeedWarnings = }; }; -module.exports = { +export { sanitizeBasicHtml, normalizeFeedItem }; diff --git a/defaultmodules/newsfeed/newsfeedfetcher.js b/defaultmodules/newsfeed/newsfeedfetcher.js index 83d3ce166d..3c84a89f17 100644 --- a/defaultmodules/newsfeed/newsfeedfetcher.js +++ b/defaultmodules/newsfeed/newsfeedfetcher.js @@ -1,9 +1,9 @@ -const stream = require("node:stream"); -const FeedParser = require("feedparser"); -const iconv = require("iconv-lite"); -const Log = require("logger"); -const { normalizeFeedItem } = require("./feeditem"); -const HTTPFetcher = require("#http_fetcher"); +import stream from "node:stream"; +import FeedParser from "feedparser"; +import iconv from "iconv-lite"; +import Log from "logger"; +import HTTPFetcher from "../../js/http_fetcher.mjs"; +import { normalizeFeedItem } from "./feeditem.js"; // The complete set of basic formatting tags users are allowed to opt into via the // `allowedBasicHtmlTags` config option. These are inline emphasis / line-break tags that @@ -159,4 +159,4 @@ class NewsfeedFetcher { } } -module.exports = NewsfeedFetcher; +export default NewsfeedFetcher; diff --git a/defaultmodules/newsfeed/node_helper.js b/defaultmodules/newsfeed/node_helper.js index 7e2f49b92d..02d6b1ce17 100644 --- a/defaultmodules/newsfeed/node_helper.js +++ b/defaultmodules/newsfeed/node_helper.js @@ -1,11 +1,11 @@ -const dns = require("node:dns"); -const ipaddr = require("ipaddr.js"); -const undici = require("undici"); -const NodeHelper = require("node_helper"); -const Log = require("logger"); -const NewsfeedFetcher = require("./newsfeedfetcher"); - -module.exports = NodeHelper.create({ +import dns from "node:dns"; +import ipaddr from "ipaddr.js"; +import undici from "undici"; +import NodeHelper from "node_helper"; +import Log from "logger"; +import NewsfeedFetcher from "./newsfeedfetcher.js"; + +export default NodeHelper.create({ // Override start method. start () { Log.log(`Starting node helper for: ${this.name}`); diff --git a/defaultmodules/newsfeed/package.json b/defaultmodules/newsfeed/package.json new file mode 100644 index 0000000000..bedb411a91 --- /dev/null +++ b/defaultmodules/newsfeed/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/eslint.config.mjs b/eslint.config.mjs index 08d1227919..567e06c77b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -181,7 +181,7 @@ export default defineConfig([ } }, { - files: ["tests/mocks/**/node_helper.js", "defaultmodules/*/node_helper.js", "defaultmodules/*/git_helper.js"], + files: ["tests/mocks/**/node_helper.js", "defaultmodules/*/node_helper.js", "defaultmodules/*/git_helper.js", "defaultmodules/newsfeed/feeditem.js", "defaultmodules/newsfeed/newsfeedfetcher.js"], rules: { // MagicMirror aliases are resolved at runtime by js/alias-resolver.js. "import-x/no-unresolved": ["error", {ignore: ["^node_helper$", "^logger$"]}] diff --git a/tests/unit/modules/default/newsfeed/feeditem_spec.js b/tests/unit/modules/default/newsfeed/feeditem_spec.js index 8694ef54f4..ba646f6eae 100644 --- a/tests/unit/modules/default/newsfeed/feeditem_spec.js +++ b/tests/unit/modules/default/newsfeed/feeditem_spec.js @@ -1,6 +1,4 @@ -const defaults = require("../../../../../js/defaults"); - -const { sanitizeBasicHtml } = require(`../../../../../${defaults.defaultModulesDir}/newsfeed/feeditem`); +import { sanitizeBasicHtml } from "../../../../../defaultmodules/newsfeed/feeditem.js"; // The full safe list users may opt into; most tests run with it enabled. const ALL_TAGS = ["b", "strong", "i", "em", "u", "br", "code", "s", "sub", "sup"]; diff --git a/tests/unit/modules/default/newsfeed/newsfeedfetcher_spec.js b/tests/unit/modules/default/newsfeed/newsfeedfetcher_spec.js index baa9b2c437..788753f216 100644 --- a/tests/unit/modules/default/newsfeed/newsfeedfetcher_spec.js +++ b/tests/unit/modules/default/newsfeed/newsfeedfetcher_spec.js @@ -1,11 +1,9 @@ -const fs = require("node:fs"); -const path = require("node:path"); -const { Readable } = require("node:stream"); -const defaults = require("../../../../../js/defaults"); +import fs from "node:fs"; +import path from "node:path"; +import { Readable } from "node:stream"; +import NewsfeedFetcher from "../../../../../defaultmodules/newsfeed/newsfeedfetcher.js"; -const NewsfeedFetcher = require(`../../../../../${defaults.defaultModulesDir}/newsfeed/newsfeedfetcher`); - -const xmlContent = fs.readFileSync(path.resolve(__dirname, "../../../../mocks/newsfeed_test.xml"), "utf8"); +const xmlContent = fs.readFileSync(path.resolve(import.meta.dirname, "../../../../mocks/newsfeed_test.xml"), "utf8"); const feedResponse = (fetcher, xml = xmlContent) => new Promise((resolve) => { fetcher.onReceive(() => resolve(fetcher.items)); diff --git a/tests/unit/modules/default/newsfeed/node_helper_spec.js b/tests/unit/modules/default/newsfeed/node_helper_spec.js index 4a6d4cd180..df5c9dca50 100644 --- a/tests/unit/modules/default/newsfeed/node_helper_spec.js +++ b/tests/unit/modules/default/newsfeed/node_helper_spec.js @@ -1,70 +1,55 @@ -import Module from "node:module"; import { afterEach, describe, expect, it, vi } from "vitest"; -const loadNewsfeedNodeHelper = async () => { - vi.resetModules(); - +const mocks = vi.hoisted(() => { + const fetcherInstances = []; const loggerMock = { log: vi.fn(), warn: vi.fn(), error: vi.fn() }; - const fetcherInstances = []; - const originalRequire = Module.prototype.require; - - Module.prototype.require = function (id) { - if (id === "node_helper") { - return { - create: vi.fn((definition) => definition) - }; + class MockNewsfeedFetcher { + constructor (...args) { + this.url = args[0]; + this.items = []; + this.onReceiveCallback = undefined; + this.onErrorCallback = undefined; + this.setReloadInterval = vi.fn(); + this.broadcastItems = vi.fn(); + this.startFetch = vi.fn(); + fetcherInstances.push(this); } - if (id === "logger") { - return loggerMock; + onReceive (callback) { + this.onReceiveCallback = callback; } - if (id === "./newsfeedfetcher") { - return class MockNewsfeedFetcher { - constructor (...args) { - this.url = args[0]; - this.items = []; - this.onReceiveCallback = undefined; - this.onErrorCallback = undefined; - this.setReloadInterval = vi.fn(); - this.broadcastItems = vi.fn(); - this.startFetch = vi.fn(); - fetcherInstances.push(this); - } - - onReceive (callback) { - this.onReceiveCallback = callback; - } - - onError (callback) { - this.onErrorCallback = callback; - } - }; + onError (callback) { + this.onErrorCallback = callback; } + } - return originalRequire.apply(this, arguments); - }; + return { fetcherInstances, loggerMock, MockNewsfeedFetcher }; +}); - let helper; - try { - const helperModule = await import("../../../../../defaultmodules/newsfeed/node_helper"); - helper = helperModule.default || helperModule; - } finally { - Module.prototype.require = originalRequire; - } +vi.mock("node_helper", () => ({ + default: { create: (definition) => definition } +})); +vi.mock("logger", () => ({ default: mocks.loggerMock })); +vi.mock("../../../../../defaultmodules/newsfeed/newsfeedfetcher.js", () => ({ + default: mocks.MockNewsfeedFetcher +})); + +import helper from "../../../../../defaultmodules/newsfeed/node_helper.js"; +const loadNewsfeedNodeHelper = () => { + mocks.fetcherInstances.length = 0; helper.fetchers = {}; helper.sendSocketNotification = vi.fn(); - return { helper, fetcherInstances, loggerMock }; + return { helper, fetcherInstances: mocks.fetcherInstances, loggerMock: mocks.loggerMock }; }; afterEach(() => { vi.resetAllMocks(); - vi.resetModules(); });