From 7694d6fa867ebeee4695667e2561da0a7ff11622 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Sat, 1 Oct 2022 20:14:26 +0200 Subject: [PATCH 001/204] Prepare 2.22.0-develop --- CHANGELOG.md | 20 ++++++++++++++++---- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 137453d0d3..5bc851268f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,22 +5,34 @@ This project adheres to [Semantic Versioning](https://semver.org/). ❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror². +## [2.22.0] - Unreleased (Develop) + +_This release is scheduled to be released on 2023-01-01._ + +### Added + +### Removed + +### Updated + +### Fixed + ## [2.21.0] - 2022-10-01 Special thanks to: @BKeyport, @buxxi, @davide125, @khassel, @kolbyjack, @krukle, @MikeBishop, @rejas, @sdetweil, @SkySails and @veeck -## Added +### Added - Possibility to fetch calendars through socket notifications. - New scripts `install-mm` (and `install-mm:dev`) for simplifying mm installation (now: `npm run install-mm`) and adding params `--no-audit --no-fund --no-update-notifier` for less noise. - New `showTimeToday` option in calendar module shows time for current-day events even if `timeFormat` is `"relative"`. - Add hourly forecasts, apparent temperature & custom location name to SMHI weather provider. -## Removed +### Removed - Old weather deprecated modules `currentweather` and `weatherforecast`. -## Updated +### Updated - Removed `DAYAFTERTOMORROW` from English. - Update dependencies. @@ -28,7 +40,7 @@ Special thanks to: @BKeyport, @buxxi, @davide125, @khassel, @kolbyjack, @krukle, - Updated font tree to use variables consistantly. - Removed deprecated Docker Repository from issue template. -## Fixed +### Fixed - Broadcast all calendar events while still honoring global and per-calendar maximumEntries. - Respect rss ttl provided by newsfeed (#2883). diff --git a/package-lock.json b/package-lock.json index b82132f3f4..55d893738b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "magicmirror", - "version": "2.21.0", + "version": "2.22.0-develop", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "magicmirror", - "version": "2.21.0", + "version": "2.22.0-develop", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index b805bb9f2c..8f1aaf1eb3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "magicmirror", - "version": "2.21.0", + "version": "2.22.0-develop", "description": "The open source modular smart mirror platform.", "main": "js/electron.js", "scripts": { From f04d578704dc3489a6a8c86200344173d0b00343 Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Tue, 4 Oct 2022 10:15:24 +0200 Subject: [PATCH 002/204] improve tests (#2923) use es6 syntax in all tests, split weather tests, remove callbacks --- CHANGELOG.md | 2 + package.json | 1 + tests/configs/default.js | 2 +- tests/configs/modules/positions.js | 2 +- tests/e2e/env_spec.js | 31 +- tests/e2e/fonts.js | 15 +- tests/e2e/global-setup.js | 33 ++- tests/e2e/ipWhitelist_spec.js | 31 +- tests/e2e/mock-console.js | 4 +- tests/e2e/modules/alert_spec.js | 14 +- tests/e2e/modules/basic-auth.js | 8 +- tests/e2e/modules/calendar_spec.js | 125 ++++---- tests/e2e/modules/clock_es_spec.js | 48 ++- tests/e2e/modules/clock_spec.js | 87 +++--- tests/e2e/modules/compliments_spec.js | 74 ++--- tests/e2e/modules/helloworld_spec.js | 28 +- tests/e2e/modules/mocks/weather_current.js | 4 +- tests/e2e/modules/mocks/weather_forecast.js | 4 +- tests/e2e/modules/newsfeed_spec.js | 86 +++--- tests/e2e/modules/weather-functions.js | 29 ++ tests/e2e/modules/weather_current_spec.js | 130 +++++++++ tests/e2e/modules/weather_forecast_spec.js | 96 ++++++ tests/e2e/modules/weather_spec.js | 273 ------------------ tests/e2e/modules_display_spec.js | 26 +- tests/e2e/modules_position_spec.js | 14 +- tests/e2e/port_config.js | 31 +- tests/e2e/translations_spec.js | 58 ++-- tests/e2e/vendor_spec.js | 25 +- tests/e2e/without_modules.js | 24 +- tests/electron/env_spec.js | 16 +- tests/unit/classes/class_spec.js | 34 +-- tests/unit/classes/deprecated_spec.js | 6 +- tests/unit/classes/translator_spec.js | 102 +++---- tests/unit/classes/utils_spec.js | 14 +- tests/unit/functions/calendar_spec.js | 44 +-- tests/unit/functions/cmp_versions_spec.js | 14 +- tests/unit/functions/newsfeed_spec.js | 6 +- .../unit/functions/updatenotification_spec.js | 24 +- tests/unit/functions/weather_object_spec.js | 10 +- .../unit/global_vars/defaults_modules_spec.js | 4 +- tests/unit/global_vars/root_path_spec.js | 10 +- 41 files changed, 750 insertions(+), 839 deletions(-) create mode 100644 tests/e2e/modules/weather-functions.js create mode 100644 tests/e2e/modules/weather_current_spec.js create mode 100644 tests/e2e/modules/weather_forecast_spec.js delete mode 100644 tests/e2e/modules/weather_spec.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc851268f..2b3b5cf5e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ _This release is scheduled to be released on 2023-01-01._ ### Updated +- updated e2e tests (moved `done()` in helper functions) and use es6 syntax in all tests + ### Fixed ## [2.21.0] - 2022-10-01 diff --git a/package.json b/package.json index 8f1aaf1eb3..e5c07ff25c 100644 --- a/package.json +++ b/package.json @@ -134,6 +134,7 @@ "testPathIgnorePatterns": [ "/tests/e2e/modules/mocks", "/tests/e2e/modules/basic-auth.js", + "/tests/e2e/modules/weather-functions.js", "/tests/e2e/global-setup.js", "/tests/e2e/mock-console.js" ] diff --git a/tests/configs/default.js b/tests/configs/default.js index 7d0f8eed15..509dfd9860 100644 --- a/tests/configs/default.js +++ b/tests/configs/default.js @@ -3,7 +3,7 @@ * By Rodrigo Ramírez Norambuena https://rodrigoramirez.com * MIT Licensed. */ -exports.configFactory = function (options) { +exports.configFactory = (options) => { return Object.assign( { electronOptions: { diff --git a/tests/configs/modules/positions.js b/tests/configs/modules/positions.js index 6086fadd4c..77ad8f72c9 100644 --- a/tests/configs/modules/positions.js +++ b/tests/configs/modules/positions.js @@ -6,7 +6,7 @@ let config = { modules: // Using exotic content. This is why don't accept go to JSON configuration file - (function () { + (() => { let positions = ["top_bar", "top_left", "top_center", "top_right", "upper_third", "middle_center", "lower_third", "bottom_left", "bottom_center", "bottom_right", "bottom_bar", "fullscreen_above", "fullscreen_below"]; let modules = Array(); for (let idx in positions) { diff --git a/tests/e2e/env_spec.js b/tests/e2e/env_spec.js index 06a09d6d5e..965d728ee7 100644 --- a/tests/e2e/env_spec.js +++ b/tests/e2e/env_spec.js @@ -1,34 +1,27 @@ -const fetch = require("fetch"); const helpers = require("./global-setup"); describe("App environment", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/default.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); afterAll(async () => { await helpers.stopApplication(); }); - it("get request from http://localhost:8080 should return 200", (done) => { - fetch("http://localhost:8080").then((res) => { - done(); - expect(res.status).toBe(200); - }); + it("get request from http://localhost:8080 should return 200", async () => { + const res = await helpers.fetch("http://localhost:8080"); + expect(res.status).toBe(200); }); - it("get request from http://localhost:8080/nothing should return 404", (done) => { - fetch("http://localhost:8080/nothing").then((res) => { - done(); - expect(res.status).toBe(404); - }); + it("get request from http://localhost:8080/nothing should return 404", async () => { + const res = await helpers.fetch("http://localhost:8080/nothing"); + expect(res.status).toBe(404); }); - it("should show the title MagicMirror²", (done) => { - helpers.waitForElement("title").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toBe("MagicMirror²"); - }); + it("should show the title MagicMirror²", async () => { + const elem = await helpers.waitForElement("title"); + expect(elem).not.toBe(null); + expect(elem.textContent).toBe("MagicMirror²"); }); }); diff --git a/tests/e2e/fonts.js b/tests/e2e/fonts.js index 056b0306ea..9110935e83 100644 --- a/tests/e2e/fonts.js +++ b/tests/e2e/fonts.js @@ -1,7 +1,6 @@ -const fetch = require("fetch"); const helpers = require("./global-setup"); -describe("All font files from roboto.css should be downloadable", function () { +describe("All font files from roboto.css should be downloadable", () => { const fontFiles = []; // Statements below filters out all 'url' lines in the CSS file const fileContent = require("fs").readFileSync(__dirname + "/../../fonts/roboto.css", "utf8"); @@ -14,18 +13,16 @@ describe("All font files from roboto.css should be downloadable", function () { match = regex.exec(fileContent); } - beforeAll(function () { + beforeAll(() => { helpers.startApplication("tests/configs/without_modules.js"); }); - afterAll(async function () { + afterAll(async () => { await helpers.stopApplication(); }); - test.each(fontFiles)("should return 200 HTTP code for file '%s'", (fontFile, done) => { + test.each(fontFiles)("should return 200 HTTP code for file '%s'", async (fontFile) => { const fontUrl = "http://localhost:8080/fonts/" + fontFile; - fetch(fontUrl).then((res) => { - expect(res.status).toBe(200); - done(); - }); + const res = await helpers.fetch(fontUrl); + expect(res.status).toBe(200); }); }); diff --git a/tests/e2e/global-setup.js b/tests/e2e/global-setup.js index 1cd9a2c0b1..1b91fe3725 100644 --- a/tests/e2e/global-setup.js +++ b/tests/e2e/global-setup.js @@ -1,4 +1,5 @@ const jsdom = require("jsdom"); +const corefetch = require("fetch"); exports.startApplication = (configFilename, exec) => { jest.resetModules(); @@ -21,14 +22,16 @@ exports.stopApplication = async () => { await new Promise((resolve) => setTimeout(resolve, 100)); }; -exports.getDocument = (callback) => { - const url = "http://" + (config.address || "localhost") + ":" + (config.port || "8080"); - jsdom.JSDOM.fromURL(url, { resources: "usable", runScripts: "dangerously" }).then((dom) => { - dom.window.name = "jsdom"; - dom.window.onload = () => { - global.document = dom.window.document; - callback(); - }; +exports.getDocument = () => { + return new Promise((resolve) => { + const url = "http://" + (config.address || "localhost") + ":" + (config.port || "8080"); + jsdom.JSDOM.fromURL(url, { resources: "usable", runScripts: "dangerously" }).then((dom) => { + dom.window.name = "jsdom"; + dom.window.onload = () => { + global.document = dom.window.document; + resolve(); + }; + }); }); }; @@ -71,3 +74,17 @@ exports.waitForAllElements = (selector) => { }, 100); }); }; + +exports.fetch = (url) => { + return new Promise((resolve) => { + corefetch(url).then((res) => { + resolve(res); + }); + }); +}; + +exports.testMatch = async (element, regex) => { + const elem = await this.waitForElement(element); + expect(elem).not.toBe(null); + expect(elem.textContent).toMatch(regex); +}; diff --git a/tests/e2e/ipWhitelist_spec.js b/tests/e2e/ipWhitelist_spec.js index 3c3a06c3e3..187355cbb0 100644 --- a/tests/e2e/ipWhitelist_spec.js +++ b/tests/e2e/ipWhitelist_spec.js @@ -1,36 +1,31 @@ -const fetch = require("fetch"); const helpers = require("./global-setup"); -describe("ipWhitelist directive configuration", function () { - describe("Set ipWhitelist without access", function () { - beforeAll(function () { +describe("ipWhitelist directive configuration", () => { + describe("Set ipWhitelist without access", () => { + beforeAll(() => { helpers.startApplication("tests/configs/noIpWhiteList.js"); }); - afterAll(async function () { + afterAll(async () => { await helpers.stopApplication(); }); - it("should return 403", function (done) { - fetch("http://localhost:8080").then((res) => { - expect(res.status).toBe(403); - done(); - }); + it("should return 403", async () => { + const res = await helpers.fetch("http://localhost:8080"); + expect(res.status).toBe(403); }); }); - describe("Set ipWhitelist []", function () { - beforeAll(function () { + describe("Set ipWhitelist []", () => { + beforeAll(() => { helpers.startApplication("tests/configs/empty_ipWhiteList.js"); }); - afterAll(async function () { + afterAll(async () => { await helpers.stopApplication(); }); - it("should return 200", function (done) { - fetch("http://localhost:8080").then((res) => { - expect(res.status).toBe(200); - done(); - }); + it("should return 200", async () => { + const res = await helpers.fetch("http://localhost:8080"); + expect(res.status).toBe(200); }); }); }); diff --git a/tests/e2e/mock-console.js b/tests/e2e/mock-console.js index 454a8e4672..3f9909f11a 100644 --- a/tests/e2e/mock-console.js +++ b/tests/e2e/mock-console.js @@ -3,13 +3,13 @@ * * @param {string} err The error message. */ -function mockError(err) { +const mockError = (err) => { if (err.includes("ECONNREFUSED") || err.includes("ECONNRESET") || err.includes("socket hang up") || err.includes("exports is not defined") || err.includes("write EPIPE")) { jest.fn(); } else { console.dir(err); } -} +}; global.console = { log: jest.fn(), diff --git a/tests/e2e/modules/alert_spec.js b/tests/e2e/modules/alert_spec.js index 9d246ed5e3..bb55e0db1f 100644 --- a/tests/e2e/modules/alert_spec.js +++ b/tests/e2e/modules/alert_spec.js @@ -1,19 +1,17 @@ const helpers = require("../global-setup"); describe("Alert module", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/alert/default.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); afterAll(async () => { await helpers.stopApplication(); }); - it("should show the welcome message", (done) => { - helpers.waitForElement(".ns-box .ns-box-inner .light.bright.small").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toContain("Welcome, start was successful!"); - }); + it("should show the welcome message", async () => { + const elem = await helpers.waitForElement(".ns-box .ns-box-inner .light.bright.small"); + expect(elem).not.toBe(null); + expect(elem.textContent).toContain("Welcome, start was successful!"); }); }); diff --git a/tests/e2e/modules/basic-auth.js b/tests/e2e/modules/basic-auth.js index 9ace932bc0..d4525c175f 100644 --- a/tests/e2e/modules/basic-auth.js +++ b/tests/e2e/modules/basic-auth.js @@ -20,10 +20,10 @@ for (let directory of directories) { let server; -exports.listen = function () { - server = app.listen.apply(app, arguments); +exports.listen = (...args) => { + server = app.listen.apply(app, args); }; -exports.close = function (callback) { - server.close(callback); +exports.close = async () => { + await server.close(); }; diff --git a/tests/e2e/modules/calendar_spec.js b/tests/e2e/modules/calendar_spec.js index 97d42b2fd8..a482f1d9e1 100644 --- a/tests/e2e/modules/calendar_spec.js +++ b/tests/e2e/modules/calendar_spec.js @@ -3,29 +3,24 @@ const serverBasicAuth = require("./basic-auth.js"); describe("Calendar module", () => { /** - * @param {string} done test done * @param {string} element css selector * @param {string} result expected number * @param {string} not reverse result */ - const testElementLength = (done, element, result, not) => { - helpers.waitForAllElements(element).then((elem) => { - done(); - expect(elem).not.toBe(null); - if (not === "not") { - expect(elem.length).not.toBe(result); - } else { - expect(elem.length).toBe(result); - } - }); + const testElementLength = async (element, result, not) => { + const elem = await helpers.waitForAllElements(element); + expect(elem).not.toBe(null); + if (not === "not") { + expect(elem.length).not.toBe(result); + } else { + expect(elem.length).toBe(result); + } }; - const testTextContain = (done, element, text) => { - helpers.waitForElement(element, "undefinedLoading").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toContain(text); - }); + const testTextContain = async (element, text) => { + const elem = await helpers.waitForElement(element, "undefinedLoading"); + expect(elem).not.toBe(null); + expect(elem.textContent).toContain(text); }; afterAll(async () => { @@ -33,133 +28,133 @@ describe("Calendar module", () => { }); describe("Default configuration", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/calendar/default.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should show the default maximumEntries of 10", (done) => { - testElementLength(done, ".calendar .event", 10); + it("should show the default maximumEntries of 10", async () => { + await testElementLength(".calendar .event", 10); }); - it("should show the default calendar symbol in each event", (done) => { - testElementLength(done, ".calendar .event .fa-calendar-alt", 0, "not"); + it("should show the default calendar symbol in each event", async () => { + await testElementLength(".calendar .event .fa-calendar-alt", 0, "not"); }); }); describe("Custom configuration", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/calendar/custom.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should show the custom maximumEntries of 4", (done) => { - testElementLength(done, ".calendar .event", 4); + it("should show the custom maximumEntries of 4", async () => { + await testElementLength(".calendar .event", 4); }); - it("should show the custom calendar symbol in each event", (done) => { - testElementLength(done, ".calendar .event .fa-birthday-cake", 4); + it("should show the custom calendar symbol in each event", async () => { + await testElementLength(".calendar .event .fa-birthday-cake", 4); }); - it("should show two custom icons for repeating events", (done) => { - testElementLength(done, ".calendar .event .fa-undo", 2); + it("should show two custom icons for repeating events", async () => { + await testElementLength(".calendar .event .fa-undo", 2); }); - it("should show two custom icons for day events", (done) => { - testElementLength(done, ".calendar .event .fa-calendar-day", 2); + it("should show two custom icons for day events", async () => { + await testElementLength(".calendar .event .fa-calendar-day", 2); }); }); describe("Recurring event", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/calendar/recurring.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should show the recurring birthday event 6 times", (done) => { - testElementLength(done, ".calendar .event", 6); + it("should show the recurring birthday event 6 times", async () => { + await testElementLength(".calendar .event", 6); }); }); process.setMaxListeners(0); for (let i = -12; i < 12; i++) { describe("Recurring event per timezone", () => { - beforeAll((done) => { + beforeAll(async () => { Date.prototype.getTimezoneOffset = () => { return i * 60; }; helpers.startApplication("tests/configs/modules/calendar/recurring.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it('should contain text "Mar 25th" in timezone UTC ' + -i, (done) => { - testTextContain(done, ".calendar", "Mar 25th"); + it('should contain text "Mar 25th" in timezone UTC ' + -i, async () => { + await testTextContain(".calendar", "Mar 25th"); }); }); } describe("Changed port", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/calendar/changed-port.js"); serverBasicAuth.listen(8010); - helpers.getDocument(done); + await helpers.getDocument(); }); - afterAll((done) => { - serverBasicAuth.close(done()); + afterAll(async () => { + await serverBasicAuth.close(); }); - it("should return TestEvents", (done) => { - testElementLength(done, ".calendar .event", 0, "not"); + it("should return TestEvents", async () => { + await testElementLength(".calendar .event", 0, "not"); }); }); describe("Basic auth", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/calendar/basic-auth.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should return TestEvents", (done) => { - testElementLength(done, ".calendar .event", 0, "not"); + it("should return TestEvents", async () => { + await testElementLength(".calendar .event", 0, "not"); }); }); describe("Basic auth by default", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/calendar/auth-default.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should return TestEvents", (done) => { - testElementLength(done, ".calendar .event", 0, "not"); + it("should return TestEvents", async () => { + await testElementLength(".calendar .event", 0, "not"); }); }); describe("Basic auth backward compatibility configuration: DEPRECATED", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/calendar/old-basic-auth.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should return TestEvents", (done) => { - testElementLength(done, ".calendar .event", 0, "not"); + it("should return TestEvents", async () => { + await testElementLength(".calendar .event", 0, "not"); }); }); describe("Fail Basic auth", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/calendar/fail-basic-auth.js"); serverBasicAuth.listen(8020); - helpers.getDocument(done); + await helpers.getDocument(); }); - afterAll((done) => { - serverBasicAuth.close(done()); + afterAll(async () => { + await serverBasicAuth.close(); }); - it("should show Unauthorized error", (done) => { - testTextContain(done, ".calendar", "Error in the calendar module. Authorization failed"); + it("should show Unauthorized error", async () => { + await testTextContain(".calendar", "Error in the calendar module. Authorization failed"); }); }); }); diff --git a/tests/e2e/modules/clock_es_spec.js b/tests/e2e/modules/clock_es_spec.js index a02a39c8e9..131d6b5876 100644 --- a/tests/e2e/modules/clock_es_spec.js +++ b/tests/e2e/modules/clock_es_spec.js @@ -5,69 +5,61 @@ describe("Clock set to spanish language module", () => { await helpers.stopApplication(); }); - const testMatch = (done, element, regex) => { - helpers.waitForElement(element).then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toMatch(regex); - }); - }; - describe("with default 24hr clock config", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/clock/es/clock_24hr.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("shows date with correct format", (done) => { + it("shows date with correct format", async () => { const dateRegex = /^(?:lunes|martes|miércoles|jueves|viernes|sábado|domingo), \d{1,2} de (?:enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre) de \d{4}$/; - testMatch(done, ".clock .date", dateRegex); + await helpers.testMatch(".clock .date", dateRegex); }); - it("shows time in 24hr format", (done) => { + it("shows time in 24hr format", async () => { const timeRegex = /^(?:2[0-3]|[01]\d):[0-5]\d[0-5]\d$/; - testMatch(done, ".clock .time", timeRegex); + await helpers.testMatch(".clock .time", timeRegex); }); }); describe("with default 12hr clock config", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/clock/es/clock_12hr.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("shows date with correct format", (done) => { + it("shows date with correct format", async () => { const dateRegex = /^(?:lunes|martes|miércoles|jueves|viernes|sábado|domingo), \d{1,2} de (?:enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre) de \d{4}$/; - testMatch(done, ".clock .date", dateRegex); + await helpers.testMatch(".clock .date", dateRegex); }); - it("shows time in 12hr format", (done) => { + it("shows time in 12hr format", async () => { const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[ap]m$/; - testMatch(done, ".clock .time", timeRegex); + await helpers.testMatch(".clock .time", timeRegex); }); }); describe("with showPeriodUpper config enabled", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/clock/es/clock_showPeriodUpper.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("shows 12hr time with upper case AM/PM", (done) => { + it("shows 12hr time with upper case AM/PM", async () => { const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[AP]M$/; - testMatch(done, ".clock .time", timeRegex); + await helpers.testMatch(".clock .time", timeRegex); }); }); describe("with showWeek config enabled", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/clock/es/clock_showWeek.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("shows week with correct format", (done) => { + it("shows week with correct format", async () => { const weekRegex = /^Semana [0-9]{1,2}$/; - testMatch(done, ".clock .week", weekRegex); + await helpers.testMatch(".clock .week", weekRegex); }); }); }); diff --git a/tests/e2e/modules/clock_spec.js b/tests/e2e/modules/clock_spec.js index 77ea8e6b90..1d53a278eb 100644 --- a/tests/e2e/modules/clock_spec.js +++ b/tests/e2e/modules/clock_spec.js @@ -6,118 +6,105 @@ describe("Clock module", () => { await helpers.stopApplication(); }); - const testMatch = (done, element, regex) => { - helpers.waitForElement(element).then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toMatch(regex); - }); - }; - describe("with default 24hr clock config", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/clock/clock_24hr.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should show the date in the correct format", (done) => { + it("should show the date in the correct format", async () => { const dateRegex = /^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}, \d{4}$/; - testMatch(done, ".clock .date", dateRegex); + await helpers.testMatch(".clock .date", dateRegex); }); - it("should show the time in 24hr format", (done) => { + it("should show the time in 24hr format", async () => { const timeRegex = /^(?:2[0-3]|[01]\d):[0-5]\d[0-5]\d$/; - testMatch(done, ".clock .time", timeRegex); + await helpers.testMatch(".clock .time", timeRegex); }); }); describe("with default 12hr clock config", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/clock/clock_12hr.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should show the date in the correct format", (done) => { + it("should show the date in the correct format", async () => { const dateRegex = /^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}, \d{4}$/; - testMatch(done, ".clock .date", dateRegex); + await helpers.testMatch(".clock .date", dateRegex); }); - it("should show the time in 12hr format", (done) => { + it("should show the time in 12hr format", async () => { const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[ap]m$/; - testMatch(done, ".clock .time", timeRegex); + await helpers.testMatch(".clock .time", timeRegex); }); }); describe("with showPeriodUpper config enabled", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/clock/clock_showPeriodUpper.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should show 12hr time with upper case AM/PM", (done) => { + it("should show 12hr time with upper case AM/PM", async () => { const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[AP]M$/; - testMatch(done, ".clock .time", timeRegex); + await helpers.testMatch(".clock .time", timeRegex); }); }); describe("with displaySeconds config disabled", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/clock/clock_displaySeconds_false.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should show 12hr time without seconds am/pm", (done) => { + it("should show 12hr time without seconds am/pm", async () => { const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[ap]m$/; - testMatch(done, ".clock .time", timeRegex); + await helpers.testMatch(".clock .time", timeRegex); }); }); describe("with showTime config disabled", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/clock/clock_showTime.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should not show the time when digital clock is shown", (done) => { - const elem = document.querySelector(".clock .digital .time"); - done(); + it("should not show the time when digital clock is shown", async () => { + const elem = await document.querySelector(".clock .digital .time"); expect(elem).toBe(null); }); }); describe("with showWeek config enabled", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/clock/clock_showWeek.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should show the week in the correct format", (done) => { + it("should show the week in the correct format", async () => { const weekRegex = /^Week [0-9]{1,2}$/; - testMatch(done, ".clock .week", weekRegex); + await helpers.testMatch(".clock .week", weekRegex); }); - it("should show the week with the correct number of week of year", (done) => { + it("should show the week with the correct number of week of year", async () => { const currentWeekNumber = moment().week(); const weekToShow = "Week " + currentWeekNumber; - helpers.waitForElement(".clock .week").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toBe(weekToShow); - }); + const elem = await helpers.waitForElement(".clock .week"); + expect(elem).not.toBe(null); + expect(elem.textContent).toBe(weekToShow); }); }); describe("with analog clock face enabled", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/clock/clock_analog.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should show the analog clock face", (done) => { - helpers.waitForElement(".clockCircle").then((elem) => { - done(); - expect(elem).not.toBe(null); - }); + it("should show the analog clock face", async () => { + const elem = helpers.waitForElement(".clockCircle"); + expect(elem).not.toBe(null); }); }); }); diff --git a/tests/e2e/modules/compliments_spec.js b/tests/e2e/modules/compliments_spec.js index d53b968760..faf1d6c322 100644 --- a/tests/e2e/modules/compliments_spec.js +++ b/tests/e2e/modules/compliments_spec.js @@ -1,97 +1,87 @@ const helpers = require("../global-setup"); -/** - * move similar tests in function doTest - * - * @param {string} done test done - * @param {Array} complimentsArray The array of compliments. - */ -const doTest = (done, complimentsArray) => { - helpers.waitForElement(".compliments").then((elem) => { +describe("Compliments module", () => { + /** + * move similar tests in function doTest + * + * @param {Array} complimentsArray The array of compliments. + */ + const doTest = async (complimentsArray) => { + let elem = await helpers.waitForElement(".compliments"); expect(elem).not.toBe(null); - helpers.waitForElement(".module-content").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(complimentsArray).toContain(elem.textContent); - }); - }); -}; + elem = await helpers.waitForElement(".module-content"); + expect(elem).not.toBe(null); + expect(complimentsArray).toContain(elem.textContent); + }; -describe("Compliments module", () => { afterAll(async () => { await helpers.stopApplication(); }); describe("parts of days", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/compliments/compliments_parts_day.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("if Morning compliments for that part of day", (done) => { + it("if Morning compliments for that part of day", async () => { const hour = new Date().getHours(); if (hour >= 3 && hour < 12) { // if morning check - doTest(done, ["Hi", "Good Morning", "Morning test"]); - } else { - done(); + await doTest(["Hi", "Good Morning", "Morning test"]); } }); - it("if Afternoon show Compliments for that part of day", (done) => { + it("if Afternoon show Compliments for that part of day", async () => { const hour = new Date().getHours(); if (hour >= 12 && hour < 17) { // if afternoon check - doTest(done, ["Hello", "Good Afternoon", "Afternoon test"]); - } else { - done(); + await doTest(["Hello", "Good Afternoon", "Afternoon test"]); } }); - it("if Evening show Compliments for that part of day", (done) => { + it("if Evening show Compliments for that part of day", async () => { const hour = new Date().getHours(); if (!(hour >= 3 && hour < 12) && !(hour >= 12 && hour < 17)) { // if evening check - doTest(done, ["Hello There", "Good Evening", "Evening test"]); - } else { - done(); + await doTest(["Hello There", "Good Evening", "Evening test"]); } }); }); describe("Feature anytime in compliments module", () => { describe("Set anytime and empty compliments for morning, evening and afternoon ", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/compliments/compliments_anytime.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("Show anytime because if configure empty parts of day compliments and set anytime compliments", (done) => { - doTest(done, ["Anytime here"]); + it("Show anytime because if configure empty parts of day compliments and set anytime compliments", async () => { + await doTest(["Anytime here"]); }); }); describe("Only anytime present in configuration compliments", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/compliments/compliments_only_anytime.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("Show anytime compliments", (done) => { - doTest(done, ["Anytime here"]); + it("Show anytime compliments", async () => { + await doTest(["Anytime here"]); }); }); }); describe("Feature date in compliments module", () => { describe("Set date and empty compliments for anytime, morning, evening and afternoon", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/compliments/compliments_date.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("Show happy new year compliment on new years day", (done) => { - doTest(done, ["Happy new year!"]); + it("Show happy new year compliment on new years day", async () => { + await doTest(["Happy new year!"]); }); }); }); diff --git a/tests/e2e/modules/helloworld_spec.js b/tests/e2e/modules/helloworld_spec.js index cba9afb452..60b9681ad3 100644 --- a/tests/e2e/modules/helloworld_spec.js +++ b/tests/e2e/modules/helloworld_spec.js @@ -6,32 +6,28 @@ describe("Test helloworld module", () => { }); describe("helloworld set config text", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/helloworld/helloworld.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("Test message helloworld module", (done) => { - helpers.waitForElement(".helloworld").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toContain("Test HelloWorld Module"); - }); + it("Test message helloworld module", async () => { + const elem = await helpers.waitForElement(".helloworld"); + expect(elem).not.toBe(null); + expect(elem.textContent).toContain("Test HelloWorld Module"); }); }); describe("helloworld default config text", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/helloworld/helloworld_default.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("Test message helloworld module", (done) => { - helpers.waitForElement(".helloworld").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toContain("Hello World!"); - }); + it("Test message helloworld module", async () => { + const elem = await helpers.waitForElement(".helloworld"); + expect(elem).not.toBe(null); + expect(elem.textContent).toContain("Hello World!"); }); }); }); diff --git a/tests/e2e/modules/mocks/weather_current.js b/tests/e2e/modules/mocks/weather_current.js index c466129afc..c7b22caf70 100644 --- a/tests/e2e/modules/mocks/weather_current.js +++ b/tests/e2e/modules/mocks/weather_current.js @@ -4,7 +4,7 @@ const _ = require("lodash"); * @param {object} extendedData extra data to add to the default mock data * @returns {string} mocked current weather data */ -function generateWeather(extendedData = {}) { +const generateWeather = (extendedData = {}) => { return JSON.stringify( _.merge( {}, @@ -59,6 +59,6 @@ function generateWeather(extendedData = {}) { extendedData ) ); -} +}; module.exports = generateWeather; diff --git a/tests/e2e/modules/mocks/weather_forecast.js b/tests/e2e/modules/mocks/weather_forecast.js index 4c0ef9c934..517f53a7d4 100644 --- a/tests/e2e/modules/mocks/weather_forecast.js +++ b/tests/e2e/modules/mocks/weather_forecast.js @@ -4,7 +4,7 @@ const _ = require("lodash"); * @param {object} extendedData extra data to add to the default mock data * @returns {string} mocked forecast weather data */ -function generateWeatherForecast(extendedData = {}) { +const generateWeatherForecast = (extendedData = {}) => { return JSON.stringify( _.merge( {}, @@ -110,6 +110,6 @@ function generateWeatherForecast(extendedData = {}) { extendedData ) ); -} +}; module.exports = generateWeatherForecast; diff --git a/tests/e2e/modules/newsfeed_spec.js b/tests/e2e/modules/newsfeed_spec.js index 11ef59121c..8d75fd17f7 100644 --- a/tests/e2e/modules/newsfeed_spec.js +++ b/tests/e2e/modules/newsfeed_spec.js @@ -6,86 +6,72 @@ describe("Newsfeed module", () => { }); describe("Default configuration", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/newsfeed/default.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should show the newsfeed title", (done) => { - helpers.waitForElement(".newsfeed .newsfeed-source").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toContain("Rodrigo Ramirez Blog"); - }); + it("should show the newsfeed title", async () => { + const elem = await helpers.waitForElement(".newsfeed .newsfeed-source"); + expect(elem).not.toBe(null); + expect(elem.textContent).toContain("Rodrigo Ramirez Blog"); }); - it("should show the newsfeed article", (done) => { - helpers.waitForElement(".newsfeed .newsfeed-title").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toContain("QPanel"); - }); + it("should show the newsfeed article", async () => { + const elem = await helpers.waitForElement(".newsfeed .newsfeed-title"); + expect(elem).not.toBe(null); + expect(elem.textContent).toContain("QPanel"); }); - it("should NOT show the newsfeed description", (done) => { - helpers.waitForElement(".newsfeed").then((elem) => { - const element = document.querySelector(".newsfeed .newsfeed-desc"); - done(); - expect(element).toBe(null); - }); + it("should NOT show the newsfeed description", async () => { + await helpers.waitForElement(".newsfeed"); + const element = document.querySelector(".newsfeed .newsfeed-desc"); + expect(element).toBe(null); }); }); describe("Custom configuration", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/newsfeed/prohibited_words.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should not show articles with prohibited words", (done) => { - helpers.waitForElement(".newsfeed .newsfeed-title").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toContain("Problema VirtualBox"); - }); + it("should not show articles with prohibited words", async () => { + const elem = await helpers.waitForElement(".newsfeed .newsfeed-title"); + expect(elem).not.toBe(null); + expect(elem.textContent).toContain("Problema VirtualBox"); }); - it("should show the newsfeed description", (done) => { - helpers.waitForElement(".newsfeed .newsfeed-desc").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent.length).not.toBe(0); - }); + it("should show the newsfeed description", async () => { + const elem = await helpers.waitForElement(".newsfeed .newsfeed-desc"); + expect(elem).not.toBe(null); + expect(elem.textContent.length).not.toBe(0); }); }); describe("Invalid configuration", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/newsfeed/incorrect_url.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should show malformed url warning", (done) => { - helpers.waitForElement(".newsfeed .small", "No news at the moment.").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toContain("Error in the Newsfeed module. Malformed url."); - }); + it("should show malformed url warning", async () => { + const elem = await helpers.waitForElement(".newsfeed .small", "No news at the moment."); + expect(elem).not.toBe(null); + expect(elem.textContent).toContain("Error in the Newsfeed module. Malformed url."); }); }); describe("Ignore items", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/newsfeed/ignore_items.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); - it("should show empty items info message", (done) => { - helpers.waitForElement(".newsfeed .small").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toContain("No news at the moment."); - }); + it("should show empty items info message", async () => { + const elem = await helpers.waitForElement(".newsfeed .small"); + expect(elem).not.toBe(null); + expect(elem.textContent).toContain("No news at the moment."); }); }); }); diff --git a/tests/e2e/modules/weather-functions.js b/tests/e2e/modules/weather-functions.js new file mode 100644 index 0000000000..156906564d --- /dev/null +++ b/tests/e2e/modules/weather-functions.js @@ -0,0 +1,29 @@ +const helpers = require("../global-setup"); +const path = require("path"); +const fs = require("fs"); +const { generateWeather, generateWeatherForecast } = require("./mocks"); + +exports.getText = async (element, result) => { + const elem = await helpers.waitForElement(element); + expect(elem).not.toBe(null); + expect( + elem.textContent + .trim() + .replace(/(\r\n|\n|\r)/gm, "") + .replace(/[ ]+/g, " ") + ).toBe(result); +}; + +exports.startApp = async (configFile, additionalMockData) => { + let mockWeather; + if (configFile.includes("forecast")) { + mockWeather = generateWeatherForecast(additionalMockData); + } else { + mockWeather = generateWeather(additionalMockData); + } + let content = fs.readFileSync(path.resolve(__dirname + "../../../../" + configFile)).toString(); + content = content.replace("#####WEATHERDATA#####", mockWeather); + fs.writeFileSync(path.resolve(__dirname + "../../../../config/config.js"), content); + helpers.startApplication(""); + await helpers.getDocument(); +}; diff --git a/tests/e2e/modules/weather_current_spec.js b/tests/e2e/modules/weather_current_spec.js new file mode 100644 index 0000000000..0ee8eecf6f --- /dev/null +++ b/tests/e2e/modules/weather_current_spec.js @@ -0,0 +1,130 @@ +const moment = require("moment"); +const helpers = require("../global-setup"); +const weatherFunc = require("./weather-functions"); + +describe("Weather module", () => { + afterAll(async () => { + await helpers.stopApplication(); + }); + + describe("Current weather", () => { + describe("Default configuration", () => { + beforeAll(async () => { + await weatherFunc.startApp("tests/configs/modules/weather/currentweather_default.js", {}); + }); + + it("should render wind speed and wind direction", async () => { + await weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "6 WSW"); // now "12" + }); + + it("should render temperature with icon", async () => { + await weatherFunc.getText(".weather .large.light span.bright", "1.5°"); // now "1°C" + }); + + it("should render feels like temperature", async () => { + await weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like -5.6°"); // now "Feels like -6°C" + }); + }); + + describe("Default configuration with sunrise", () => { + beforeAll(async () => { + const sunrise = moment().startOf("day").unix(); + const sunset = moment().startOf("day").unix(); + await weatherFunc.startApp("tests/configs/modules/weather/currentweather_default.js", { sys: { sunrise, sunset } }); + }); + + it("should render sunrise", async () => { + await weatherFunc.getText(".weather .normal.medium span:nth-child(4)", "12:00 am"); + }); + }); + + describe("Default configuration with sunset", () => { + beforeAll(async () => { + const sunrise = moment().startOf("day").unix(); + const sunset = moment().endOf("day").unix(); + await weatherFunc.startApp("tests/configs/modules/weather/currentweather_default.js", { sys: { sunrise, sunset } }); + }); + + it("should render sunset", async () => { + await weatherFunc.getText(".weather .normal.medium span:nth-child(4)", "11:59 pm"); + }); + }); + }); + + describe("Compliments Integration", () => { + beforeAll(async () => { + await weatherFunc.startApp("tests/configs/modules/weather/currentweather_compliments.js", {}); + }); + + it("should render a compliment based on the current weather", async () => { + await weatherFunc.getText(".compliments .module-content span", "snow"); + }); + }); + + describe("Configuration Options", () => { + beforeAll(async () => { + await weatherFunc.startApp("tests/configs/modules/weather/currentweather_options.js", {}); + }); + + it("should render useBeaufort = false", async () => { + await weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "12"); + }); + + it("should render showWindDirectionAsArrow = true", async () => { + const elem = await helpers.waitForElement(".weather .normal.medium sup i.fa-long-arrow-alt-up"); + expect(elem).not.toBe(null); + expect(elem.outerHTML).toContain("transform:rotate(250deg);"); + }); + + it("should render showHumidity = true", async () => { + await weatherFunc.getText(".weather .normal.medium span:nth-child(3)", "93.7"); + }); + + it("should render degreeLabel = true for temp", async () => { + await weatherFunc.getText(".weather .large.light span.bright", "1°C"); + }); + + it("should render degreeLabel = true for feels like", async () => { + await weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like -6°C"); + }); + }); + + describe("Current weather units", () => { + beforeAll(async () => { + await weatherFunc.startApp("tests/configs/modules/weather/currentweather_units.js", { + main: { + temp: (1.49 * 9) / 5 + 32, + temp_min: (1 * 9) / 5 + 32, + temp_max: (2 * 9) / 5 + 32 + }, + wind: { + speed: 11.8 * 2.23694 + } + }); + }); + + it("should render imperial units for wind", async () => { + await weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "6 WSW"); + }); + + it("should render imperial units for temp", async () => { + await weatherFunc.getText(".weather .large.light span.bright", "34,7°"); + }); + + it("should render imperial units for feels like", async () => { + await weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like 22,0°"); + }); + + it("should render custom decimalSymbol = ',' for humidity", async () => { + await weatherFunc.getText(".weather .normal.medium span:nth-child(3)", "93,7"); + }); + + it("should render custom decimalSymbol = ',' for temp", async () => { + await weatherFunc.getText(".weather .large.light span.bright", "34,7°"); + }); + + it("should render custom decimalSymbol = ',' for feels like", async () => { + await weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like 22,0°"); + }); + }); +}); diff --git a/tests/e2e/modules/weather_forecast_spec.js b/tests/e2e/modules/weather_forecast_spec.js new file mode 100644 index 0000000000..e7bef559a3 --- /dev/null +++ b/tests/e2e/modules/weather_forecast_spec.js @@ -0,0 +1,96 @@ +const helpers = require("../global-setup"); +const weatherFunc = require("./weather-functions"); + +describe("Weather module: Weather Forecast", () => { + afterAll(async () => { + await helpers.stopApplication(); + }); + + describe("Default configuration", () => { + beforeAll(async () => { + await weatherFunc.startApp("tests/configs/modules/weather/forecastweather_default.js", {}); + }); + + const days = ["Today", "Tomorrow", "Sun", "Mon", "Tue"]; + for (const [index, day] of days.entries()) { + it("should render day " + day, async () => { + await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day); + }); + } + + const icons = ["day-cloudy", "rain", "day-sunny", "day-sunny", "day-sunny"]; + for (const [index, icon] of icons.entries()) { + it("should render icon " + icon, async () => { + const elem = await helpers.waitForElement(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(2) span.wi-${icon}`); + expect(elem).not.toBe(null); + }); + } + + const maxTemps = ["24.4°", "21.0°", "22.9°", "23.4°", "20.6°"]; + for (const [index, temp] of maxTemps.entries()) { + it("should render max temperature " + temp, async () => { + await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(3)`, temp); + }); + } + + const minTemps = ["15.3°", "13.6°", "13.8°", "13.9°", "10.9°"]; + for (const [index, temp] of minTemps.entries()) { + it("should render min temperature " + temp, async () => { + await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(4)`, temp); + }); + } + + const opacities = [1, 1, 0.8, 0.5333333333333333, 0.2666666666666667]; + for (const [index, opacity] of opacities.entries()) { + it("should render fading of rows with opacity=" + opacity, async () => { + const elem = await helpers.waitForElement(`.weather table.small tr:nth-child(${index + 1})`); + expect(elem).not.toBe(null); + expect(elem.outerHTML).toContain(``); + }); + } + }); + + describe("Absolute configuration", () => { + beforeAll(async () => { + await weatherFunc.startApp("tests/configs/modules/weather/forecastweather_absolute.js", {}); + }); + + const days = ["Fri", "Sat", "Sun", "Mon", "Tue"]; + for (const [index, day] of days.entries()) { + it("should render day " + day, async () => { + await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day); + }); + } + }); + + describe("Configuration Options", () => { + beforeAll(async () => { + await weatherFunc.startApp("tests/configs/modules/weather/forecastweather_options.js", {}); + }); + + it("should render custom table class", async () => { + const elem = await helpers.waitForElement(".weather table.myTableClass"); + expect(elem).not.toBe(null); + }); + + it("should render colored rows", async () => { + const table = await helpers.waitForElement(".weather table.myTableClass"); + expect(table).not.toBe(null); + expect(table.rows).not.toBe(null); + expect(table.rows.length).toBe(5); + }); + }); + + describe("Forecast weather units", () => { + beforeAll(async () => { + await weatherFunc.startApp("tests/configs/modules/weather/forecastweather_units.js", {}); + }); + + const temperatures = ["24_4°", "21_0°", "22_9°", "23_4°", "20_6°"]; + for (const [index, temp] of temperatures.entries()) { + it("should render custom decimalSymbol = '_' for temp " + temp, async () => { + await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(3)`, temp); + }); + } + }); +}); diff --git a/tests/e2e/modules/weather_spec.js b/tests/e2e/modules/weather_spec.js deleted file mode 100644 index 9816e153a3..0000000000 --- a/tests/e2e/modules/weather_spec.js +++ /dev/null @@ -1,273 +0,0 @@ -const moment = require("moment"); -const helpers = require("../global-setup"); -const path = require("path"); -const fs = require("fs"); -const { generateWeather, generateWeatherForecast } = require("./mocks"); - -describe("Weather module", () => { - /** - * @param {string} done test done - * @param {string} element css selector - * @param {string} result Expected text in given selector - */ - const getText = (done, element, result) => { - helpers.waitForElement(element).then((elem) => { - done(); - expect(elem).not.toBe(null); - expect( - elem.textContent - .trim() - .replace(/(\r\n|\n|\r)/gm, "") - .replace(/[ ]+/g, " ") - ).toBe(result); - }); - }; - - /** - * @param {string} configFile path to configuration file - * @param {string} additionalMockData special data for mocking - * @param {string} callback callback - */ - const startApp = (configFile, additionalMockData, callback) => { - let mockWeather; - if (configFile.includes("forecast")) { - mockWeather = generateWeatherForecast(additionalMockData); - } else { - mockWeather = generateWeather(additionalMockData); - } - let content = fs.readFileSync(path.resolve(__dirname + "../../../../" + configFile)).toString(); - content = content.replace("#####WEATHERDATA#####", mockWeather); - fs.writeFileSync(path.resolve(__dirname + "../../../../config/config.js"), content); - helpers.startApplication(""); - helpers.getDocument(callback); - }; - - afterAll(async () => { - await helpers.stopApplication(); - }); - - describe("Current weather", () => { - describe("Default configuration", () => { - beforeAll((done) => { - startApp("tests/configs/modules/weather/currentweather_default.js", {}, done); - }); - - it("should render wind speed and wind direction", (done) => { - getText(done, ".weather .normal.medium span:nth-child(2)", "6 WSW"); // now "12" - }); - - it("should render temperature with icon", (done) => { - getText(done, ".weather .large.light span.bright", "1.5°"); // now "1°C" - }); - - it("should render feels like temperature", (done) => { - getText(done, ".weather .normal.medium.feelslike span.dimmed", "Feels like -5.6°"); // now "Feels like -6°C" - }); - }); - - describe("Default configuration with sunrise", () => { - beforeAll((done) => { - const sunrise = moment().startOf("day").unix(); - const sunset = moment().startOf("day").unix(); - startApp("tests/configs/modules/weather/currentweather_default.js", { sys: { sunrise, sunset } }, done); - }); - - it("should render sunrise", (done) => { - getText(done, ".weather .normal.medium span:nth-child(4)", "12:00 am"); - }); - }); - - describe("Default configuration with sunset", () => { - beforeAll((done) => { - const sunrise = moment().startOf("day").unix(); - const sunset = moment().endOf("day").unix(); - startApp("tests/configs/modules/weather/currentweather_default.js", { sys: { sunrise, sunset } }, done); - }); - - it("should render sunset", (done) => { - getText(done, ".weather .normal.medium span:nth-child(4)", "11:59 pm"); - }); - }); - }); - - describe("Compliments Integration", () => { - beforeAll((done) => { - startApp("tests/configs/modules/weather/currentweather_compliments.js", {}, done); - }); - - it("should render a compliment based on the current weather", (done) => { - getText(done, ".compliments .module-content span", "snow"); - }); - }); - - describe("Configuration Options", () => { - beforeAll((done) => { - startApp("tests/configs/modules/weather/currentweather_options.js", {}, done); - }); - - it("should render useBeaufort = false", (done) => { - getText(done, ".weather .normal.medium span:nth-child(2)", "12"); - }); - - it("should render showWindDirectionAsArrow = true", (done) => { - helpers.waitForElement(".weather .normal.medium sup i.fa-long-arrow-alt-up").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.outerHTML).toContain("transform:rotate(250deg);"); - }); - }); - - it("should render showHumidity = true", (done) => { - getText(done, ".weather .normal.medium span:nth-child(3)", "93.7"); - }); - - it("should render degreeLabel = true for temp", (done) => { - getText(done, ".weather .large.light span.bright", "1°C"); - }); - - it("should render degreeLabel = true for feels like", (done) => { - getText(done, ".weather .normal.medium.feelslike span.dimmed", "Feels like -6°C"); - }); - }); - - describe("Current weather units", () => { - beforeAll((done) => { - startApp( - "tests/configs/modules/weather/currentweather_units.js", - { - main: { - temp: (1.49 * 9) / 5 + 32, - temp_min: (1 * 9) / 5 + 32, - temp_max: (2 * 9) / 5 + 32 - }, - wind: { - speed: 11.8 * 2.23694 - } - }, - done - ); - }); - - it("should render imperial units for wind", (done) => { - getText(done, ".weather .normal.medium span:nth-child(2)", "6 WSW"); - }); - - it("should render imperial units for temp", (done) => { - getText(done, ".weather .large.light span.bright", "34,7°"); - }); - - it("should render imperial units for feels like", (done) => { - getText(done, ".weather .normal.medium.feelslike span.dimmed", "Feels like 22,0°"); - }); - - it("should render custom decimalSymbol = ',' for humidity", (done) => { - getText(done, ".weather .normal.medium span:nth-child(3)", "93,7"); - }); - - it("should render custom decimalSymbol = ',' for temp", (done) => { - getText(done, ".weather .large.light span.bright", "34,7°"); - }); - - it("should render custom decimalSymbol = ',' for feels like", (done) => { - getText(done, ".weather .normal.medium.feelslike span.dimmed", "Feels like 22,0°"); - }); - }); - - describe("Weather Forecast", () => { - describe("Default configuration", () => { - beforeAll((done) => { - startApp("tests/configs/modules/weather/forecastweather_default.js", {}, done); - }); - - const days = ["Today", "Tomorrow", "Sun", "Mon", "Tue"]; - for (const [index, day] of days.entries()) { - it("should render day " + day, (done) => { - getText(done, `.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day); - }); - } - - const icons = ["day-cloudy", "rain", "day-sunny", "day-sunny", "day-sunny"]; - for (const [index, icon] of icons.entries()) { - it("should render icon " + icon, (done) => { - helpers.waitForElement(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(2) span.wi-${icon}`).then((elem) => { - done(); - expect(elem).not.toBe(null); - }); - }); - } - - const maxTemps = ["24.4°", "21.0°", "22.9°", "23.4°", "20.6°"]; - for (const [index, temp] of maxTemps.entries()) { - it("should render max temperature " + temp, (done) => { - getText(done, `.weather table.small tr:nth-child(${index + 1}) td:nth-child(3)`, temp); - }); - } - - const minTemps = ["15.3°", "13.6°", "13.8°", "13.9°", "10.9°"]; - for (const [index, temp] of minTemps.entries()) { - it("should render min temperature " + temp, (done) => { - getText(done, `.weather table.small tr:nth-child(${index + 1}) td:nth-child(4)`, temp); - }); - } - - const opacities = [1, 1, 0.8, 0.5333333333333333, 0.2666666666666667]; - for (const [index, opacity] of opacities.entries()) { - it("should render fading of rows with opacity=" + opacity, (done) => { - helpers.waitForElement(`.weather table.small tr:nth-child(${index + 1})`).then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.outerHTML).toContain(``); - }); - }); - } - }); - - describe("Absolute configuration", () => { - beforeAll((done) => { - startApp("tests/configs/modules/weather/forecastweather_absolute.js", {}, done); - }); - - const days = ["Fri", "Sat", "Sun", "Mon", "Tue"]; - for (const [index, day] of days.entries()) { - it("should render day " + day, (done) => { - getText(done, `.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day); - }); - } - }); - - describe("Configuration Options", () => { - beforeAll((done) => { - startApp("tests/configs/modules/weather/forecastweather_options.js", {}, done); - }); - - it("should render custom table class", (done) => { - helpers.waitForElement(".weather table.myTableClass").then((elem) => { - done(); - expect(elem).not.toBe(null); - }); - }); - - it("should render colored rows", (done) => { - helpers.waitForElement(".weather table.myTableClass").then((table) => { - done(); - expect(table).not.toBe(null); - expect(table.rows).not.toBe(null); - expect(table.rows.length).toBe(5); - }); - }); - }); - - describe("Forecast weather units", () => { - beforeAll((done) => { - startApp("tests/configs/modules/weather/forecastweather_units.js", {}, done); - }); - - const temperatures = ["24_4°", "21_0°", "22_9°", "23_4°", "20_6°"]; - for (const [index, temp] of temperatures.entries()) { - it("should render custom decimalSymbol = '_' for temp " + temp, (done) => { - getText(done, `.weather table.small tr:nth-child(${index + 1}) td:nth-child(3)`, temp); - }); - } - }); - }); -}); diff --git a/tests/e2e/modules_display_spec.js b/tests/e2e/modules_display_spec.js index e907df3663..1bac74fa57 100644 --- a/tests/e2e/modules_display_spec.js +++ b/tests/e2e/modules_display_spec.js @@ -1,28 +1,24 @@ const helpers = require("./global-setup"); describe("Display of modules", () => { - beforeAll(function (done) { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/display.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); afterAll(async () => { await helpers.stopApplication(); }); - it("should show the test header", (done) => { - helpers.waitForElement("#module_0_helloworld .module-header").then((elem) => { - done(); - expect(elem).not.toBe(null); - // textContent gibt hier lowercase zurück, das uppercase wird durch css realisiert, was daher nicht in textContent landet - expect(elem.textContent).toBe("test_header"); - }); + it("should show the test header", async () => { + const elem = await helpers.waitForElement("#module_0_helloworld .module-header"); + expect(elem).not.toBe(null); + // textContent gibt hier lowercase zurück, das uppercase wird durch css realisiert, was daher nicht in textContent landet + expect(elem.textContent).toBe("test_header"); }); - it("should show no header if no header text is specified", (done) => { - helpers.waitForElement("#module_1_helloworld .module-header").then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toBe("undefined"); - }); + it("should show no header if no header text is specified", async () => { + const elem = await helpers.waitForElement("#module_1_helloworld .module-header"); + expect(elem).not.toBe(null); + expect(elem.textContent).toBe("undefined"); }); }); diff --git a/tests/e2e/modules_position_spec.js b/tests/e2e/modules_position_spec.js index 3b6149d37a..dc7014c0ca 100644 --- a/tests/e2e/modules_position_spec.js +++ b/tests/e2e/modules_position_spec.js @@ -1,9 +1,9 @@ const helpers = require("./global-setup"); describe("Position of modules", () => { - beforeAll((done) => { + beforeAll(async () => { helpers.startApplication("tests/configs/modules/positions.js"); - helpers.getDocument(done); + await helpers.getDocument(); }); afterAll(async () => { await helpers.stopApplication(); @@ -13,12 +13,10 @@ describe("Position of modules", () => { for (const position of positions) { const className = position.replace("_", "."); - it("should show text in " + position, (done) => { - helpers.waitForElement("." + className).then((elem) => { - done(); - expect(elem).not.toBe(null); - expect(elem.textContent).toContain("Text in " + position); - }); + it("should show text in " + position, async () => { + const elem = await helpers.waitForElement("." + className); + expect(elem).not.toBe(null); + expect(elem.textContent).toContain("Text in " + position); }); } }); diff --git a/tests/e2e/port_config.js b/tests/e2e/port_config.js index 4a168d91f7..2236fda88e 100644 --- a/tests/e2e/port_config.js +++ b/tests/e2e/port_config.js @@ -1,36 +1,31 @@ -const fetch = require("fetch"); const helpers = require("./global-setup"); -describe("port directive configuration", function () { - describe("Set port 8090", function () { - beforeAll(function () { +describe("port directive configuration", () => { + describe("Set port 8090", () => { + beforeAll(() => { helpers.startApplication("tests/configs/port_8090.js"); }); - afterAll(async function () { + afterAll(async () => { await helpers.stopApplication(); }); - it("should return 200", function (done) { - fetch("http://localhost:8090").then((res) => { - expect(res.status).toBe(200); - done(); - }); + it("should return 200", async () => { + const res = await helpers.fetch("http://localhost:8090"); + expect(res.status).toBe(200); }); }); - describe("Set port 8100 on environment variable MM_PORT", function () { - beforeAll(function () { + describe("Set port 8100 on environment variable MM_PORT", () => { + beforeAll(() => { helpers.startApplication("tests/configs/port_8090.js", (process.env.MM_PORT = 8100)); }); - afterAll(async function () { + afterAll(async () => { await helpers.stopApplication(); }); - it("should return 200", function (done) { - fetch("http://localhost:8100").then((res) => { - expect(res.status).toBe(200); - done(); - }); + it("should return 200", async () => { + const res = await helpers.fetch("http://localhost:8100"); + expect(res.status).toBe(200); }); }); }); diff --git a/tests/e2e/translations_spec.js b/tests/e2e/translations_spec.js index 89ae60d918..4d2d1c1e9d 100644 --- a/tests/e2e/translations_spec.js +++ b/tests/e2e/translations_spec.js @@ -6,13 +6,13 @@ const { JSDOM } = require("jsdom"); const express = require("express"); const sinon = require("sinon"); -describe("Translations", function () { +describe("Translations", () => { let server; - beforeAll(function () { + beforeAll(() => { const app = express(); app.use(helmet()); - app.use(function (req, res, next) { + app.use((req, res, next) => { res.header("Access-Control-Allow-Origin", "*"); next(); }); @@ -21,11 +21,11 @@ describe("Translations", function () { server = app.listen(3000); }); - afterAll(function () { + afterAll(() => { server.close(); }); - it("should have a translation file in the specified path", function () { + it("should have a translation file in the specified path", () => { for (let language in translations) { const file = fs.statSync(translations[language]); expect(file.isFile()).toBe(true); @@ -37,7 +37,7 @@ describe("Translations", function () { beforeEach(() => { dom = new JSDOM( - `\ + `\ \ `, { runScripts: "dangerously", resources: "usable" } @@ -45,7 +45,7 @@ describe("Translations", function () { }); it("should load translation file", (done) => { - dom.window.onload = async function () { + dom.window.onload = async () => { const { Translator, Module, config } = dom.window; config.language = "en"; Translator.load = sinon.stub().callsFake((_m, _f, _fb, callback) => callback()); @@ -65,7 +65,7 @@ describe("Translations", function () { }); it("should load translation + fallback file", (done) => { - dom.window.onload = async function () { + dom.window.onload = async () => { const { Translator, Module } = dom.window; Translator.load = sinon.stub().callsFake((_m, _f, _fb, callback) => callback()); @@ -85,7 +85,7 @@ describe("Translations", function () { }); it("should load translation fallback file", (done) => { - dom.window.onload = async function () { + dom.window.onload = async () => { const { Translator, Module, config } = dom.window; config.language = "--"; Translator.load = sinon.stub().callsFake((_m, _f, _fb, callback) => callback()); @@ -105,7 +105,7 @@ describe("Translations", function () { }); it("should load no file", (done) => { - dom.window.onload = async function () { + dom.window.onload = async () => { const { Translator, Module } = dom.window; Translator.load = sinon.stub(); @@ -130,18 +130,18 @@ describe("Translations", function () { } }; - describe("Parsing language files through the Translator class", function () { + describe("Parsing language files through the Translator class", () => { for (let language in translations) { - it(`should parse ${language}`, function (done) { + it(`should parse ${language}`, (done) => { const dom = new JSDOM( - `\ + `\ \ + `\ \ + `\ \ + `\ \ + `\ \ + `\ \ + `\ \ + `\ \ + `\ \ + `\ \ + `\ \ + `\ + diff --git a/js/animateCSS.js b/js/animateCSS.js new file mode 100644 index 0000000000..ae6e7bec7b --- /dev/null +++ b/js/animateCSS.js @@ -0,0 +1,165 @@ +/* MagicMirror² + * AnimateCSS System from https://animate.style/ + * by @bugsounet + * for Michael Teeuw https://michaelteeuw.nl + * MIT Licensed. + */ + +/* enumeration of animations in Array **/ +const AnimateCSSIn = [ + // Attention seekers + "bounce", + "flash", + "pulse", + "rubberBand", + "shakeX", + "shakeY", + "headShake", + "swing", + "tada", + "wobble", + "jello", + "heartBeat", + // Back entrances + "backInDown", + "backInLeft", + "backInRight", + "backInUp", + // Bouncing entrances + "bounceIn", + "bounceInDown", + "bounceInLeft", + "bounceInRight", + "bounceInUp", + // Fading entrances + "fadeIn", + "fadeInDown", + "fadeInDownBig", + "fadeInLeft", + "fadeInLeftBig", + "fadeInRight", + "fadeInRightBig", + "fadeInUp", + "fadeInUpBig", + "fadeInTopLeft", + "fadeInTopRight", + "fadeInBottomLeft", + "fadeInBottomRight", + // Flippers + "flip", + "flipInX", + "flipInY", + // Lightspeed + "lightSpeedInRight", + "lightSpeedInLeft", + // Rotating entrances + "rotateIn", + "rotateInDownLeft", + "rotateInDownRight", + "rotateInUpLeft", + "rotateInUpRight", + // Specials + "jackInTheBox", + "rollIn", + // Zooming entrances + "zoomIn", + "zoomInDown", + "zoomInLeft", + "zoomInRight", + "zoomInUp", + // Sliding entrances + "slideInDown", + "slideInLeft", + "slideInRight", + "slideInUp" +]; + +const AnimateCSSOut = [ + // Back exits + "backOutDown", + "backOutLeft", + "backOutRight", + "backOutUp", + // Bouncing exits + "bounceOut", + "bounceOutDown", + "bounceOutLeft", + "bounceOutRight", + "bounceOutUp", + // Fading exits + "fadeOut", + "fadeOutDown", + "fadeOutDownBig", + "fadeOutLeft", + "fadeOutLeftBig", + "fadeOutRight", + "fadeOutRightBig", + "fadeOutUp", + "fadeOutUpBig", + "fadeOutTopLeft", + "fadeOutTopRight", + "fadeOutBottomRight", + "fadeOutBottomLeft", + // Flippers + "flipOutX", + "flipOutY", + // Lightspeed + "lightSpeedOutRight", + "lightSpeedOutLeft", + // Rotating exits + "rotateOut", + "rotateOutDownLeft", + "rotateOutDownRight", + "rotateOutUpLeft", + "rotateOutUpRight", + // Specials + "hinge", + "rollOut", + // Zooming exits + "zoomOut", + "zoomOutDown", + "zoomOutLeft", + "zoomOutRight", + "zoomOutUp", + // Sliding exits + "slideOutDown", + "slideOutLeft", + "slideOutRight", + "slideOutUp" +]; + +/** + * Create an animation with Animate CSS + * resolved as Promise when done + * @param {string} [element] div element to animate. + * @param {string} [animation] animation name. + * @param {number} [animationTime] animation duration. + */ +function AnimateCSS(element, animation, animationTime) { + /* We create a Promise and return it */ + return new Promise((resolve) => { + const animationName = `animate__${animation}`; + const node = document.getElementById(element); + if (!node) { + // don't execute animate and resolve + Log.warn(`AnimateCSS: node not found for`, element); + resolve(); + return; + } + node.style.setProperty("--animate-duration", `${animationTime}s`); + node.classList.add("animate__animated", animationName); + + /** + * When the animation ends, we clean the classes and resolve the Promise + * @param {object} event object + */ + function handleAnimationEnd(event) { + node.classList.remove("animate__animated", animationName); + node.style.removeProperty("--animate-duration", `${animationTime}s`); + event.stopPropagation(); + resolve(); + } + + node.addEventListener("animationend", handleAnimationEnd, { once: true }); + }); +} diff --git a/js/loader.js b/js/loader.js index 517999bb86..a5dc36fcab 100644 --- a/js/loader.js +++ b/js/loader.js @@ -88,6 +88,8 @@ const Loader = (function () { path: `${moduleFolder}/`, file: `${moduleName}.js`, position: moduleData.position, + animateIn: moduleData.animateIn, + animateOut: moduleData.animateOut, hiddenOnStartup: moduleData.hiddenOnStartup, header: moduleData.header, configDeepMerge: typeof moduleData.configDeepMerge === "boolean" ? moduleData.configDeepMerge : false, diff --git a/js/main.js b/js/main.js index 026410c777..5efce70abe 100644 --- a/js/main.js +++ b/js/main.js @@ -1,4 +1,4 @@ -/* global Loader, defaults, Translator */ +/* global Loader, defaults, Translator, AnimateCSS, AnimateCSSIn, AnimateCSSOut */ /* MagicMirror² * Main System @@ -22,6 +22,10 @@ const MM = (function () { return; } + let haveAnimateIn = null; + // check if have valid animateIn in module definition (module.data.animateIn) + if (module.data.animateIn && AnimateCSSIn.indexOf(module.data.animateIn) !== -1) haveAnimateIn = module.data.animateIn; + const wrapper = selectWrapper(module.data.position); const dom = document.createElement("div"); @@ -50,7 +54,12 @@ const MM = (function () { moduleContent.className = "module-content"; dom.appendChild(moduleContent); - const domCreationPromise = updateDom(module, 0); + // create the domCreationPromise with AnimateCSS (with animateIn of module definition) + // or just display it + var domCreationPromise; + if (haveAnimateIn) domCreationPromise = updateDom(module, 1000, null, haveAnimateIn, true); + else domCreationPromise = updateDom(module, 0); + domCreationPromises.push(domCreationPromise); domCreationPromise .then(function () { @@ -101,11 +110,30 @@ const MM = (function () { /** * Update the dom for a specific module. * @param {Module} module The module that needs an update. - * @param {number} [speed] The (optional) number of microseconds for the animation. + * @param {object|number} [updateOptions] The (optional) number of microseconds for the animation or object with updateOptions (speed/animates) + * @param {boolean} [createAnimatedDom] for displaying only animateIn (used on first start of MagicMirror) * @returns {Promise} Resolved when the dom is fully updated. */ - const updateDom = function (module, speed) { + const updateDom = function (module, updateOptions, createAnimatedDom = false) { return new Promise(function (resolve) { + let speed = updateOptions; + let animateOut = null; + let animateIn = null; + if (typeof updateOptions === "object") { + if (typeof updateOptions.options === "object" && updateOptions.options.speed !== undefined) { + speed = updateOptions.options.speed; + Log.debug(`updateDom: ${module.identifier} Has speed in object: ${speed}`); + if (typeof updateOptions.options.animate === "object") { + animateOut = updateOptions.options.animate.out; + animateIn = updateOptions.options.animate.in; + Log.debug(`updateDom: ${module.identifier} Has animate in object: out->${animateOut}, in->${animateIn}`); + } + } else { + Log.debug(`updateDom: ${module.identifier} Has no speed in object`); + speed = 0; + } + } + const newHeader = module.getHeader(); let newContentPromise = module.getDom(); @@ -116,7 +144,7 @@ const MM = (function () { newContentPromise .then(function (newContent) { - const updatePromise = updateDomWithContent(module, speed, newHeader, newContent); + const updatePromise = updateDomWithContent(module, speed, newHeader, newContent, animateOut, animateIn, createAnimatedDom); updatePromise.then(resolve).catch(Log.error); }) @@ -130,9 +158,12 @@ const MM = (function () { * @param {number} [speed] The (optional) number of microseconds for the animation. * @param {string} newHeader The new header that is generated. * @param {HTMLElement} newContent The new content that is generated. + * @param {string} [animateOut] AnimateCss animation name before hidden + * @param {string} [animateIn] AnimateCss animation name on show + * @param {boolean} [createAnimatedDom] for displaying only animateIn (used on first start) * @returns {Promise} Resolved when the module dom has been updated. */ - const updateDomWithContent = function (module, speed, newHeader, newContent) { + const updateDomWithContent = function (module, speed, newHeader, newContent, animateOut, animateIn, createAnimatedDom = false) { return new Promise(function (resolve) { if (module.hidden || !speed) { updateModuleContent(module, newHeader, newContent); @@ -151,13 +182,28 @@ const MM = (function () { return; } - hideModule(module, speed / 2, function () { + if (createAnimatedDom && animateIn !== null) { + Log.debug(`${module.identifier} createAnimatedDom (${animateIn})`); updateModuleContent(module, newHeader, newContent); if (!module.hidden) { - showModule(module, speed / 2); + showModule(module, speed, null, { animate: animateIn }); } resolve(); - }); + return; + } + + hideModule( + module, + speed / 2, + function () { + updateModuleContent(module, newHeader, newContent); + if (!module.hidden) { + showModule(module, speed / 2, null, { animate: animateIn }); + } + resolve(); + }, + { animate: animateOut } + ); }); }; @@ -223,7 +269,7 @@ const MM = (function () { * @param {Function} callback Called when the animation is done. * @param {object} [options] Optional settings for the hide method. */ - const hideModule = function (module, speed, callback, options = {}) { + const hideModule = async function (module, speed, callback, options = {}) { // set lockString if set in options. if (options.lockString) { // Log.log("Has lockstring: " + options.lockString); @@ -234,24 +280,49 @@ const MM = (function () { const moduleWrapper = document.getElementById(module.identifier); if (moduleWrapper !== null) { - moduleWrapper.style.transition = `opacity ${speed / 1000}s`; - moduleWrapper.style.opacity = 0; - moduleWrapper.classList.add("hidden"); - clearTimeout(module.showHideTimer); - module.showHideTimer = setTimeout(function () { - // To not take up any space, we just make the position absolute. - // since it's fade out anyway, we can see it lay above or - // below other modules. This works way better than adjusting - // the .display property. + + // haveAnimateName for verify if we are using AninateCSS library + // we check AnimateCSSOut Array for validate it + // and finaly return the animate name or `null` (for default MM² animation) + let haveAnimateName = null; + // check if have valid animateOut in module definition (module.data.animateOut) + if (module.data.animateOut && AnimateCSSOut.indexOf(module.data.animateOut) !== -1) haveAnimateName = module.data.animateOut; + // can't be override with options.animate + else if (options.animate && AnimateCSSOut.indexOf(options.animate) !== -1) haveAnimateName = options.animate; + + if (haveAnimateName) { + // with AnimateCSS + Log.debug(`${module.identifier} Has animateOut: ${haveAnimateName}`); + await AnimateCSS(module.identifier, haveAnimateName, speed / 1000); + // AnimateCSS is now done + moduleWrapper.style.opacity = 0; + moduleWrapper.classList.add("hidden"); moduleWrapper.style.position = "fixed"; updateWrapperStates(); - if (typeof callback === "function") { callback(); } - }, speed); + } else { + // default MM² Animate + moduleWrapper.style.transition = `opacity ${speed / 1000}s`; + moduleWrapper.style.opacity = 0; + moduleWrapper.classList.add("hidden"); + module.showHideTimer = setTimeout(function () { + // To not take up any space, we just make the position absolute. + // since it's fade out anyway, we can see it lay above or + // below other modules. This works way better than adjusting + // the .display property. + moduleWrapper.style.position = "fixed"; + + updateWrapperStates(); + + if (typeof callback === "function") { + callback(); + } + }, speed); + } } else { // invoke callback even if no content, issue 1308 if (typeof callback === "function") { @@ -267,7 +338,7 @@ const MM = (function () { * @param {Function} callback Called when the animation is done. * @param {object} [options] Optional settings for the show method. */ - const showModule = function (module, speed, callback, options = {}) { + const showModule = async function (module, speed, callback, options = {}) { // remove lockString if set in options. if (options.lockString) { const index = module.lockStrings.indexOf(options.lockString); @@ -296,7 +367,18 @@ const MM = (function () { const moduleWrapper = document.getElementById(module.identifier); if (moduleWrapper !== null) { - moduleWrapper.style.transition = `opacity ${speed / 1000}s`; + clearTimeout(module.showHideTimer); + + // haveAnimateName for verify if we are using AninateCSS library + // we check AnimateCSSIn Array for validate it + // and finaly return the animate name or `null` (for default MM² animation) + let haveAnimateName = null; + // check if have valid animateOut in module definition (module.data.animateIn) + if (module.data.animateIn && AnimateCSSIn.indexOf(module.data.animateIn) !== -1) haveAnimateName = module.data.animateIn; + // can't be override with options.animate + else if (options.animate && AnimateCSSIn.indexOf(options.animate) !== -1) haveAnimateName = options.animate; + + if (!haveAnimateName) moduleWrapper.style.transition = `opacity ${speed / 1000}s`; // Restore the position. See hideModule() for more info. moduleWrapper.style.position = "static"; moduleWrapper.classList.remove("hidden"); @@ -307,12 +389,21 @@ const MM = (function () { const dummy = moduleWrapper.parentElement.parentElement.offsetHeight; moduleWrapper.style.opacity = 1; - clearTimeout(module.showHideTimer); - module.showHideTimer = setTimeout(function () { + if (haveAnimateName) { + // with AnimateCSS + Log.debug(`${module.identifier} Has animateIn: ${haveAnimateName}`); + await AnimateCSS(module.identifier, haveAnimateName, speed / 1000); if (typeof callback === "function") { callback(); } - }, speed); + } else { + // default MM² Animate + module.showHideTimer = setTimeout(function () { + if (typeof callback === "function") { + callback(); + } + }, speed); + } } else { // invoke callback if (typeof callback === "function") { @@ -514,9 +605,9 @@ const MM = (function () { /** * Update the dom for a specific module. * @param {Module} module The module that needs an update. - * @param {number} [speed] The number of microseconds for the animation. + * @param {object|number} [updateOptions] The (optional) number of microseconds for the animation or object with updateOptions (speed/animates) */ - updateDom: function (module, speed) { + updateDom: function (module, updateOptions) { if (!(module instanceof Module)) { Log.error("updateDom: Sender should be a module."); return; @@ -528,7 +619,7 @@ const MM = (function () { } // Further implementation is done in the private method. - updateDom(module, speed); + updateDom(module, updateOptions); }, /** diff --git a/js/module.js b/js/module.js index 110ccc5595..62534b0178 100644 --- a/js/module.js +++ b/js/module.js @@ -193,7 +193,7 @@ const Module = Class.extend({ }, /********************************************* - * The methods below don"t need subclassing. * + * The methods below don't need subclassing. * *********************************************/ /** @@ -327,10 +327,10 @@ const Module = Class.extend({ /** * Request an (animated) update of the module. - * @param {number} [speed] The speed of the animation. + * @param {number|object} [updateOptions] The speed of the animation or object with for updateOptions (speed/animates) */ - updateDom: function (speed) { - MM.updateDom(this, speed); + updateDom: function (updateOptions) { + MM.updateDom(this, updateOptions); }, /** diff --git a/vendor/package-lock.json b/vendor/package-lock.json index 4f88463dc4..30ff41b062 100644 --- a/vendor/package-lock.json +++ b/vendor/package-lock.json @@ -8,6 +8,7 @@ "license": "MIT", "dependencies": { "@fortawesome/fontawesome-free": "^6.4.2", + "animate.css": "^4.1.1", "moment": "^2.29.4", "moment-timezone": "^0.5.43", "nunjucks": "^3.2.4", @@ -29,6 +30,11 @@ "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" }, + "node_modules/animate.css": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/animate.css/-/animate.css-4.1.1.tgz", + "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==" + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -107,6 +113,11 @@ "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" }, + "animate.css": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/animate.css/-/animate.css-4.1.1.tgz", + "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==" + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", diff --git a/vendor/package.json b/vendor/package.json index 376b1f07a4..9f2f93c52f 100644 --- a/vendor/package.json +++ b/vendor/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@fortawesome/fontawesome-free": "^6.4.2", + "animate.css": "^4.1.1", "moment": "^2.29.4", "moment-timezone": "^0.5.43", "nunjucks": "^3.2.4", From 79e99e18ea770337a94c8fd5d0f1dc5c2601a68c Mon Sep 17 00:00:00 2001 From: "J. Kenzal Hunter" Date: Fri, 8 Sep 2023 01:44:49 -0400 Subject: [PATCH 146/204] Cross UTC time fix (#3175) Update calendarfetcherutils.js to force recurrence date time to be the same as event datetime I found an issue with one of my calendars displaying the wrong time for certain recurring events. Each event was set up by someone in a different timezone (Central European) than my own (Eastern US). I traced the issue back to the `Rrule.between()` method generating odd time portions under certain circumstances. The fix I found was to set the UTC time portion of the recurrence datetime to be the same as the UTC time portion of the event start date. This resolved the issues with the maladjusted event times, and had no effect on other event times. While there may be edge cases that are affected, I have been unable to locate any. --------- Co-authored-by: Veeck --- CHANGELOG.md | 1 + modules/default/calendar/calendarfetcherutils.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9ca2aed0f..9fd6cf86d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ _This release is scheduled to be released on 2023-10-01._ - Fix undefined formatTime method in clock module (#3143) - Fix clientonly startup fails after async added (#3151) - Fix electron width/heigth when using xrandr under bullseye +- Fix time issue with certain recurring events in calendar module - Fix ipWhiteList test (#3179) ## [2.24.0] - 2023-07-01 diff --git a/modules/default/calendar/calendarfetcherutils.js b/modules/default/calendar/calendarfetcherutils.js index d425f0a478..34cc2578c6 100644 --- a/modules/default/calendar/calendarfetcherutils.js +++ b/modules/default/calendar/calendarfetcherutils.js @@ -313,6 +313,9 @@ const CalendarFetcherUtils = { let curEvent = event; let showRecurrence = true; + // set the time information in the date to equal the time information in the event + date.setUTCHours(curEvent.start.getUTCHours(), curEvent.start.getUTCMinutes(), curEvent.start.getUTCSeconds(), curEvent.start.getUTCMilliseconds()); + // Get the offset of today where we are processing // This will be the correction, we need to apply. let nowOffset = new Date().getTimezoneOffset(); From ffdf321e23c31d0039810517904648c3e65550b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bugsounet=20-=20C=C3=A9dric?= Date: Sat, 9 Sep 2023 10:38:19 +0200 Subject: [PATCH 147/204] Mistake on Changelog (#3186) Move AnimateCSS changeLog from v2.24 to v2.25 --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fd6cf86d2..02b9e783b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ _This release is scheduled to be released on 2023-10-01._ - Added UV Index support to OpenWeatherMap - Added 'hideDuplicates' flag to the calendar module - Added `allowOverrideNotification` to weather module to enable sending current weather objects with the `CURRENT_WEATHER_OVERRIDE` notification to supplement/replace the current weather displayed +- Added optional AnimateCSS animate for `hide()`, `show()`, `updateDom()` +- Added AnimateIn and animateOut in module config definition +- Apply AnimateIn rules on the first start ### Removed @@ -51,9 +54,6 @@ Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not al - updatenotification: Added `sendUpdatesNotifications` feature. Broadcast update with `UPDATES` notification to other modules - updatenotification: allow force scanning with `SCAN_UPDATES` notification from other modules - Added per-calendar fetchInterval -- Added optional AnimateCSS animate for `hide()`, `show()`, `updateDom()` -- Added AnimateIn and animateOut in module config definition -- Apply AnimateIn rules on the first start ### Removed From f2957f90df66a2392b190e68a923f97c9087892b Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Sat, 9 Sep 2023 21:12:31 +0200 Subject: [PATCH 148/204] use internal fetch as replacement for node-fetch (#3184) related to #2649 I was able to move to internal fetch and all tests seems fine so far. But we have one problem with the calendar module. In the docs we have several authentication methods and one of them is `digest`. For this we used `digest-fetch` which needs `node-fetch` (this is not so clear from code but I was not able to get it working). So we have 3 options: - remove `digest` as authentication method for calendar module (this is what this PR does at the moment) - find an alternative npm package or implement the digest stuff ourselves - use `digest-fetch` and `node-fetch` for calendar module (so they would remain as dependencies in `package.json`) Opinions? @KristjanESPERANTO @rejas @sdetweil @MichMich --- CHANGELOG.md | 5 ++ js/fetch.js | 20 -------- js/server_functions.js | 1 - modules/default/calendar/calendarfetcher.js | 10 +--- modules/default/newsfeed/newsfeedfetcher.js | 1 - package-lock.json | 51 ------------------- package.json | 5 +- tests/e2e/env_spec.js | 4 +- tests/e2e/fonts_spec.js | 2 +- tests/e2e/helpers/global-setup.js | 11 +--- tests/e2e/ipWhitelist_spec.js | 4 +- tests/e2e/port_spec.js | 4 +- tests/e2e/serveronly_spec.js | 4 +- tests/e2e/template_spec.js | 2 +- tests/e2e/vendor_spec.js | 4 +- tests/unit/functions/server_functions_spec.js | 11 ++-- 16 files changed, 24 insertions(+), 115 deletions(-) delete mode 100644 js/fetch.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 02b9e783b5..9a1f0603aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ This project adheres to [Semantic Versioning](https://semver.org/). _This release is scheduled to be released on 2023-10-01._ +> ⚠️ This release needs nodejs version > `v18`, older release have reached end of life and will not work! + ### Added - Added UV Index support to OpenWeatherMap @@ -20,6 +22,8 @@ _This release is scheduled to be released on 2023-10-01._ ### Removed +- **Breaking Change**: Removed `digest` authentication method from calendar module (which was already broken since release `2.15.0`) + ### Updated - Update roboto fonts to version v5 @@ -29,6 +33,7 @@ _This release is scheduled to be released on 2023-10-01._ - Update engine node >=18. v16 reached it's end of life. (#3170) - Update typescript definition for modules - Cleaned up nunjuck templates +- Replace `node-fetch` with internal fetch (#2649) and remove `digest-fetch` ### Fixed diff --git a/js/fetch.js b/js/fetch.js deleted file mode 100644 index cf195993d5..0000000000 --- a/js/fetch.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Helper class to provide either third party fetch library or (if node >= 18) - * return internal node fetch implementation. - * - * Attention: After some discussion we always return the third party - * implementation until the node implementation is stable and more tested - * @see https://github.com/MichMich/MagicMirror/pull/2952 - * @see https://github.com/MichMich/MagicMirror/issues/2649 - * @param {string} url to be fetched - * @param {object} options object e.g. for headers - * @class - */ -async function fetch(url, options = {}) { - // return global.fetch(url, options); - - const nodefetch = require("node-fetch"); - return nodefetch(url, options); -} - -module.exports = fetch; diff --git a/js/server_functions.js b/js/server_functions.js index 8e9d9aa91d..ef418e3244 100644 --- a/js/server_functions.js +++ b/js/server_functions.js @@ -1,7 +1,6 @@ const fs = require("fs"); const path = require("path"); const Log = require("logger"); -const fetch = require("./fetch"); /** * Gets the config. diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js index c7b62960d8..3ae9bcd315 100644 --- a/modules/default/calendar/calendarfetcher.js +++ b/modules/default/calendar/calendarfetcher.js @@ -6,9 +6,7 @@ */ const https = require("https"); -const digest = require("digest-fetch"); const ical = require("node-ical"); -const fetch = require("fetch"); const Log = require("logger"); const NodeHelper = require("node_helper"); const CalendarFetcherUtils = require("./calendarfetcherutils"); @@ -39,7 +37,6 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn clearTimeout(reloadTimer); reloadTimer = null; const nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]); - let fetcher = null; let httpsAgent = null; let headers = { "User-Agent": `Mozilla/5.0 (Node.js ${nodeVersion}) MagicMirror/${global.version}` @@ -53,17 +50,12 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn if (auth) { if (auth.method === "bearer") { headers.Authorization = `Bearer ${auth.pass}`; - } else if (auth.method === "digest") { - fetcher = new digest(auth.user, auth.pass).fetch(url, { headers: headers, agent: httpsAgent }); } else { headers.Authorization = `Basic ${Buffer.from(`${auth.user}:${auth.pass}`).toString("base64")}`; } } - if (fetcher === null) { - fetcher = fetch(url, { headers: headers, agent: httpsAgent }); - } - fetcher + fetch(url, { headers: headers, agent: httpsAgent }) .then(NodeHelper.checkFetchStatus) .then((response) => response.text()) .then((responseData) => { diff --git a/modules/default/newsfeed/newsfeedfetcher.js b/modules/default/newsfeed/newsfeedfetcher.js index 51d38f83fb..f61867486c 100644 --- a/modules/default/newsfeed/newsfeedfetcher.js +++ b/modules/default/newsfeed/newsfeedfetcher.js @@ -8,7 +8,6 @@ const stream = require("stream"); const FeedMe = require("feedme"); const iconv = require("iconv-lite"); -const fetch = require("fetch"); const Log = require("logger"); const NodeHelper = require("node_helper"); diff --git a/package-lock.json b/package-lock.json index d7432ab870..20c00a4d7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,6 @@ "dependencies": { "colors": "^1.4.0", "console-stamp": "^3.1.2", - "digest-fetch": "^2.0.3", "envsub": "^4.1.0", "eslint": "^8.48.0", "express": "^4.18.2", @@ -23,7 +22,6 @@ "luxon": "^1.28.1", "module-alias": "^2.2.3", "moment": "^2.29.4", - "node-fetch": "^2.6.12", "node-ical": "^0.16.1", "socket.io": "^4.7.2" }, @@ -3432,17 +3430,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/digest-fetch": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-2.0.3.tgz", - "integrity": "sha512-HuTjHQE+wplAR+H8/YGwQjIGR1RQUCEsQcRyp3dZfuuxpSQH4OTm4BkHxyXuzxwmxUrNVzIPf9XkXi8QMJDNwQ==", - "dependencies": { - "base-64": "^0.1.0", - "js-sha256": "^0.9.0", - "js-sha512": "^0.8.0", - "md5": "^2.3.0" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -7291,44 +7278,6 @@ "isarray": "0.0.1" } }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/node-ical": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.16.1.tgz", diff --git a/package.json b/package.json index 9b0da64144..3f5fafd6b0 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,6 @@ "dependencies": { "colors": "^1.4.0", "console-stamp": "^3.1.2", - "digest-fetch": "^2.0.3", "envsub": "^4.1.0", "eslint": "^8.48.0", "express": "^4.18.2", @@ -85,7 +84,6 @@ "luxon": "^1.28.1", "module-alias": "^2.2.3", "moment": "^2.29.4", - "node-fetch": "^2.6.12", "node-ical": "^0.16.1", "socket.io": "^4.7.2" }, @@ -96,8 +94,7 @@ }, "_moduleAliases": { "node_helper": "js/node_helper.js", - "logger": "js/logger.js", - "fetch": "js/fetch.js" + "logger": "js/logger.js" }, "engines": { "node": ">=18" diff --git a/tests/e2e/env_spec.js b/tests/e2e/env_spec.js index a62ab54448..2a9945a85f 100644 --- a/tests/e2e/env_spec.js +++ b/tests/e2e/env_spec.js @@ -10,12 +10,12 @@ describe("App environment", () => { }); it("get request from http://localhost:8080 should return 200", async () => { - const res = await helpers.fetch("http://localhost:8080"); + const res = await fetch("http://localhost:8080"); expect(res.status).toBe(200); }); it("get request from http://localhost:8080/nothing should return 404", async () => { - const res = await helpers.fetch("http://localhost:8080/nothing"); + const res = await fetch("http://localhost:8080/nothing"); expect(res.status).toBe(404); }); diff --git a/tests/e2e/fonts_spec.js b/tests/e2e/fonts_spec.js index 160359ecae..706ed4a909 100644 --- a/tests/e2e/fonts_spec.js +++ b/tests/e2e/fonts_spec.js @@ -22,7 +22,7 @@ describe("All font files from roboto.css should be downloadable", () => { test.each(fontFiles)("should return 200 HTTP code for file '%s'", async (fontFile) => { const fontUrl = `http://localhost:8080/fonts/${fontFile}`; - const res = await helpers.fetch(fontUrl); + const res = await fetch(fontUrl); expect(res.status).toBe(200); }); }); diff --git a/tests/e2e/helpers/global-setup.js b/tests/e2e/helpers/global-setup.js index b79c1f1f13..d5506024af 100644 --- a/tests/e2e/helpers/global-setup.js +++ b/tests/e2e/helpers/global-setup.js @@ -1,5 +1,4 @@ const jsdom = require("jsdom"); -const corefetch = require("fetch"); exports.startApplication = async (configFilename, exec) => { jest.resetModules(); @@ -31,7 +30,7 @@ exports.getDocument = () => { const url = `http://${config.address || "localhost"}:${config.port || "8080"}`; jsdom.JSDOM.fromURL(url, { resources: "usable", runScripts: "dangerously" }).then((dom) => { dom.window.name = "jsdom"; - dom.window.fetch = corefetch; + dom.window.fetch = fetch; dom.window.onload = () => { global.document = dom.window.document; resolve(); @@ -80,14 +79,6 @@ exports.waitForAllElements = (selector) => { }); }; -exports.fetch = (url) => { - return new Promise((resolve) => { - corefetch(url).then((res) => { - resolve(res); - }); - }); -}; - exports.testMatch = async (element, regex) => { const elem = await this.waitForElement(element); expect(elem).not.toBe(null); diff --git a/tests/e2e/ipWhitelist_spec.js b/tests/e2e/ipWhitelist_spec.js index 2bb2d682a8..07a0425e8d 100644 --- a/tests/e2e/ipWhitelist_spec.js +++ b/tests/e2e/ipWhitelist_spec.js @@ -10,7 +10,7 @@ describe("ipWhitelist directive configuration", () => { }); it("should return 403", async () => { - const res = await helpers.fetch("http://localhost:8181"); + const res = await fetch("http://localhost:8181"); expect(res.status).toBe(403); }); }); @@ -24,7 +24,7 @@ describe("ipWhitelist directive configuration", () => { }); it("should return 200", async () => { - const res = await helpers.fetch("http://localhost:8282"); + const res = await fetch("http://localhost:8282"); expect(res.status).toBe(200); }); }); diff --git a/tests/e2e/port_spec.js b/tests/e2e/port_spec.js index 104b9373dc..f6900a3dd5 100644 --- a/tests/e2e/port_spec.js +++ b/tests/e2e/port_spec.js @@ -10,7 +10,7 @@ describe("port directive configuration", () => { }); it("should return 200", async () => { - const res = await helpers.fetch("http://localhost:8090"); + const res = await fetch("http://localhost:8090"); expect(res.status).toBe(200); }); }); @@ -24,7 +24,7 @@ describe("port directive configuration", () => { }); it("should return 200", async () => { - const res = await helpers.fetch("http://localhost:8100"); + const res = await fetch("http://localhost:8100"); expect(res.status).toBe(200); }); }); diff --git a/tests/e2e/serveronly_spec.js b/tests/e2e/serveronly_spec.js index 78ecba30f5..82d0429b83 100644 --- a/tests/e2e/serveronly_spec.js +++ b/tests/e2e/serveronly_spec.js @@ -17,12 +17,12 @@ describe("App environment", () => { }); it("get request from http://localhost:8080 should return 200", async () => { - const res = await helpers.fetch("http://localhost:8080"); + const res = await fetch("http://localhost:8080"); expect(res.status).toBe(200); }); it("get request from http://localhost:8080/nothing should return 404", async () => { - const res = await helpers.fetch("http://localhost:8080/nothing"); + const res = await fetch("http://localhost:8080/nothing"); expect(res.status).toBe(404); }); }); diff --git a/tests/e2e/template_spec.js b/tests/e2e/template_spec.js index 0c706c1cc5..3bcc5e4427 100644 --- a/tests/e2e/template_spec.js +++ b/tests/e2e/template_spec.js @@ -9,7 +9,7 @@ describe("templated config with port variable", () => { }); it("should return 200", async () => { - const res = await helpers.fetch("http://localhost:8090"); + const res = await fetch("http://localhost:8090"); expect(res.status).toBe(200); }); }); diff --git a/tests/e2e/vendor_spec.js b/tests/e2e/vendor_spec.js index dff9585810..49d3ab8517 100644 --- a/tests/e2e/vendor_spec.js +++ b/tests/e2e/vendor_spec.js @@ -14,7 +14,7 @@ describe("Vendors", () => { Object.keys(vendors).forEach((vendor) => { it(`should return 200 HTTP code for vendor "${vendor}"`, async () => { const urlVendor = `http://localhost:8080/vendor/${vendors[vendor]}`; - const res = await helpers.fetch(urlVendor); + const res = await fetch(urlVendor); expect(res.status).toBe(200); }); }); @@ -22,7 +22,7 @@ describe("Vendors", () => { Object.keys(vendors).forEach((vendor) => { it(`should return 404 HTTP code for vendor https://localhost/"${vendor}"`, async () => { const urlVendor = `http://localhost:8080/${vendors[vendor]}`; - const res = await helpers.fetch(urlVendor); + const res = await fetch(urlVendor); expect(res.status).toBe(404); }); }); diff --git a/tests/unit/functions/server_functions_spec.js b/tests/unit/functions/server_functions_spec.js index 3548e38a0e..6242c9a9b5 100644 --- a/tests/unit/functions/server_functions_spec.js +++ b/tests/unit/functions/server_functions_spec.js @@ -8,13 +8,9 @@ describe("server_functions tests", () => { let corsResponse; let request; - jest.mock("node-fetch"); - let nodefetch = require("node-fetch"); let fetchMock; beforeEach(() => { - nodefetch.mockReset(); - fetchResponseHeadersGet = jest.fn(() => {}); fetchResponseHeadersText = jest.fn(() => {}); fetchResponse = { @@ -23,10 +19,11 @@ describe("server_functions tests", () => { }, text: fetchResponseHeadersText }; - jest.mock("node-fetch", () => jest.fn()); - nodefetch.mockImplementation(() => fetchResponse); + // eslint-disable-next-line + fetch = jest.fn(); + fetch.mockImplementation(() => fetchResponse); - fetchMock = nodefetch; + fetchMock = fetch; corsResponse = { set: jest.fn(() => {}), From 7a1591b2d6db709348fdb6f363398872d26ae098 Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Wed, 13 Sep 2023 22:46:17 +0200 Subject: [PATCH 149/204] added automatic client page reload (#3188) solution for #3105 ~~not sure if updatenotification is the right place, so opinions?~~ now impleneted in `main.js` --- CHANGELOG.md | 1 + js/defaults.js | 5 +++++ js/main.js | 21 +++++++++++++++++++++ js/server.js | 4 +++- js/server_functions.js | 12 +++++++++++- 5 files changed, 41 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a1f0603aa..d9c866b7b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ _This release is scheduled to be released on 2023-10-01._ - Added optional AnimateCSS animate for `hide()`, `show()`, `updateDom()` - Added AnimateIn and animateOut in module config definition - Apply AnimateIn rules on the first start +- Added automatic client page reload when server was restarted by setting `reloadAfterServerRestart: true` in `config.js`, per default `false` (#3105) ### Removed diff --git a/js/defaults.js b/js/defaults.js index b2edb8e4ff..c8f849587b 100644 --- a/js/defaults.js +++ b/js/defaults.js @@ -29,6 +29,11 @@ const defaults = { // e.g. you need to add `frameguard: false` for embedding MagicMirror in another website, see https://github.com/MichMich/MagicMirror/issues/2847 httpHeaders: { contentSecurityPolicy: false, crossOriginOpenerPolicy: false, crossOriginEmbedderPolicy: false, crossOriginResourcePolicy: false, originAgentCluster: false }, + // properties for checking if server is alive and has same startup-timestamp, the check is per default enabled + // (interval 30 seconds). If startup-timestamp has changed the client reloads the magicmirror webpage. + checkServerInterval: 30 * 1000, + reloadAfterServerRestart: false, + modules: [ { module: "updatenotification", diff --git a/js/main.js b/js/main.js index 5efce70abe..3c023af359 100644 --- a/js/main.js +++ b/js/main.js @@ -568,12 +568,33 @@ const MM = (function () { */ modulesStarted: function (moduleObjects) { modules = []; + let startUp = ""; + moduleObjects.forEach((module) => modules.push(module)); Log.info("All modules started!"); sendNotification("ALL_MODULES_STARTED"); createDomObjects(); + + if (config.reloadAfterServerRestart) { + setInterval(async () => { + // if server startup time has changed (which means server was restarted) + // the client reloads the mm page + try { + const res = await fetch(`${location.protocol}//${location.host}/startup`); + const curr = await res.text(); + if (startUp === "") startUp = curr; + if (startUp !== curr) { + startUp = ""; + window.location.reload(true); + console.warn("Refreshing Website because server was restarted"); + } + } catch (err) { + Log.error(`MagicMirror not reachable: ${err}`); + } + }, config.checkServerInterval); + } }, /** diff --git a/js/server.js b/js/server.js index 771870f244..0cb1b92286 100644 --- a/js/server.js +++ b/js/server.js @@ -15,7 +15,7 @@ const socketio = require("socket.io"); const Log = require("logger"); const Utils = require("./utils"); -const { cors, getConfig, getHtml, getVersion } = require("./server_functions"); +const { cors, getConfig, getHtml, getVersion, getStartup } = require("./server_functions"); /** * Server @@ -91,6 +91,8 @@ function Server(config) { app.get("/config", (req, res) => getConfig(req, res)); + app.get("/startup", (req, res) => getStartup(req, res)); + app.get("/", (req, res) => getHtml(req, res)); server.on("listening", () => { diff --git a/js/server_functions.js b/js/server_functions.js index ef418e3244..5693ad41c4 100644 --- a/js/server_functions.js +++ b/js/server_functions.js @@ -1,6 +1,7 @@ const fs = require("fs"); const path = require("path"); const Log = require("logger"); +const startUp = new Date(); /** * Gets the config. @@ -11,6 +12,15 @@ function getConfig(req, res) { res.send(config); } +/** + * Gets the startup time. + * @param {Request} req - the request + * @param {Response} res - the result + */ +function getStartup(req, res) { + res.send(startUp); +} + /** * A method that forwards HTTP Get-methods to the internet to avoid CORS-errors. * @@ -117,4 +127,4 @@ function getVersion(req, res) { res.send(global.version); } -module.exports = { cors, getConfig, getHtml, getVersion }; +module.exports = { cors, getConfig, getHtml, getVersion, getStartup }; From 91fd931a588064e7568900d8655edb80f29d215d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bugsounet=20-=20C=C3=A9dric?= Date: Wed, 13 Sep 2023 22:47:07 +0200 Subject: [PATCH 150/204] Convert HTML entities, codes and tag (#3191) related to PR [#3092](https://github.com/MichMich/MagicMirror/pull/3092) maybe best way is using `html-to-text` library sample: `"Hello World"` will be transformed to `"Hello World"` --- CHANGELOG.md | 1 + modules/default/newsfeed/newsfeedfetcher.js | 3 + package-lock.json | 184 +++++++++++++++----- package.json | 1 + 4 files changed, 141 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9c866b7b1..47553d30b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ _This release is scheduled to be released on 2023-10-01._ - Fix electron width/heigth when using xrandr under bullseye - Fix time issue with certain recurring events in calendar module - Fix ipWhiteList test (#3179) +- Fix newsfeed: Convert HTML entities, codes and tag in description (#3191) ## [2.24.0] - 2023-07-01 diff --git a/modules/default/newsfeed/newsfeedfetcher.js b/modules/default/newsfeed/newsfeedfetcher.js index f61867486c..a0d871fac5 100644 --- a/modules/default/newsfeed/newsfeedfetcher.js +++ b/modules/default/newsfeed/newsfeedfetcher.js @@ -8,6 +8,7 @@ const stream = require("stream"); const FeedMe = require("feedme"); const iconv = require("iconv-lite"); +const { htmlToText } = require("html-to-text"); const Log = require("logger"); const NodeHelper = require("node_helper"); @@ -53,6 +54,8 @@ const NewsfeedFetcher = function (url, reloadInterval, encoding, logFeedWarnings if (title && pubdate) { const regex = /(<([^>]+)>)/gi; description = description.toString().replace(regex, ""); + // Convert HTML entities, codes and tag + description = htmlToText(description, { wordwrap: false }); items.push({ title: title, diff --git a/package-lock.json b/package-lock.json index 20c00a4d7c..461b37f110 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "express-ipfilter": "^1.3.1", "feedme": "^2.0.2", "helmet": "^7.0.0", + "html-to-text": "^9.0.5", "iconv-lite": "^0.6.3", "luxon": "^1.28.1", "module-alias": "^2.2.3", @@ -1411,6 +1412,18 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, + "node_modules/@selderee/plugin-htmlparser2": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", + "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", + "dependencies": { + "domhandler": "^5.0.3", + "selderee": "^0.11.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -2290,11 +2303,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/base-64": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", - "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" - }, "node_modules/base64id": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", @@ -2654,14 +2662,6 @@ "node": ">=10" } }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "engines": { - "node": "*" - } - }, "node_modules/ci-info": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", @@ -2982,14 +2982,6 @@ "node": ">= 8" } }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "engines": { - "node": "*" - } - }, "node_modules/css-functions-list": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.0.tgz", @@ -3176,7 +3168,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3453,6 +3444,30 @@ "node": ">=6.0.0" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, "node_modules/domexception": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", @@ -3465,6 +3480,33 @@ "node": ">=12" } }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -3595,7 +3637,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, "engines": { "node": ">=0.12" }, @@ -5072,6 +5113,39 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/html-to-text": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", + "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", + "dependencies": { + "@selderee/plugin-htmlparser2": "^0.11.0", + "deepmerge": "^4.3.1", + "dom-serializer": "^2.0.0", + "htmlparser2": "^8.0.2", + "selderee": "^0.11.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -5338,11 +5412,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, "node_modules/is-builtin-module": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", @@ -6350,16 +6419,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/js-sha256": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", - "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" - }, - "node_modules/js-sha512": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz", - "integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ==" - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6526,6 +6585,14 @@ "integrity": "sha512-9pSL5XB4J+ifHP0e0jmmC98OGC1nL8/JjS+fi6mnTlIf//yt/MfVLtKg7S6nCtj/8KTcWX7nRlY0XywoYY1ISQ==", "dev": true }, + "node_modules/leac": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", + "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -6983,16 +7050,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "dependencies": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - } - }, "node_modules/mdn-data": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", @@ -7630,6 +7687,18 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parseley": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", + "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", + "dependencies": { + "leac": "^0.6.0", + "peberminta": "^0.9.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -7682,6 +7751,14 @@ "node": ">=8" } }, + "node_modules/peberminta": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", + "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -8513,6 +8590,17 @@ "node": ">=v12.22.7" } }, + "node_modules/selderee": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", + "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", + "dependencies": { + "parseley": "^0.12.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", diff --git a/package.json b/package.json index 3f5fafd6b0..1813e1e07c 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,7 @@ "express-ipfilter": "^1.3.1", "feedme": "^2.0.2", "helmet": "^7.0.0", + "html-to-text": "^9.0.5", "iconv-lite": "^0.6.3", "luxon": "^1.28.1", "module-alias": "^2.2.3", From fa7c7fc8cf47bdae0c1cc30ab6f9afce162d924f Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Wed, 13 Sep 2023 22:59:36 +0200 Subject: [PATCH 151/204] respect width/height (no fullscreen) if set in electronOptions... (#3187) ... in `config.js`. Solves #3174 With getting width/heigt from `electron.screen.getPrimaryDisplay().workAreaSize` introduced with https://github.com/MichMich/MagicMirror/pull/3161 the solution was to remove the `setFullscreen` line. So per default the fullscreen resolution is used but when someone now uses `electronOptions.width`/`electronOptions.height` in `config.js` these parameters are used and so "no fullscreen" is possible. --- CHANGELOG.md | 3 ++- js/electron.js | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47553d30b3..9319104f17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). _This release is scheduled to be released on 2023-10-01._ -> ⚠️ This release needs nodejs version > `v18`, older release have reached end of life and will not work! +> ⚠️ This release needs nodejs version >= `v18`, older release have reached end of life and will not work! ### Added @@ -45,6 +45,7 @@ _This release is scheduled to be released on 2023-10-01._ - Fix time issue with certain recurring events in calendar module - Fix ipWhiteList test (#3179) - Fix newsfeed: Convert HTML entities, codes and tag in description (#3191) +- Respect width/height (no fullscreen) if set in electronOptions in `config.js` (#3174) ## [2.24.0] - 2023-07-01 diff --git a/js/electron.js b/js/electron.js index c6cb273a2e..d225470492 100644 --- a/js/electron.js +++ b/js/electron.js @@ -130,7 +130,6 @@ function createWindow() { }); mainWindow.once("ready-to-show", () => { - mainWindow.setFullScreen(true); mainWindow.show(); }); } From 7127979c6fe201a08f42cbff6226dc9235fe6dd1 Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Thu, 14 Sep 2023 08:01:50 +0200 Subject: [PATCH 152/204] electron: add missing fullscreen option (#3192) follow up for https://github.com/MichMich/MagicMirror/pull/3187 @bugsounet can you please confirm that this now works for you? --- CHANGELOG.md | 2 +- js/electron.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9319104f17..ca9184d796 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,7 +45,7 @@ _This release is scheduled to be released on 2023-10-01._ - Fix time issue with certain recurring events in calendar module - Fix ipWhiteList test (#3179) - Fix newsfeed: Convert HTML entities, codes and tag in description (#3191) -- Respect width/height (no fullscreen) if set in electronOptions in `config.js` (#3174) +- Respect width/height (no fullscreen) if set in electronOptions (together with `fullscreen: false`) in `config.js` (#3174) ## [2.24.0] - 2023-07-01 diff --git a/js/electron.js b/js/electron.js index d225470492..43f637acbb 100644 --- a/js/electron.js +++ b/js/electron.js @@ -59,6 +59,7 @@ function createWindow() { electronOptionsDefaults.frame = false; electronOptionsDefaults.transparent = true; electronOptionsDefaults.hasShadow = false; + electronOptionsDefaults.fullscreen = true; } const electronOptions = Object.assign({}, electronOptionsDefaults, config.electronOptions); From e5adbea49c5341451a6a3149df2cf8af812037c9 Mon Sep 17 00:00:00 2001 From: Teddy Date: Thu, 14 Sep 2023 15:32:24 +0200 Subject: [PATCH 153/204] Update french translation (#3194) Updated the French translation according to the English file. --------- Co-authored-by: TeddyStarinvest --- CHANGELOG.md | 1 + translations/fr.json | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca9184d796..8616abaa61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ _This release is scheduled to be released on 2023-10-01._ - Update typescript definition for modules - Cleaned up nunjuck templates - Replace `node-fetch` with internal fetch (#2649) and remove `digest-fetch` +- Updated the French translation according to the English file. ### Fixed diff --git a/translations/fr.json b/translations/fr.json index 1e4a294652..319e0fda3d 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -29,9 +29,16 @@ "FEELS": "Ressenti {DEGREE}", "PRECIP_POP": "Probabilité de précipitations", + "PRECIP_AMOUNT": "Quantité des précipitations", "MODULE_CONFIG_CHANGED": "Les options de configuration du module {MODULE_NAME} ont changé.\nVeuillez consulter la documentation.", "MODULE_CONFIG_ERROR": "Erreur dans le module {MODULE_NAME}. {ERROR}", + "MODULE_ERROR_MALFORMED_URL": "URL mal formée.", + "MODULE_ERROR_NO_CONNECTION": "Pas de connexion Internet.", + "MODULE_ERROR_UNAUTHORIZED": "L'autorisation à échouée.", + "MODULE_ERROR_UNSPECIFIED": "Consultez les journaux pour plus de détails.", + + "NEWSFEED_NO_ITEMS": "Aucune nouvelle pour le moment.", "UPDATE_NOTIFICATION": "Une mise à jour de MagicMirror² est disponible", "UPDATE_NOTIFICATION_MODULE": "Une mise à jour est disponible pour le module {MODULE_NAME}.", From af0fe37f70efe14171a2093a32b27e55087c5d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bugsounet=20-=20C=C3=A9dric?= Date: Tue, 19 Sep 2023 07:14:11 +0200 Subject: [PATCH 154/204] Fix: Uncaught SyntaxError: Identifier 'getCorsUrl' has already been declared (#3204) Issue #3202 move shared modules/default/utils.js file to index.html --- CHANGELOG.md | 1 + index.html | 1 + modules/default/clock/clock.js | 2 +- modules/default/weather/weather.js | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8616abaa61..ab411fd19f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ _This release is scheduled to be released on 2023-10-01._ - Fix ipWhiteList test (#3179) - Fix newsfeed: Convert HTML entities, codes and tag in description (#3191) - Respect width/height (no fullscreen) if set in electronOptions (together with `fullscreen: false`) in `config.js` (#3174) +- Fix `Uncaught SyntaxError: Identifier 'getCorsUrl' has already been declared (at utils.js:1:1)` when using `clock` and `weather` module (#3204) ## [2.24.0] - 2023-07-01 diff --git a/index.html b/index.html index 501c1af5b0..b97124be10 100644 --- a/index.html +++ b/index.html @@ -46,6 +46,7 @@ + diff --git a/modules/default/clock/clock.js b/modules/default/clock/clock.js index 4345d20a49..c063d4dc88 100644 --- a/modules/default/clock/clock.js +++ b/modules/default/clock/clock.js @@ -38,7 +38,7 @@ Module.register("clock", { }, // Define required scripts. getScripts: function () { - return ["moment.js", "moment-timezone.js", "suncalc.js", this.file("../utils.js")]; + return ["moment.js", "moment-timezone.js", "suncalc.js"]; }, // Define styles. getStyles: function () { diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index 701e151622..57c91d7b07 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -62,7 +62,7 @@ Module.register("weather", { // Return the scripts that are necessary for the weather module. getScripts: function () { - return ["moment.js", this.file("../utils.js"), "weatherutils.js", "weatherobject.js", this.file("providers/overrideWrapper.js"), "weatherprovider.js", "suncalc.js", this.file(`providers/${this.config.weatherProvider.toLowerCase()}.js`)]; + return ["moment.js", "weatherutils.js", "weatherobject.js", this.file("providers/overrideWrapper.js"), "weatherprovider.js", "suncalc.js", this.file(`providers/${this.config.weatherProvider.toLowerCase()}.js`)]; }, // Override getHeader method. From 4eccce3f775b498577122f53b4416653503dcc45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bugsounet=20-=20C=C3=A9dric?= Date: Tue, 19 Sep 2023 20:14:35 +0200 Subject: [PATCH 155/204] Fix: AnimateCSS merge hide() and show() animated css class when we do multiple call (#3200) PR: #3113 I see this bugs: AnimateCSS merge hide() and show() animated css class when we do multiple call --> result it will stay en hide state I think event listener (is animateCSS file) is not a proper solution I correct it with like traditional code with timer Fix too: AnimateIn on first start --- CHANGELOG.md | 1 + js/animateCSS.js | 53 ++++++++++++++++++------------------- js/main.js | 69 +++++++++++++++++++++++++++++++++++------------- js/module.js | 2 ++ 4 files changed, 80 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab411fd19f..efb6f59924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ _This release is scheduled to be released on 2023-10-01._ - Fix ipWhiteList test (#3179) - Fix newsfeed: Convert HTML entities, codes and tag in description (#3191) - Respect width/height (no fullscreen) if set in electronOptions (together with `fullscreen: false`) in `config.js` (#3174) +- Fix: AnimateCSS merge hide() and show() animated css class when we do multiple call - Fix `Uncaught SyntaxError: Identifier 'getCorsUrl' has already been declared (at utils.js:1:1)` when using `clock` and `weather` module (#3204) ## [2.24.0] - 2023-07-01 diff --git a/js/animateCSS.js b/js/animateCSS.js index ae6e7bec7b..cf3f26bcb7 100644 --- a/js/animateCSS.js +++ b/js/animateCSS.js @@ -130,36 +130,35 @@ const AnimateCSSOut = [ /** * Create an animation with Animate CSS - * resolved as Promise when done * @param {string} [element] div element to animate. * @param {string} [animation] animation name. * @param {number} [animationTime] animation duration. */ -function AnimateCSS(element, animation, animationTime) { - /* We create a Promise and return it */ - return new Promise((resolve) => { - const animationName = `animate__${animation}`; - const node = document.getElementById(element); - if (!node) { - // don't execute animate and resolve - Log.warn(`AnimateCSS: node not found for`, element); - resolve(); - return; - } - node.style.setProperty("--animate-duration", `${animationTime}s`); - node.classList.add("animate__animated", animationName); - - /** - * When the animation ends, we clean the classes and resolve the Promise - * @param {object} event object - */ - function handleAnimationEnd(event) { - node.classList.remove("animate__animated", animationName); - node.style.removeProperty("--animate-duration", `${animationTime}s`); - event.stopPropagation(); - resolve(); - } +function addAnimateCSS(element, animation, animationTime) { + const animationName = `animate__${animation}`; + const node = document.getElementById(element); + if (!node) { + // don't execute animate: we don't find div + Log.warn(`addAnimateCSS: node not found for`, element); + return; + } + node.style.setProperty("--animate-duration", `${animationTime}s`); + node.classList.add("animate__animated", animationName); +} - node.addEventListener("animationend", handleAnimationEnd, { once: true }); - }); +/** + * Remove an animation with Animate CSS + * @param {string} [element] div element to animate. + * @param {string} [animation] animation name. + */ +function removeAnimateCSS(element, animation) { + const animationName = `animate__${animation}`; + const node = document.getElementById(element); + if (!node) { + // don't execute animate: we don't find div + Log.warn(`removeAnimateCSS: node not found for`, element); + return; + } + node.classList.remove("animate__animated", animationName); + node.style.removeProperty("--animate-duration"); } diff --git a/js/main.js b/js/main.js index 3c023af359..c7d4d39f77 100644 --- a/js/main.js +++ b/js/main.js @@ -1,4 +1,4 @@ -/* global Loader, defaults, Translator, AnimateCSS, AnimateCSSIn, AnimateCSSOut */ +/* global Loader, defaults, Translator, addAnimateCSS, removeAnimateCSS, AnimateCSSIn, AnimateCSSOut */ /* MagicMirror² * Main System @@ -57,7 +57,7 @@ const MM = (function () { // create the domCreationPromise with AnimateCSS (with animateIn of module definition) // or just display it var domCreationPromise; - if (haveAnimateIn) domCreationPromise = updateDom(module, 1000, null, haveAnimateIn, true); + if (haveAnimateIn) domCreationPromise = updateDom(module, { options: { speed: 1000, animate: { in: haveAnimateIn } } }, true); else domCreationPromise = updateDom(module, 0); domCreationPromises.push(domCreationPromise); @@ -269,7 +269,7 @@ const MM = (function () { * @param {Function} callback Called when the animation is done. * @param {object} [options] Optional settings for the hide method. */ - const hideModule = async function (module, speed, callback, options = {}) { + const hideModule = function (module, speed, callback, options = {}) { // set lockString if set in options. if (options.lockString) { // Log.log("Has lockstring: " + options.lockString); @@ -281,7 +281,17 @@ const MM = (function () { const moduleWrapper = document.getElementById(module.identifier); if (moduleWrapper !== null) { clearTimeout(module.showHideTimer); - + // reset all animations if needed + if (module.hasAnimateOut) { + removeAnimateCSS(module.identifier, module.hasAnimateOut); + Log.debug(`${module.identifier} Force remove animateOut (in hide): ${module.hasAnimateOut}`); + module.hasAnimateOut = false; + } + if (module.hasAnimateIn) { + removeAnimateCSS(module.identifier, module.hasAnimateIn); + Log.debug(`${module.identifier} Force remove animateIn (in hide): ${module.hasAnimateIn}`); + module.hasAnimateIn = false; + } // haveAnimateName for verify if we are using AninateCSS library // we check AnimateCSSOut Array for validate it // and finaly return the animate name or `null` (for default MM² animation) @@ -294,16 +304,22 @@ const MM = (function () { if (haveAnimateName) { // with AnimateCSS Log.debug(`${module.identifier} Has animateOut: ${haveAnimateName}`); - await AnimateCSS(module.identifier, haveAnimateName, speed / 1000); - // AnimateCSS is now done - moduleWrapper.style.opacity = 0; - moduleWrapper.classList.add("hidden"); - moduleWrapper.style.position = "fixed"; + module.hasAnimateOut = haveAnimateName; + addAnimateCSS(module.identifier, haveAnimateName, speed / 1000); + module.showHideTimer = setTimeout(function () { + removeAnimateCSS(module.identifier, haveAnimateName); + Log.debug(`${module.identifier} Remove animateOut: ${module.hasAnimateOut}`); + // AnimateCSS is now done + moduleWrapper.style.opacity = 0; + moduleWrapper.classList.add("hidden"); + moduleWrapper.style.position = "fixed"; + module.hasAnimateOut = false; - updateWrapperStates(); - if (typeof callback === "function") { - callback(); - } + updateWrapperStates(); + if (typeof callback === "function") { + callback(); + } + }, speed); } else { // default MM² Animate moduleWrapper.style.transition = `opacity ${speed / 1000}s`; @@ -338,7 +354,7 @@ const MM = (function () { * @param {Function} callback Called when the animation is done. * @param {object} [options] Optional settings for the show method. */ - const showModule = async function (module, speed, callback, options = {}) { + const showModule = function (module, speed, callback, options = {}) { // remove lockString if set in options. if (options.lockString) { const index = module.lockStrings.indexOf(options.lockString); @@ -356,6 +372,17 @@ const MM = (function () { } return; } + // reset all animations if needed + if (module.hasAnimateOut) { + removeAnimateCSS(module.identifier, module.hasAnimateOut); + Log.debug(`${module.identifier} Force remove animateOut (in show): ${module.hasAnimateOut}`); + module.hasAnimateOut = false; + } + if (module.hasAnimateIn) { + removeAnimateCSS(module.identifier, module.hasAnimateIn); + Log.debug(`${module.identifier} Force remove animateIn (in show): ${module.hasAnimateIn}`); + module.hasAnimateIn = false; + } module.hidden = false; @@ -392,10 +419,16 @@ const MM = (function () { if (haveAnimateName) { // with AnimateCSS Log.debug(`${module.identifier} Has animateIn: ${haveAnimateName}`); - await AnimateCSS(module.identifier, haveAnimateName, speed / 1000); - if (typeof callback === "function") { - callback(); - } + module.hasAnimateIn = haveAnimateName; + addAnimateCSS(module.identifier, haveAnimateName, speed / 1000); + module.showHideTimer = setTimeout(function () { + removeAnimateCSS(module.identifier, haveAnimateName); + Log.debug(`${module.identifier} Remove animateIn: ${haveAnimateName}`); + module.hasAnimateIn = false; + if (typeof callback === "function") { + callback(); + } + }, speed); } else { // default MM² Animate module.showHideTimer = setTimeout(function () { diff --git a/js/module.js b/js/module.js index 62534b0178..4ef3ab1d61 100644 --- a/js/module.js +++ b/js/module.js @@ -205,6 +205,8 @@ const Module = Class.extend({ this.name = data.name; this.identifier = data.identifier; this.hidden = false; + this.hasAnimateIn = false; + this.hasAnimateOut = false; this.setConfig(data.config, data.configDeepMerge); }, From 8b1c279c077b67f1cf04cddf24ca0fab027d49d6 Mon Sep 17 00:00:00 2001 From: martingron <61826403+martingron@users.noreply.github.com> Date: Wed, 20 Sep 2023 11:37:01 +0200 Subject: [PATCH 156/204] Update yr provider to new api (#3197) Some changes after yr api was deprecated and replaced with a new one. Fixes #3189 --- CHANGELOG.md | 1 + modules/default/weather/providers/yr.js | 11 +++-------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efb6f59924..c4ed028b7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,6 +91,7 @@ Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not al - Fix date not shown when clock in analog mode (#3100) - Fix envcanada today percentage-of-precipitation (#3106) - Fix updatenotification where no branch is checked out but e.g. a version tag (#3130) +- Fix yr weather provider after changes in yr API (#3189) ## [2.23.0] - 2023-04-04 diff --git a/modules/default/weather/providers/yr.js b/modules/default/weather/providers/yr.js index 52de53ba12..be876a6dc8 100644 --- a/modules/default/weather/providers/yr.js +++ b/modules/default/weather/providers/yr.js @@ -352,8 +352,7 @@ WeatherProvider.register("yr", { if (hours.length < 2) { hours = `0${hours}`; } - - return `${this.config.apiBase}/sunrise/2.0/.json?date=${date}&days=${days}&height=${altitude}&lat=${lat}&lon=${lon}&offset=${utcOffsetPrefix}${hours}%3A${minutes}`; + return `${this.config.apiBase}/sunrise/2.3/sun?lat=${lat}&lon=${lon}&date=${date}&offset=${utcOffsetPrefix}${hours}%3A${minutes}`; }, cacheStellarData(data) { @@ -362,8 +361,6 @@ WeatherProvider.register("yr", { getWeatherDataFrom(forecast, stellarData, units) { const weather = new WeatherObject(); - const stellarTimesToday = stellarData?.today ? this.getStellarTimesFrom(stellarData.today, moment().format("YYYY-MM-DD")) : undefined; - const stellarTimesTomorrow = stellarData?.tomorrow ? this.getStellarTimesFrom(stellarData.tomorrow, moment().add(1, "days").format("YYYY-MM-DD")) : undefined; weather.date = moment(forecast.time); weather.windSpeed = forecast.data.instant.details.wind_speed; @@ -377,10 +374,8 @@ WeatherProvider.register("yr", { weather.precipitationProbability = forecast.precipitationProbability; weather.precipitationUnits = units.precipitation_amount; - if (stellarTimesToday) { - weather.sunset = moment(stellarTimesToday.sunset.time); - weather.sunrise = weather.sunset < moment() && stellarTimesTomorrow ? moment(stellarTimesTomorrow.sunrise.time) : moment(stellarTimesToday.sunrise.time); - } + weather.sunrise = stellarData?.today?.properties?.sunrise?.time; + weather.sunset = stellarData?.today?.properties?.sunset?.time; return weather; }, From a67a0b677cb4155fafca072d3acb313b291e56d5 Mon Sep 17 00:00:00 2001 From: Teddy Date: Wed, 20 Sep 2023 22:04:41 +0200 Subject: [PATCH 157/204] Add eventClass for customEvents in calendar (#3193) Hello, This pull request allows you to add a class to the tr of the event sought in customEvents. You must enter the class with the "eventClass" option. --------- Co-authored-by: TeddyStarinvest --- CHANGELOG.md | 1 + modules/default/calendar/calendar.js | 13 ++++++++----- tests/configs/modules/calendar/custom.js | 2 +- tests/e2e/modules/calendar_spec.js | 4 ++++ 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4ed028b7f..f06974f635 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ _This release is scheduled to be released on 2023-10-01._ - Added AnimateIn and animateOut in module config definition - Apply AnimateIn rules on the first start - Added automatic client page reload when server was restarted by setting `reloadAfterServerRestart: true` in `config.js`, per default `false` (#3105) +- Added eventClass option for customEvents on the default calendar ### Removed diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 30f7baa226..681177d514 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -42,7 +42,7 @@ Module.register("calendar", { hideDuplicates: true, showTimeToday: false, colored: false, - customEvents: [], // Array of {keyword: "", symbol: "", color: ""} where Keyword is a regexp and symbol/color are to be applied for matched + customEvents: [], // Array of {keyword: "", symbol: "", color: "", eventClass: ""} where Keyword is a regexp and symbol/color/eventClass are to be applied for matched tableClass: "small", calendars: [ { @@ -321,12 +321,12 @@ Module.register("calendar", { } } - // Color events if custom color is specified + // Color events if custom color or eventClass are specified if (this.config.customEvents.length > 0) { for (let ev in this.config.customEvents) { - if (typeof this.config.customEvents[ev].color !== "undefined" && this.config.customEvents[ev].color !== "") { - let needle = new RegExp(this.config.customEvents[ev].keyword, "gi"); - if (needle.test(event.title)) { + let needle = new RegExp(this.config.customEvents[ev].keyword, "gi"); + if (needle.test(event.title)) { + if (typeof this.config.customEvents[ev].color !== "undefined" && this.config.customEvents[ev].color !== "") { // Respect parameter ColoredSymbolOnly also for custom events if (this.config.coloredText) { eventWrapper.style.cssText = `color:${this.config.customEvents[ev].color}`; @@ -337,6 +337,9 @@ Module.register("calendar", { } break; } + if (typeof this.config.customEvents[ev].eventClass !== "undefined" && this.config.customEvents[ev].eventClass !== "") { + eventWrapper.className += ` ${this.config.customEvents[ev].eventClass}`; + } } } } diff --git a/tests/configs/modules/calendar/custom.js b/tests/configs/modules/calendar/custom.js index 993ac483e6..4153da0460 100644 --- a/tests/configs/modules/calendar/custom.js +++ b/tests/configs/modules/calendar/custom.js @@ -11,7 +11,7 @@ let config = { module: "calendar", position: "bottom_bar", config: { - customEvents: [{ keyword: "CustomEvent", symbol: "dice" }], + customEvents: [{ keyword: "CustomEvent", symbol: "dice", eventClass: "undo" }], calendars: [ { maximumEntries: 5, diff --git a/tests/e2e/modules/calendar_spec.js b/tests/e2e/modules/calendar_spec.js index fc9c1a174e..299bdf664e 100644 --- a/tests/e2e/modules/calendar_spec.js +++ b/tests/e2e/modules/calendar_spec.js @@ -60,6 +60,10 @@ describe("Calendar module", () => { await testElementLength(".calendar .event .fa-dice", 1); }); + it("should show a customEvent calendar eventClass in one event", async () => { + await testElementLength(".calendar .event.undo", 1); + }); + it("should show two custom icons for repeating events", async () => { await testElementLength(".calendar .event .fa-undo", 2); }); From 95ec3096e0830af0724078d33d23ee4e2ddddbea Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Fri, 22 Sep 2023 14:45:46 +0200 Subject: [PATCH 158/204] avoid overriding `config.js` when running tests (#3205) solves #3201 --- CHANGELOG.md | 1 + tests/e2e/helpers/weather-functions.js | 3 +-- tests/e2e/modules/weather_current_spec.js | 2 ++ tests/e2e/modules/weather_forecast_spec.js | 2 ++ tests/e2e/modules/weather_hourly_spec.js | 2 ++ tests/e2e/template_spec.js | 6 ++++++ tests/electron/helpers/weather-setup.js | 5 ++--- tests/electron/modules/weather_spec.js | 2 ++ tests/utils/weather_mocker.js | 15 ++++++++++++--- 9 files changed, 30 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f06974f635..e59f84c60d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ _This release is scheduled to be released on 2023-10-01._ - Respect width/height (no fullscreen) if set in electronOptions (together with `fullscreen: false`) in `config.js` (#3174) - Fix: AnimateCSS merge hide() and show() animated css class when we do multiple call - Fix `Uncaught SyntaxError: Identifier 'getCorsUrl' has already been declared (at utils.js:1:1)` when using `clock` and `weather` module (#3204) +- Fix overriding `config.js` when running tests (#3201) ## [2.24.0] - 2023-07-01 diff --git a/tests/e2e/helpers/weather-functions.js b/tests/e2e/helpers/weather-functions.js index e28eb9de02..02713754cf 100644 --- a/tests/e2e/helpers/weather-functions.js +++ b/tests/e2e/helpers/weather-functions.js @@ -13,7 +13,6 @@ exports.getText = async (element, result) => { }; exports.startApp = async (configFileName, additionalMockData) => { - injectMockData(configFileName, additionalMockData); - await helpers.startApplication(""); + await helpers.startApplication(injectMockData(configFileName, additionalMockData)); await helpers.getDocument(); }; diff --git a/tests/e2e/modules/weather_current_spec.js b/tests/e2e/modules/weather_current_spec.js index 592b3735d0..d97f83094e 100644 --- a/tests/e2e/modules/weather_current_spec.js +++ b/tests/e2e/modules/weather_current_spec.js @@ -1,9 +1,11 @@ const helpers = require("../helpers/global-setup"); const weatherFunc = require("../helpers/weather-functions"); +const { cleanupMockData } = require("../../utils/weather_mocker"); describe("Weather module", () => { afterAll(async () => { await helpers.stopApplication(); + await cleanupMockData(); }); describe("Current weather", () => { diff --git a/tests/e2e/modules/weather_forecast_spec.js b/tests/e2e/modules/weather_forecast_spec.js index 2f10692276..a5ce70d20a 100644 --- a/tests/e2e/modules/weather_forecast_spec.js +++ b/tests/e2e/modules/weather_forecast_spec.js @@ -1,9 +1,11 @@ const helpers = require("../helpers/global-setup"); const weatherFunc = require("../helpers/weather-functions"); +const { cleanupMockData } = require("../../utils/weather_mocker"); describe("Weather module: Weather Forecast", () => { afterAll(async () => { await helpers.stopApplication(); + await cleanupMockData(); }); describe("Default configuration", () => { diff --git a/tests/e2e/modules/weather_hourly_spec.js b/tests/e2e/modules/weather_hourly_spec.js index 3a5f03f138..87fc4411cb 100644 --- a/tests/e2e/modules/weather_hourly_spec.js +++ b/tests/e2e/modules/weather_hourly_spec.js @@ -1,9 +1,11 @@ const helpers = require("../helpers/global-setup"); const weatherFunc = require("../helpers/weather-functions"); +const { cleanupMockData } = require("../../utils/weather_mocker"); describe("Weather module: Weather Hourly Forecast", () => { afterAll(async () => { await helpers.stopApplication(); + await cleanupMockData(); }); describe("Default configuration", () => { diff --git a/tests/e2e/template_spec.js b/tests/e2e/template_spec.js index 3bcc5e4427..46417aea79 100644 --- a/tests/e2e/template_spec.js +++ b/tests/e2e/template_spec.js @@ -1,3 +1,4 @@ +const fs = require("fs"); const helpers = require("./helpers/global-setup"); describe("templated config with port variable", () => { @@ -6,6 +7,11 @@ describe("templated config with port variable", () => { }); afterAll(async () => { await helpers.stopApplication(); + try { + fs.unlinkSync("tests/configs/port_variable.js"); + } catch (err) { + // do nothing + } }); it("should return 200", async () => { diff --git a/tests/electron/helpers/weather-setup.js b/tests/electron/helpers/weather-setup.js index 4dd3cdb2a6..e939af6763 100644 --- a/tests/electron/helpers/weather-setup.js +++ b/tests/electron/helpers/weather-setup.js @@ -13,7 +13,6 @@ exports.getText = async (element, result) => { ).toBe(result); }; -exports.startApp = async (configFileNameName, systemDate) => { - injectMockData(configFileNameName); - await helpers.startApplication("", systemDate); +exports.startApp = async (configFileName, systemDate) => { + await helpers.startApplication(injectMockData(configFileName), systemDate); }; diff --git a/tests/electron/modules/weather_spec.js b/tests/electron/modules/weather_spec.js index fe77743110..811a0e02da 100644 --- a/tests/electron/modules/weather_spec.js +++ b/tests/electron/modules/weather_spec.js @@ -1,9 +1,11 @@ const helpers = require("../helpers/global-setup"); const weatherHelper = require("../helpers/weather-setup"); +const { cleanupMockData } = require("../../utils/weather_mocker"); describe("Weather module", () => { afterEach(async () => { await helpers.stopApplication(); + await cleanupMockData(); }); describe("Current weather with sunrise", () => { diff --git a/tests/utils/weather_mocker.js b/tests/utils/weather_mocker.js index 81e912451e..83279001fa 100644 --- a/tests/utils/weather_mocker.js +++ b/tests/utils/weather_mocker.js @@ -1,5 +1,7 @@ const fs = require("fs"); const path = require("path"); +const util = require("util"); +const exec = util.promisify(require("child_process").exec); const _ = require("lodash"); /** @@ -35,9 +37,16 @@ const injectMockData = (configFileName, extendedData = {}) => { } else { mockWeather = readMockData("current", extendedData); } - let content = fs.readFileSync(path.resolve(`${__dirname}../../../${configFileName}`)).toString(); + let content = fs.readFileSync(configFileName).toString(); content = content.replace("#####WEATHERDATA#####", mockWeather); - fs.writeFileSync(path.resolve(`${__dirname}../../../config/config.js`), content); + const tempFile = configFileName.replace(".js", "_temp.js"); + fs.writeFileSync(tempFile, content); + return tempFile; }; -module.exports = { injectMockData }; +const cleanupMockData = async () => { + const tempDir = path.resolve(`${__dirname}/../configs`).toString(); + await exec(`find ${tempDir} -type f -name *_temp.js -delete`); +}; + +module.exports = { injectMockData, cleanupMockData }; From ad665a7a336a497dc2808ba1e8cfc98d6d0b4101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bugsounet=20-=20C=C3=A9dric?= Date: Mon, 25 Sep 2023 22:27:52 +0200 Subject: [PATCH 159/204] AnimateCSS integration in tests suite (#3206) Hi, Just because, i never try to code a test I purpose to supervise this work After, perhaps it is not necessary to integrate it in develop branch. It's up to you to decide --- CHANGELOG.md | 1 + .../compliments/compliments_animateCSS.js | 28 +++++++ ...ompliments_animateCSS_fallbackToDefault.js | 29 +++++++ ...iments_animateCSS_invertedAnimationName.js | 28 +++++++ tests/e2e/animateCSS_spec.js | 78 +++++++++++++++++++ tests/e2e/helpers/global-setup.js | 1 + 6 files changed, 165 insertions(+) create mode 100644 tests/configs/modules/compliments/compliments_animateCSS.js create mode 100644 tests/configs/modules/compliments/compliments_animateCSS_fallbackToDefault.js create mode 100644 tests/configs/modules/compliments/compliments_animateCSS_invertedAnimationName.js create mode 100644 tests/e2e/animateCSS_spec.js diff --git a/CHANGELOG.md b/CHANGELOG.md index e59f84c60d..bfbce86b24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ _This release is scheduled to be released on 2023-10-01._ - Apply AnimateIn rules on the first start - Added automatic client page reload when server was restarted by setting `reloadAfterServerRestart: true` in `config.js`, per default `false` (#3105) - Added eventClass option for customEvents on the default calendar +- Added AnimateCSS integration in tests suite (#3206) ### Removed diff --git a/tests/configs/modules/compliments/compliments_animateCSS.js b/tests/configs/modules/compliments/compliments_animateCSS.js new file mode 100644 index 0000000000..24e2736073 --- /dev/null +++ b/tests/configs/modules/compliments/compliments_animateCSS.js @@ -0,0 +1,28 @@ +/* MagicMirror² Test config sample for AnimateCSS integration with compliments module + * + * By bugsounet https://github.com/bugsounet + * 09/2023 + * MIT Licensed. + */ +let config = { + modules: [ + { + module: "compliments", + position: "lower_third", + animateIn: "flipInX", + animateOut: "flipOutX", + config: { + compliments: { + anytime: ["AnimateCSS Testing..."] + }, + updateInterval: 2000, + fadeSpeed: 1000 + } + } + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/configs/modules/compliments/compliments_animateCSS_fallbackToDefault.js b/tests/configs/modules/compliments/compliments_animateCSS_fallbackToDefault.js new file mode 100644 index 0000000000..89c0694466 --- /dev/null +++ b/tests/configs/modules/compliments/compliments_animateCSS_fallbackToDefault.js @@ -0,0 +1,29 @@ +/* MagicMirror² Test config sample for AnimateCSS integration with compliments module + * --> if animation name is not an AnimateCSS animation + * --> must fallback to default (no animation) + * By bugsounet https://github.com/bugsounet + * 09/2023 + * MIT Licensed. + */ +let config = { + modules: [ + { + module: "compliments", + position: "lower_third", + animateIn: "foo", + animateOut: "bar", + config: { + compliments: { + anytime: ["AnimateCSS Testing..."] + }, + updateInterval: 2000, + fadeSpeed: 1000 + } + } + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/configs/modules/compliments/compliments_animateCSS_invertedAnimationName.js b/tests/configs/modules/compliments/compliments_animateCSS_invertedAnimationName.js new file mode 100644 index 0000000000..eb4af0ca1b --- /dev/null +++ b/tests/configs/modules/compliments/compliments_animateCSS_invertedAnimationName.js @@ -0,0 +1,28 @@ +/* MagicMirror² Test config sample for AnimateCSS integration with compliments module + * --> inversed name animation : in for out and vice versa (must return no animation) + * By bugsounet https://github.com/bugsounet + * 09/2023 + * MIT Licensed. + */ +let config = { + modules: [ + { + module: "compliments", + position: "lower_third", + animateIn: "flipOutX", + animateOut: "flipInX", + config: { + compliments: { + anytime: ["AnimateCSS Testing..."] + }, + updateInterval: 2000, + fadeSpeed: 1000 + } + } + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/e2e/animateCSS_spec.js b/tests/e2e/animateCSS_spec.js new file mode 100644 index 0000000000..3f7cac8354 --- /dev/null +++ b/tests/e2e/animateCSS_spec.js @@ -0,0 +1,78 @@ +/* AnimateCSS integration Test with compliments module + * + * By bugsounet https://github.com/bugsounet + * and helped by khassel + * 09/2023 + * MIT Licensed. + */ +const helpers = require("./helpers/global-setup.js"); + +describe("AnimateCSS integration Test", () => { + // define config file for testing + let testConfigFile = "tests/configs/modules/compliments/compliments_animateCSS.js"; + // define config file to fallback to default: wrong animation name (must return no animation) + let testConfigFileFallbackToDefault = "tests/configs/modules/compliments/compliments_animateCSS_fallbackToDefault.js"; + // define config file with an inversed name animation : in for out and vice versa (must return no animation) + let testConfigFileInvertedAnimationName = "tests/configs/modules/compliments/compliments_animateCSS_invertedAnimationName.js"; + // define config file with no animation defined + let testConfigByDefault = "tests/configs/modules/compliments/compliments_anytime.js"; + + /** + * move similar tests in function doTest + * @param {string} [animationIn] animation in name of AnimateCSS to test. + * @param {string} [animationOut] animation out name of AnimateCSS to test. + */ + const doTest = async (animationIn, animationOut) => { + await helpers.getDocument(); + let elem = await helpers.waitForElement(`.compliments`); + expect(elem).not.toBe(null); + let styles = window.getComputedStyle(elem); + + if (animationIn && animationIn !== "") { + expect(styles._values["animation-name"]).toBe(animationIn); + } else { + expect(styles._values["animation-name"]).toBe(undefined); + } + + if (animationOut && animationOut !== "") { + elem = await helpers.waitForElement(`.compliments.animate__animated.animate__${animationOut}`); + expect(elem).not.toBe(null); + styles = window.getComputedStyle(elem); + expect(styles._values["animation-name"]).toBe(animationOut); + } else { + expect(styles._values["animation-name"]).toBe(undefined); + } + }; + + afterEach(async () => { + await helpers.stopApplication(); + }); + + describe("animateIn and animateOut Test", () => { + it("with flipInX and flipOutX animation", async () => { + await helpers.startApplication(testConfigFile); + await doTest("flipInX", "flipOutX"); + }); + }); + + describe("use animateOut name for animateIn (vice versa) Test", () => { + it("without animation", async () => { + await helpers.startApplication(testConfigFileInvertedAnimationName); + await doTest(); + }); + }); + + describe("false Animation name test", () => { + it("without animation", async () => { + await helpers.startApplication(testConfigFileFallbackToDefault); + await doTest(); + }); + }); + + describe("no Animation defined test", () => { + it("without animation", async () => { + await helpers.startApplication(testConfigByDefault); + await doTest(); + }); + }); +}); diff --git a/tests/e2e/helpers/global-setup.js b/tests/e2e/helpers/global-setup.js index d5506024af..d20156e5fd 100644 --- a/tests/e2e/helpers/global-setup.js +++ b/tests/e2e/helpers/global-setup.js @@ -30,6 +30,7 @@ exports.getDocument = () => { const url = `http://${config.address || "localhost"}:${config.port || "8080"}`; jsdom.JSDOM.fromURL(url, { resources: "usable", runScripts: "dangerously" }).then((dom) => { dom.window.name = "jsdom"; + global.window = dom.window; dom.window.fetch = fetch; dom.window.onload = () => { global.document = dom.window.document; From a3c2e7b816fb18582a68ed482daca7a044141ded Mon Sep 17 00:00:00 2001 From: dgoth <132394363+dgoth@users.noreply.github.com> Date: Mon, 25 Sep 2023 15:42:27 -0500 Subject: [PATCH 160/204] Fixed probability of precipitation in weathergov.js (#3195) Fixes https://github.com/MichMich/MagicMirror/issues/3182 Fixed issue with probability of precipitation not showing up on hourly or daily forecast --------- Co-authored-by: veeck --- CHANGELOG.md | 1 + modules/default/weather/providers/weathergov.js | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfbce86b24..bea8baeb97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ _This release is scheduled to be released on 2023-10-01._ - Fix: AnimateCSS merge hide() and show() animated css class when we do multiple call - Fix `Uncaught SyntaxError: Identifier 'getCorsUrl' has already been declared (at utils.js:1:1)` when using `clock` and `weather` module (#3204) - Fix overriding `config.js` when running tests (#3201) +- Fix issue in weathergov provider with probability of precipitation not showing up on hourly or daily forecast ## [2.24.0] - 2023-07-01 diff --git a/modules/default/weather/providers/weathergov.js b/modules/default/weather/providers/weathergov.js index b1c69ee753..8111044be4 100644 --- a/modules/default/weather/providers/weathergov.js +++ b/modules/default/weather/providers/weathergov.js @@ -182,6 +182,12 @@ WeatherProvider.register("weathergov", { weather.windSpeed = WeatherUtils.convertWindToMs(weather.windSpeed); weather.windFromDirection = forecast.windDirection; weather.temperature = forecast.temperature; + //assign probability of precipitation + if (forecast.probabilityOfPrecipitation.value === null) { + weather.precipitationProbability = 0; + } else { + weather.precipitationProbability = forecast.probabilityOfPrecipitation.value; + } // use the forecast isDayTime attribute to help build the weatherType label weather.weatherType = this.convertWeatherType(forecast.shortForecast, forecast.isDaytime); @@ -238,8 +244,6 @@ WeatherProvider.register("weathergov", { * fetch forecast information for daily forecast. */ fetchForecastDaily(forecasts) { - const precipitationProbabilityRegEx = "Chance of precipitation is ([0-9]+?)%"; - // initial variable declaration const days = []; // variables for temperature range and rain @@ -262,8 +266,12 @@ WeatherProvider.register("weathergov", { minTemp = []; maxTemp = []; - const precipitation = new RegExp(precipitationProbabilityRegEx, "g").exec(forecast.detailedForecast); - if (precipitation) weather.precipitationProbability = precipitation[1]; + //assign probability of precipitation + if (forecast.probabilityOfPrecipitation.value === null) { + weather.precipitationProbability = 0; + } else { + weather.precipitationProbability = forecast.probabilityOfPrecipitation.value; + } // set new date date = moment(forecast.startTime).format("YYYY-MM-DD"); From e530c783f8a039b3126a2d9a0a030eb9c8863a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bugsounet=20-=20C=C3=A9dric?= Date: Tue, 26 Sep 2023 23:12:09 +0200 Subject: [PATCH 161/204] Update Electron based on a severity vulnerability (develop) (#3207) I just see `electron` package used in develop branch have `1 high severity vulnerability` Detail is [there](https://github.com/advisories/GHSA-j7hp-h8jx-5ppr) We can patch it with electron v26.2.2 (last version at this day) and will correct it (ChangeLog is not needed in this case) --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: veeck --- CHANGELOG.md | 4 +- package-lock.json | 1059 ++++++++++++++++++++++++--------------------- package.json | 14 +- 3 files changed, 570 insertions(+), 507 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bea8baeb97..d5229ae4de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,13 +31,13 @@ _This release is scheduled to be released on 2023-10-01._ - Update roboto fonts to version v5 - Update issue template -- Update dependencies incl. electron to v26 +- Update dev/dependencies incl. electron to v26 - Replace pretty-quick by lint-staged () - Update engine node >=18. v16 reached it's end of life. (#3170) - Update typescript definition for modules - Cleaned up nunjuck templates - Replace `node-fetch` with internal fetch (#2649) and remove `digest-fetch` -- Updated the French translation according to the English file. +- Update the French translation according to the English file. ### Fixed diff --git a/package-lock.json b/package-lock.json index 461b37f110..ea1a2baa2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "colors": "^1.4.0", "console-stamp": "^3.1.2", "envsub": "^4.1.0", - "eslint": "^8.48.0", + "eslint": "^8.50.0", "express": "^4.18.2", "express-ipfilter": "^1.3.1", "feedme": "^2.0.2", @@ -29,18 +29,18 @@ "devDependencies": { "eslint-config-prettier": "^9.0.0", "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jest": "^27.2.3", - "eslint-plugin-jsdoc": "^46.5.1", + "eslint-plugin-jest": "^27.4.0", + "eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-prettier": "^5.0.0", "express-basic-auth": "^1.2.1", "husky": "^8.0.3", - "jest": "^29.6.4", + "jest": "^29.7.0", "jsdom": "^22.1.0", "lint-staged": "^14.0.1", "lodash": "^4.17.21", - "playwright": "^1.37.1", + "playwright": "^1.38.1", "prettier": "^3.0.3", - "sinon": "^15.2.0", + "sinon": "^16.0.0", "stylelint": "^15.10.3", "stylelint-config-standard": "^34.0.0", "stylelint-prettier": "^4.0.2", @@ -50,7 +50,7 @@ "node": ">=18" }, "optionalDependencies": { - "electron": "^26.1.0" + "electron": "^26.2.2" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -159,31 +159,31 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", + "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.15.tgz", - "integrity": "sha512-PtZqMmgRrvj8ruoEOIwVA3yoF91O+Hgw9o7DAUTNBA6Mo2jpu31clx9a7Nz/9JznqetTR6zwfC4L3LAjKQXUwA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz", + "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", + "@babel/generator": "^7.23.0", "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.22.15", - "@babel/helpers": "^7.22.15", - "@babel/parser": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.0", + "@babel/parser": "^7.23.0", "@babel/template": "^7.22.15", - "@babel/traverse": "^7.22.15", - "@babel/types": "^7.22.15", - "convert-source-map": "^1.7.0", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", @@ -197,19 +197,13 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, "node_modules/@babel/generator": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", - "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.22.15", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -235,22 +229,22 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -281,16 +275,16 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.15.tgz", - "integrity": "sha512-l1UiX4UyHSFsYt17iQ3Se5pQQZZHa22zyIXURmvkmLCD4t/aU+dvNWHatKac/D9Vm9UES7nvIqHs4jZqKviUmQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.15" + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -342,9 +336,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz", - "integrity": "sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -360,26 +354,26 @@ } }, "node_modules/@babel/helpers": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", - "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz", + "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==", "dev": true, "dependencies": { "@babel/template": "^7.22.15", - "@babel/traverse": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", - "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, @@ -459,9 +453,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.15.tgz", - "integrity": "sha512-RWmQ/sklUN9BvGGpCDgSubhHWfAx24XDTDObup4ffvxaYsptOg2P3KG0j+1eWKLxpkX0j0uHxmpq2Z1SP/VhxA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -662,19 +656,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.15.tgz", - "integrity": "sha512-DdHPwvJY0sEeN4xJU5uRLmZjgMMDIvMPniLuYzUVXj/GGzysPl0/fwt44JBkyUIzGJPV8QgHMcQdQ34XFuKTYQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", + "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -692,13 +686,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.15.tgz", - "integrity": "sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.15", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -712,9 +706,9 @@ "dev": true }, "node_modules/@csstools/css-parser-algorithms": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.3.1.tgz", - "integrity": "sha512-xrvsmVUtefWMWQsGgFffqWSK03pZ1vfDki4IVIIUxxDKnGBzqNgv0A7SB1oXtVNEkcVO8xi1ZrTL29HhSu5kGA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.3.2.tgz", + "integrity": "sha512-sLYGdAdEY2x7TSw9FtmdaTrh2wFtRJO5VMbBrA8tEqEod7GEggFmxTSK9XqExib3yMuYNcvcTdCZIP6ukdjAIA==", "dev": true, "funding": [ { @@ -730,13 +724,13 @@ "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^2.2.0" + "@csstools/css-tokenizer": "^2.2.1" } }, "node_modules/@csstools/css-tokenizer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.2.0.tgz", - "integrity": "sha512-wErmsWCbsmig8sQKkM6pFhr/oPha1bHfvxsUY5CYSQxwyhA9Ulrs8EqCgClhg4Tgg2XapVstGqSVcz0xOYizZA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.2.1.tgz", + "integrity": "sha512-Zmsf2f/CaEPWEVgw29odOj+WEVoiJy9s9NOv5GgNY9mZ1CZ7394By6wONrONrTsnNDv6F9hR02nvFihrGVGHBg==", "dev": true, "funding": [ { @@ -753,9 +747,9 @@ } }, "node_modules/@csstools/media-query-list-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.4.tgz", - "integrity": "sha512-V/OUXYX91tAC1CDsiY+HotIcJR+vPtzrX8pCplCpT++i8ThZZsq5F5dzZh/bDM3WUOjrvC1ljed1oSJxMfjqhw==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.5.tgz", + "integrity": "sha512-IxVBdYzR8pYe89JiyXQuYk4aVVoCPhMJkz6ElRwlVysjwURTsTk/bmY/z4FfeRE+CRBMlykPwXEVUg8lThv7AQ==", "dev": true, "funding": [ { @@ -771,8 +765,8 @@ "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.3.1", - "@csstools/css-tokenizer": "^2.2.0" + "@csstools/css-parser-algorithms": "^2.3.2", + "@csstools/css-tokenizer": "^2.2.1" } }, "node_modules/@csstools/selector-specificity": { @@ -798,9 +792,9 @@ } }, "node_modules/@electron/get": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.2.tgz", - "integrity": "sha512-eFZVFoRXb3GFGd7Ak7W4+6jBl9wBtiZ4AaYOse97ej6mKj5tkyO0dUnUChs1IhJZtx1BENo4/p4WUTXpi6vT+g==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", "optional": true, "dependencies": { "debug": "^4.1.1", @@ -847,9 +841,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", - "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.2.tgz", + "integrity": "sha512-0MGxAVt1m/ZK+LTJp/j0qF7Hz97D9O/FH9Ms3ltnyIdDD57cbb1ACIQTkbHvNXtWDv5TPq7w5Kq56+cNukbo7g==", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -877,9 +871,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", - "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", + "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -1029,16 +1023,16 @@ } }, "node_modules/@jest/console": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", - "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -1046,15 +1040,15 @@ } }, "node_modules/@jest/core": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", - "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", - "@jest/reporters": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", @@ -1062,21 +1056,21 @@ "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.6.3", - "jest-config": "^29.6.4", - "jest-haste-map": "^29.6.4", - "jest-message-util": "^29.6.3", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-resolve-dependencies": "^29.6.4", - "jest-runner": "^29.6.4", - "jest-runtime": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "jest-watcher": "^29.6.4", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -1093,37 +1087,37 @@ } }, "node_modules/@jest/environment": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", - "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.6.4", + "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.3" + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", - "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, "dependencies": { - "expect": "^29.6.4", - "jest-snapshot": "^29.6.4" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", - "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, "dependencies": { "jest-get-type": "^29.6.3" @@ -1133,47 +1127,47 @@ } }, "node_modules/@jest/fake-timers": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", - "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.6.3", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3" + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", - "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/expect": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", "@jest/types": "^29.6.3", - "jest-mock": "^29.6.3" + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", - "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", @@ -1187,9 +1181,9 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", - "jest-worker": "^29.6.4", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -1234,12 +1228,12 @@ } }, "node_modules/@jest/test-result": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", - "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", + "@jest/console": "^29.7.0", "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" @@ -1249,14 +1243,14 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", - "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.4", + "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -1264,9 +1258,9 @@ } }, "node_modules/@jest/transform": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", - "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -1277,9 +1271,9 @@ "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -1513,9 +1507,9 @@ } }, "node_modules/@types/babel__core": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", - "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", + "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", "dev": true, "dependencies": { "@babel/parser": "^7.20.7", @@ -1526,18 +1520,18 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz", + "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==", "dev": true, "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz", + "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==", "dev": true, "dependencies": { "@babel/parser": "^7.1.0", @@ -1545,9 +1539,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", - "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz", + "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==", "dev": true, "dependencies": { "@babel/types": "^7.20.7" @@ -1579,18 +1573,18 @@ } }, "node_modules/@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.7.tgz", + "integrity": "sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz", + "integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw==", "optional": true }, "node_modules/@types/istanbul-lib-coverage": { @@ -1600,27 +1594,27 @@ "dev": true }, "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-gPQuzaPR5h/djlAv2apEG1HVOyj1IUs7GpfMZixU0/0KXT3pm64ylHuMUI1/Akh+sq/iikxg6Z2j+fcMDXaaTQ==", "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-kv43F9eb3Lhj+lr/Hn6OcLCs/sSM8bt+fIaP11rCYngfV6NVjzWXJ17owQtDQTL9tQ8WSLUrGsSJ6rJz0F1w1A==", "dev": true, "dependencies": { "@types/istanbul-lib-report": "*" } }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", "dev": true }, "node_modules/@types/json5": { @@ -1645,14 +1639,14 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.17.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.14.tgz", - "integrity": "sha512-ZE/5aB73CyGqgQULkLG87N9GnyGe5TcQjv34pwS8tfBs1IkCh0ASM69mydb2znqd6v0eX+9Ytvk6oQRqu8T1Vw==" + "version": "18.18.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.0.tgz", + "integrity": "sha512-3xA4X31gHT1F1l38ATDIL9GpRLdwVhnEFC8Uikv5ZLlXATwrCYyPq7ZWHxzxc3J/30SUiwiYT+bQe0/XvKlWbw==" }, "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.2.tgz", + "integrity": "sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==", "dev": true }, "node_modules/@types/responselike": { @@ -1665,9 +1659,9 @@ } }, "node_modules/@types/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", "dev": true }, "node_modules/@types/stack-utils": { @@ -1677,24 +1671,24 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "version": "17.0.25", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.25.tgz", + "integrity": "sha512-gy7iPgwnzNvxgAEi2bXOHWCVOG6f7xsprVJH4MjlAWeBmJ7vh/Y1kwMtUrs64ztf24zVIRCpr3n/z6gm9QIkgg==", "dev": true, "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==", "dev": true }, "node_modules/@types/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.1.tgz", + "integrity": "sha512-CHzgNU3qYBnp/O4S3yv2tXPlvMTq0YWSTVg2/JYLqWZGHwwgJGAwd00poay/11asPq8wLFwHzubyInqHIFmmiw==", "optional": true, "dependencies": { "@types/node": "*" @@ -2093,14 +2087,14 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -2111,14 +2105,14 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -2129,13 +2123,14 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", - "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", "dependencies": { "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.2", "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "get-intrinsic": "^1.2.1", "is-array-buffer": "^3.0.2", "is-shared-array-buffer": "^1.0.2" @@ -2192,12 +2187,12 @@ } }, "node_modules/babel-jest": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", - "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "dependencies": { - "@jest/transform": "^29.6.4", + "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", "babel-preset-jest": "^29.6.3", @@ -2430,9 +2425,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.21.11", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.11.tgz", + "integrity": "sha512-xn1UXOKUz7DjdGlg9RrUr0GGiWzI97UQJnugHtH0OLDfJB7jMgoIkYvRIEO1l9EeEERVqeqLYOcFBW9ldjypbQ==", "dev": true, "funding": [ { @@ -2449,10 +2444,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", + "caniuse-lite": "^1.0.30001538", + "electron-to-chromium": "^1.4.526", "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -2619,9 +2614,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001527", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001527.tgz", - "integrity": "sha512-YkJi7RwPgWtXVSgK4lG9AHH57nSzvvOp9MesgXmw4Q7n0C3H04L0foHqfxcmSAm5AcWb8dW9AYj2tR7/5GnddQ==", + "version": "1.0.30001539", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001539.tgz", + "integrity": "sha512-hfS5tE8bnNiNvEOEkm8HElUHroYwlqMMENEzELymy77+tJ6m+gA2krtHl5hxJaj71OlpC2cHZbdSMX1/YEqEkA==", "dev": true, "funding": [ { @@ -2944,9 +2939,9 @@ } }, "node_modules/cosmiconfig": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.4.tgz", - "integrity": "sha512-SF+2P8+o/PTV05rgsAjDzL4OFdVXAulSfC/L19VaeVT7+tpOOSscCt2QLxDZ+CLxF2WOiq6y1K5asvs8qUJT/Q==", + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, "dependencies": { "import-fresh": "^3.3.0", @@ -2969,6 +2964,27 @@ } } }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3337,6 +3353,19 @@ "node": ">=10" } }, + "node_modules/define-data-property": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", + "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/define-lazy-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", @@ -3350,10 +3379,11 @@ } }, "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dependencies": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, @@ -3519,9 +3549,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-26.1.0.tgz", - "integrity": "sha512-qEh19H09Pysn3ibms5nZ0haIh5pFoOd7/5Ww7gzmAwDQOulRi8Sa2naeueOyIb1GKpf+6L4ix3iceYRAuA5r5Q==", + "version": "26.2.2", + "resolved": "https://registry.npmjs.org/electron/-/electron-26.2.2.tgz", + "integrity": "sha512-Ihb3Zt4XYnHF52DYSq17ySkgFqJV4OT0VnfhUYZASAql7Vembz3VsAq7mB3OALBHXltAW34P8BxTIwTqZaMS3g==", "hasInstallScript": true, "optional": true, "dependencies": { @@ -3537,9 +3567,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.508", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.508.tgz", - "integrity": "sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg==", + "version": "1.4.530", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.530.tgz", + "integrity": "sha512-rsJ9O8SCI4etS8TBsXuRfHa2eZReJhnGf5MHZd3Vo05PukWHKXhk3VQGbHHnDLa8nZz9woPCpLCMQpLGgkGNRA==", "dev": true }, "node_modules/emittery": { @@ -3694,17 +3724,17 @@ } }, "node_modules/es-abstract": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", - "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", + "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", "dependencies": { "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.2", "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", + "function.prototype.name": "^1.1.6", "get-intrinsic": "^1.2.1", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", @@ -3720,23 +3750,23 @@ "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", + "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", "object-inspect": "^1.12.3", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "safe-array-concat": "^1.0.0", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", "typed-array-buffer": "^1.0.0", "typed-array-byte-length": "^1.0.0", "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.10" + "which-typed-array": "^1.1.11" }, "engines": { "node": ">= 0.4" @@ -3815,15 +3845,15 @@ } }, "node_modules/eslint": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", - "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", + "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.48.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint/js": "8.50.0", + "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.12.4", @@ -3978,9 +4008,9 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "27.2.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.2.3.tgz", - "integrity": "sha512-sRLlSCpICzWuje66Gl9zvdF6mwD5X86I4u55hJyFBsxYOsBCmT5+kSUjf+fkFWVMMgpzNEupjW8WzUqi83hJAQ==", + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.4.0.tgz", + "integrity": "sha512-ukVeKmMPAUA5SWjHenvyyXnirKfHKMdOsTZdn5tZx5EW05HGVQwBohigjFZGGj3zuv1cV6hc82FvWv6LdIbkgg==", "dev": true, "dependencies": { "@typescript-eslint/utils": "^5.10.0" @@ -4003,9 +4033,9 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "46.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.5.1.tgz", - "integrity": "sha512-CPbvKprmEuJYoxMj5g8gXfPqUGgcqMM6jpH06Kp4pn5Uy5MrPkFKzoD7UFp2E4RBzfXbJz1+TeuEivwFVMkXBg==", + "version": "46.8.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.8.2.tgz", + "integrity": "sha512-5TSnD018f3tUJNne4s4gDWQflbsgOycIKEUBoCLn6XtBMgNHxQFmV8vVxUtiPxAQq8lrX85OaSG/2gnctxw9uQ==", "dev": true, "dependencies": { "@es-joy/jsdoccomment": "~0.40.1", @@ -4239,16 +4269,16 @@ } }, "node_modules/expect": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", - "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.6.4", + "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3" + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -4529,14 +4559,14 @@ } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==" }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", "funding": [ { "type": "individual", @@ -4842,9 +4872,9 @@ } }, "node_modules/globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "version": "13.22.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz", + "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==", "dependencies": { "type-fest": "^0.20.2" }, @@ -5831,15 +5861,15 @@ } }, "node_modules/jest": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", - "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "dependencies": { - "@jest/core": "^29.6.4", + "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.6.4" + "jest-cli": "^29.7.0" }, "bin": { "jest": "bin/jest.js" @@ -5857,13 +5887,13 @@ } }, "node_modules/jest-changed-files": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", - "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "dependencies": { "execa": "^5.0.0", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "p-limit": "^3.1.0" }, "engines": { @@ -5871,28 +5901,28 @@ } }, "node_modules/jest-circus": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", - "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/expect": "^29.6.4", - "@jest/test-result": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-runtime": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -5902,22 +5932,21 @@ } }, "node_modules/jest-cli": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", - "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "dependencies": { - "@jest/core": "^29.6.4", - "@jest/test-result": "^29.6.4", + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "chalk": "^4.0.0", + "create-jest": "^29.7.0", "exit": "^0.1.2", - "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "prompts": "^2.0.1", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "yargs": "^17.3.1" }, "bin": { @@ -5936,31 +5965,31 @@ } }, "node_modules/jest-config": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", - "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.4", + "@jest/test-sequencer": "^29.7.0", "@jest/types": "^29.6.3", - "babel-jest": "^29.6.4", + "babel-jest": "^29.7.0", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.4", - "jest-environment-node": "^29.6.4", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", "jest-get-type": "^29.6.3", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-runner": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -5981,24 +6010,24 @@ } }, "node_modules/jest-diff": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", - "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-docblock": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", - "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "dependencies": { "detect-newline": "^3.0.0" @@ -6008,33 +6037,33 @@ } }, "node_modules/jest-each": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", - "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", "jest-get-type": "^29.6.3", - "jest-util": "^29.6.3", - "pretty-format": "^29.6.3" + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-environment-node": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", - "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3" + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -6050,9 +6079,9 @@ } }, "node_modules/jest-haste-map": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", - "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", @@ -6062,8 +6091,8 @@ "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", - "jest-util": "^29.6.3", - "jest-worker": "^29.6.4", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -6075,37 +6104,37 @@ } }, "node_modules/jest-leak-detector": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", - "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "dependencies": { "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", - "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.6.4", + "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-message-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", - "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", @@ -6114,7 +6143,7 @@ "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -6123,14 +6152,14 @@ } }, "node_modules/jest-mock": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", - "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.6.3" + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -6163,17 +6192,17 @@ } }, "node_modules/jest-resolve": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", - "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -6183,43 +6212,43 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", - "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, "dependencies": { "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.6.4" + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runner": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", - "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", - "@jest/environment": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.6.3", - "jest-environment-node": "^29.6.4", - "jest-haste-map": "^29.6.4", - "jest-leak-detector": "^29.6.3", - "jest-message-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-runtime": "^29.6.4", - "jest-util": "^29.6.3", - "jest-watcher": "^29.6.4", - "jest-worker": "^29.6.4", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -6228,17 +6257,17 @@ } }, "node_modules/jest-runtime": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", - "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", - "@jest/globals": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", @@ -6246,13 +6275,13 @@ "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-mock": "^29.6.3", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -6261,9 +6290,9 @@ } }, "node_modules/jest-snapshot": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", - "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -6271,20 +6300,20 @@ "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.6.4", + "expect": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.4", + "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "natural-compare": "^1.4.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "semver": "^7.5.3" }, "engines": { @@ -6325,9 +6354,9 @@ "dev": true }, "node_modules/jest-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", - "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", @@ -6342,9 +6371,9 @@ } }, "node_modules/jest-validate": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", - "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", @@ -6352,7 +6381,7 @@ "chalk": "^4.0.0", "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -6371,18 +6400,18 @@ } }, "node_modules/jest-watcher": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", - "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.4", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "string-length": "^4.0.1" }, "engines": { @@ -6390,13 +6419,13 @@ } }, "node_modules/jest-worker": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", - "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -7869,25 +7898,27 @@ } }, "node_modules/playwright": { - "version": "1.37.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.37.1.tgz", - "integrity": "sha512-bgUXRrQKhT48zHdxDYQTpf//0xDfDd5hLeEhjuSw8rXEGoT9YeElpfvs/izonTNY21IQZ7d3s22jLxYaAnubbQ==", + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz", + "integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==", "dev": true, - "hasInstallScript": true, "dependencies": { - "playwright-core": "1.37.1" + "playwright-core": "1.38.1" }, "bin": { "playwright": "cli.js" }, "engines": { "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.37.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz", - "integrity": "sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA==", + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz", + "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -7896,10 +7927,24 @@ "node": ">=16" } }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/postcss": { - "version": "8.4.29", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", - "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", + "version": "8.4.30", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz", + "integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==", "dev": true, "funding": [ { @@ -8001,9 +8046,9 @@ } }, "node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", @@ -8283,13 +8328,13 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" + "set-function-name": "^2.0.0" }, "engines": { "node": ">= 0.4" @@ -8331,9 +8376,9 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "version": "1.22.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", + "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", "dev": true, "dependencies": { "is-core-module": "^2.13.0", @@ -8520,12 +8565,12 @@ } }, "node_modules/safe-array-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", - "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", + "get-intrinsic": "^1.2.1", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -8574,9 +8619,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" }, "node_modules/saxes": { "version": "6.0.0", @@ -8698,6 +8743,19 @@ "node": ">= 0.8.0" } }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -8742,9 +8800,9 @@ "dev": true }, "node_modules/sinon": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", - "integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.0.0.tgz", + "integrity": "sha512-B8AaZZm9CT5pqe4l4uWJztfD/mOTa7dL8Qo0W4+s+t74xECOgSZDDQCBjNgIK3+n4kyxQrSTv2V5ul8K25qkiQ==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0", @@ -8922,15 +8980,15 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.15.tgz", + "integrity": "sha512-lpT8hSQp9jAKp9mhtBU4Xjon8LPGBvLIuBiSVhMEtmLecTh2mO0tlqrAMp47tBXzMr13NJMQ2lf7RpQGLJ3HsQ==", "dev": true }, "node_modules/sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "optional": true }, "node_modules/stack-utils": { @@ -9029,9 +9087,9 @@ } }, "node_modules/string.prototype.matchall": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.9.tgz", - "integrity": "sha512-6i5hL3MqG/K2G43mWXWgP+qizFW/QH/7kCNN13JrJS5q48FN5IKksLDscexKP3dnmB6cdm9jlNgAsWNLpSykmA==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -9040,6 +9098,7 @@ "has-symbols": "^1.0.3", "internal-slot": "^1.0.5", "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", "side-channel": "^1.0.4" }, "funding": { @@ -9047,13 +9106,13 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -9063,13 +9122,13 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9829,9 +9888,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "funding": [ { @@ -9891,9 +9950,13 @@ } }, "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -10127,9 +10190,9 @@ } }, "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", "dev": true, "engines": { "node": ">=10.0.0" diff --git a/package.json b/package.json index 1813e1e07c..4fc01f3cdf 100644 --- a/package.json +++ b/package.json @@ -51,31 +51,31 @@ "devDependencies": { "eslint-config-prettier": "^9.0.0", "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jest": "^27.2.3", - "eslint-plugin-jsdoc": "^46.5.1", + "eslint-plugin-jest": "^27.4.0", + "eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-prettier": "^5.0.0", "express-basic-auth": "^1.2.1", "husky": "^8.0.3", - "jest": "^29.6.4", + "jest": "^29.7.0", "jsdom": "^22.1.0", "lint-staged": "^14.0.1", "lodash": "^4.17.21", - "playwright": "^1.37.1", + "playwright": "^1.38.1", "prettier": "^3.0.3", - "sinon": "^15.2.0", + "sinon": "^16.0.0", "stylelint": "^15.10.3", "stylelint-config-standard": "^34.0.0", "stylelint-prettier": "^4.0.2", "suncalc": "^1.9.0" }, "optionalDependencies": { - "electron": "^26.1.0" + "electron": "^26.2.2" }, "dependencies": { "colors": "^1.4.0", "console-stamp": "^3.1.2", "envsub": "^4.1.0", - "eslint": "^8.48.0", + "eslint": "^8.50.0", "express": "^4.18.2", "express-ipfilter": "^1.3.1", "feedme": "^2.0.2", From 6b204cda25d438e1fcafbf4271c3a27ee4421473 Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Wed, 27 Sep 2023 23:37:10 +0200 Subject: [PATCH 162/204] calendar: add url to broadcast logging (#3211) minimal solution for #3110 --- CHANGELOG.md | 3 ++- modules/default/calendar/calendarfetcher.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5229ae4de..c18ff3cb5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). _This release is scheduled to be released on 2023-10-01._ -> ⚠️ This release needs nodejs version >= `v18`, older release have reached end of life and will not work! +> ⚠️ This release needs nodejs version >= `v18`, older releases have reached end of life and will not work! ### Added @@ -22,6 +22,7 @@ _This release is scheduled to be released on 2023-10-01._ - Added automatic client page reload when server was restarted by setting `reloadAfterServerRestart: true` in `config.js`, per default `false` (#3105) - Added eventClass option for customEvents on the default calendar - Added AnimateCSS integration in tests suite (#3206) +- Added improved logging for calendar (#3110) ### Removed diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js index 3ae9bcd315..51db30d7c7 100644 --- a/modules/default/calendar/calendarfetcher.js +++ b/modules/default/calendar/calendarfetcher.js @@ -107,7 +107,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn * Broadcast the existing events. */ this.broadcastEvents = function () { - Log.info(`Calendar-Fetcher: Broadcasting ${events.length} events.`); + Log.info(`Calendar-Fetcher: Broadcasting ${events.length} events from ${url}.`); eventsReceivedCallback(this); }; From 9566d6c9a0589093d2bbcb3b31d00fce3235b133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bugsounet=20-=20C=C3=A9dric?= Date: Wed, 27 Sep 2023 23:43:13 +0200 Subject: [PATCH 163/204] Add npm dependabot (#3210) Like mentioned [there](https://github.com/MichMich/MagicMirror/pull/3207#issuecomment-1736181753) I open an PR with npm dependabot (every monthly) It might be interesting to have an overview every month --------- Co-authored-by: Veeck --- .github/dependabot.yaml | 6 ++++++ CHANGELOG.md | 1 + 2 files changed, 7 insertions(+) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 38ae723d5e..a77711e223 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -5,3 +5,9 @@ updates: schedule: interval: "weekly" target-branch: "develop" + + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "monthly" + target-branch: "develop" diff --git a/CHANGELOG.md b/CHANGELOG.md index c18ff3cb5d..fa8493760e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ _This release is scheduled to be released on 2023-10-01._ - Added automatic client page reload when server was restarted by setting `reloadAfterServerRestart: true` in `config.js`, per default `false` (#3105) - Added eventClass option for customEvents on the default calendar - Added AnimateCSS integration in tests suite (#3206) +- Added npm dependabot [Reserved to developer] (#3210) - Added improved logging for calendar (#3110) ### Removed From 290b350856e80a5d4f783bb895d187943cca667b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bugsounet=20-=20C=C3=A9dric?= Date: Sat, 30 Sep 2023 15:18:56 +0200 Subject: [PATCH 164/204] Update last Dependencies before release and add dependabot to vendor/fonts directory (#3213) Last check before release: * update to electron: v2.26.4 * update eslint-plugins-jest: v27.4.2 * renew `package-lock.json` * add dependabot to `vendor` and `fonts` directory (monthly check) --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/dependabot.yaml | 12 +++++++ CHANGELOG.md | 2 ++ package-lock.json | 80 ++++++++++++++++++++--------------------- package.json | 4 +-- 4 files changed, 56 insertions(+), 42 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index a77711e223..9e699b7e65 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -11,3 +11,15 @@ updates: schedule: interval: "monthly" target-branch: "develop" + + - package-ecosystem: "npm" + directory: "/vendor" + schedule: + interval: "monthly" + target-branch: "develop" + + - package-ecosystem: "npm" + directory: "/fonts" + schedule: + interval: "monthly" + target-branch: "develop" diff --git a/CHANGELOG.md b/CHANGELOG.md index fa8493760e..2f31d0e1d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,8 @@ _This release is scheduled to be released on 2023-10-01._ - Cleaned up nunjuck templates - Replace `node-fetch` with internal fetch (#2649) and remove `digest-fetch` - Update the French translation according to the English file. +- Update dependabot incl. vendor/fonts (monthly check) +- Renew `package-lock.json` for release ### Fixed diff --git a/package-lock.json b/package-lock.json index ea1a2baa2a..1d55cf650b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "devDependencies": { "eslint-config-prettier": "^9.0.0", "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jest": "^27.4.0", + "eslint-plugin-jest": "^27.4.2", "eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-prettier": "^5.0.0", "express-basic-auth": "^1.2.1", @@ -50,7 +50,7 @@ "node": ">=18" }, "optionalDependencies": { - "electron": "^26.2.2" + "electron": "^26.2.4" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -841,9 +841,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.2.tgz", - "integrity": "sha512-0MGxAVt1m/ZK+LTJp/j0qF7Hz97D9O/FH9Ms3ltnyIdDD57cbb1ACIQTkbHvNXtWDv5TPq7w5Kq56+cNukbo7g==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.0.tgz", + "integrity": "sha512-zJmuCWj2VLBt4c25CfBIbMZLGLyhkvs7LznyVX5HfpzeocThgIj5XQK4L+g3U36mMcx8bPMhGyPpwCATamC4jQ==", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -1633,15 +1633,15 @@ } }, "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.3.tgz", + "integrity": "sha512-ZYFzrvyWUNhaPomn80dsMNgMeXxNWZBdkuG/hWlUvXvbdUH8ZERNBGXnU87McuGcWDsyzX2aChCv/SVN348k3A==", "dev": true }, "node_modules/@types/node": { - "version": "18.18.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.0.tgz", - "integrity": "sha512-3xA4X31gHT1F1l38ATDIL9GpRLdwVhnEFC8Uikv5ZLlXATwrCYyPq7ZWHxzxc3J/30SUiwiYT+bQe0/XvKlWbw==" + "version": "18.18.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.1.tgz", + "integrity": "sha512-3G42sxmm0fF2+Vtb9TJQpnjmP+uKlWvFa8KoEGquh4gqRmoUG/N0ufuhikw6HEsdG2G2oIKhog1GCTfz9v5NdQ==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.2", @@ -1650,9 +1650,9 @@ "dev": true }, "node_modules/@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.1.tgz", + "integrity": "sha512-TiGnitEDxj2X0j+98Eqk5lv/Cij8oHd32bU4D/Yw6AOq7vvTk0gSD2GPj0G/HkvhMoVsdlhYF4yqqlyPBTM6Sg==", "optional": true, "dependencies": { "@types/node": "*" @@ -1671,9 +1671,9 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.25", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.25.tgz", - "integrity": "sha512-gy7iPgwnzNvxgAEi2bXOHWCVOG6f7xsprVJH4MjlAWeBmJ7vh/Y1kwMtUrs64ztf24zVIRCpr3n/z6gm9QIkgg==", + "version": "17.0.26", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.26.tgz", + "integrity": "sha512-Y3vDy2X6zw/ZCumcwLpdhM5L7jmyGpmBCTYMHDLqT2IKVMYRRLdv6ZakA+wxhra6Z/3bwhNbNl9bDGXaFU+6rw==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -2425,9 +2425,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.11", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.11.tgz", - "integrity": "sha512-xn1UXOKUz7DjdGlg9RrUr0GGiWzI97UQJnugHtH0OLDfJB7jMgoIkYvRIEO1l9EeEERVqeqLYOcFBW9ldjypbQ==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", "dev": true, "funding": [ { @@ -2444,8 +2444,8 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001538", - "electron-to-chromium": "^1.4.526", + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", "node-releases": "^2.0.13", "update-browserslist-db": "^1.0.13" }, @@ -2614,9 +2614,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001539", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001539.tgz", - "integrity": "sha512-hfS5tE8bnNiNvEOEkm8HElUHroYwlqMMENEzELymy77+tJ6m+gA2krtHl5hxJaj71OlpC2cHZbdSMX1/YEqEkA==", + "version": "1.0.30001541", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001541.tgz", + "integrity": "sha512-bLOsqxDgTqUBkzxbNlSBt8annkDpQB9NdzdTbO2ooJ+eC/IQcvDspDc058g84ejCelF7vHUx57KIOjEecOHXaw==", "dev": true, "funding": [ { @@ -3549,9 +3549,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron": { - "version": "26.2.2", - "resolved": "https://registry.npmjs.org/electron/-/electron-26.2.2.tgz", - "integrity": "sha512-Ihb3Zt4XYnHF52DYSq17ySkgFqJV4OT0VnfhUYZASAql7Vembz3VsAq7mB3OALBHXltAW34P8BxTIwTqZaMS3g==", + "version": "26.2.4", + "resolved": "https://registry.npmjs.org/electron/-/electron-26.2.4.tgz", + "integrity": "sha512-weMUSMyDho5E0DPQ3breba3D96IxwNvtYHjMd/4/wNN3BdI5s3+0orNnPVGJFcLhSvKoxuKUqdVonUocBPwlQA==", "hasInstallScript": true, "optional": true, "dependencies": { @@ -3567,9 +3567,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.530", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.530.tgz", - "integrity": "sha512-rsJ9O8SCI4etS8TBsXuRfHa2eZReJhnGf5MHZd3Vo05PukWHKXhk3VQGbHHnDLa8nZz9woPCpLCMQpLGgkGNRA==", + "version": "1.4.537", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.537.tgz", + "integrity": "sha512-W1+g9qs9hviII0HAwOdehGYkr+zt7KKdmCcJcjH0mYg6oL8+ioT3Skjmt7BLoAQqXhjf40AXd+HlR4oAWMlXjA==", "dev": true }, "node_modules/emittery": { @@ -4008,9 +4008,9 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.4.0.tgz", - "integrity": "sha512-ukVeKmMPAUA5SWjHenvyyXnirKfHKMdOsTZdn5tZx5EW05HGVQwBohigjFZGGj3zuv1cV6hc82FvWv6LdIbkgg==", + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.4.2.tgz", + "integrity": "sha512-3Nfvv3wbq2+PZlRTf2oaAWXWwbdBejFRBR2O8tAO67o+P8zno+QGbcDYaAXODlreXVg+9gvWhKKmG2rgfb8GEg==", "dev": true, "dependencies": { "@typescript-eslint/utils": "^5.10.0" @@ -7942,9 +7942,9 @@ } }, "node_modules/postcss": { - "version": "8.4.30", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz", - "integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "dev": true, "funding": [ { @@ -8135,9 +8135,9 @@ } }, "node_modules/pure-rand": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.3.tgz", - "integrity": "sha512-KddyFewCsO0j3+np81IQ+SweXLDnDQTs5s67BOnrYmYe/yNmUhttQyGsYzy8yUnoljGAQ9sl38YB4vH8ur7Y+w==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", "dev": true, "funding": [ { diff --git a/package.json b/package.json index 4fc01f3cdf..056d07a3a1 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "devDependencies": { "eslint-config-prettier": "^9.0.0", "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jest": "^27.4.0", + "eslint-plugin-jest": "^27.4.2", "eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-prettier": "^5.0.0", "express-basic-auth": "^1.2.1", @@ -69,7 +69,7 @@ "suncalc": "^1.9.0" }, "optionalDependencies": { - "electron": "^26.2.2" + "electron": "^26.2.4" }, "dependencies": { "colors": "^1.4.0", From 6ea94e45125302fa2f597712e5cb3b57dca3df22 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Sun, 1 Oct 2023 20:01:14 +0200 Subject: [PATCH 165/204] Release v2.25.0 --- CHANGELOG.md | 6 ++++-- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f31d0e1d0..859bd590fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,11 @@ This project adheres to [Semantic Versioning](https://semver.org/). ❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror². -## [2.25.0] - Unreleased (`develop` branch) +## [2.25.0] - 2023-10-01 -_This release is scheduled to be released on 2023-10-01._ +Thanks to: @bugsounet, @dgoth, @dependabot, @kenzal, @Knapoc, @KristjanESPERANTO, @martingron, @NolanKingdon, @Paranoid93, @TeddyStarinvest and @Ybbet. + +Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not all) of the work on this release as project collaborators. This version would not be there without their effort. Thank you guys! You are awesome! > ⚠️ This release needs nodejs version >= `v18`, older releases have reached end of life and will not work! diff --git a/package-lock.json b/package-lock.json index 1d55cf650b..d9ca3b28bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "magicmirror", - "version": "2.25.0-develop", + "version": "2.25.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "magicmirror", - "version": "2.25.0-develop", + "version": "2.25.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 056d07a3a1..811d0c1c8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "magicmirror", - "version": "2.25.0-develop", + "version": "2.25.0", "description": "The open source modular smart mirror platform.", "main": "js/electron.js", "scripts": { From 66b29ec26e68061f71234a7f8c5e0ac8089215e1 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Sun, 1 Oct 2023 20:17:06 +0200 Subject: [PATCH 166/204] Prepare v2.26.0-develop --- CHANGELOG.md | 12 ++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 859bd590fb..dd7a6dc248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ This project adheres to [Semantic Versioning](https://semver.org/). ❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror². +## [2.26.0] - Unreleased (`develop` branch) + +_This release is scheduled to be released on 2024-01-01._ + +### Added + +### Removed + +### Updated + +### Fixed + ## [2.25.0] - 2023-10-01 Thanks to: @bugsounet, @dgoth, @dependabot, @kenzal, @Knapoc, @KristjanESPERANTO, @martingron, @NolanKingdon, @Paranoid93, @TeddyStarinvest and @Ybbet. diff --git a/package-lock.json b/package-lock.json index d9ca3b28bf..ca509ac59a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "magicmirror", - "version": "2.25.0", + "version": "2.26.0-develop", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "magicmirror", - "version": "2.25.0", + "version": "2.26.0-develop", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 811d0c1c8c..d9913871e2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "magicmirror", - "version": "2.25.0", + "version": "2.26.0-develop", "description": "The open source modular smart mirror platform.", "main": "js/electron.js", "scripts": { From b067711edea3864582d2bb5a325e050a7c6aa59d Mon Sep 17 00:00:00 2001 From: Teddy Date: Tue, 3 Oct 2023 12:24:50 +0200 Subject: [PATCH 167/204] Event class bugfix (#3218) Hello, Bugfix. :-) The "break" line did not allow the "eventClass" option to execute. --- CHANGELOG.md | 2 ++ modules/default/calendar/calendar.js | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd7a6dc248..049e6c803c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ _This release is scheduled to be released on 2024-01-01._ ### Fixed +- Fix the option eventClass on customEvents. + ## [2.25.0] - 2023-10-01 Thanks to: @bugsounet, @dgoth, @dependabot, @kenzal, @Knapoc, @KristjanESPERANTO, @martingron, @NolanKingdon, @Paranoid93, @TeddyStarinvest and @Ybbet. diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 681177d514..faff12e539 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -335,7 +335,6 @@ Module.register("calendar", { if (this.config.displaySymbol && this.config.coloredSymbol) { symbolWrapper.style.cssText = `color:${this.config.customEvents[ev].color}`; } - break; } if (typeof this.config.customEvents[ev].eventClass !== "undefined" && this.config.customEvents[ev].eventClass !== "") { eventWrapper.className += ` ${this.config.customEvents[ev].eventClass}`; From 5d2ddbd3dd68f500d9a84fd2b133ffccd6dadefc Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Fri, 13 Oct 2023 07:41:35 +0200 Subject: [PATCH 168/204] removed Codecov workflow (not working anymore, other workflow required) (#3230) see #3107 --- .github/workflows/codecov-test-suites.yaml | 35 ---------------------- CHANGELOG.md | 2 ++ README.md | 3 -- 3 files changed, 2 insertions(+), 38 deletions(-) delete mode 100644 .github/workflows/codecov-test-suites.yaml diff --git a/.github/workflows/codecov-test-suites.yaml b/.github/workflows/codecov-test-suites.yaml deleted file mode 100644 index 44244bac69..0000000000 --- a/.github/workflows/codecov-test-suites.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# This workflow runs the automated test and uploads the coverage results to codecov.io -# For more information see: https://github.com/codecov/codecov-action - -name: "Run Codecov Tests" - -on: - push: - branches: [master, develop] - pull_request: - branches: [master, develop] - -permissions: - contents: read - -jobs: - run-and-upload-coverage-report: - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: "Checkout code" - uses: actions/checkout@v4 - - name: "Install dependencies" - run: | - npm ci - - name: "Run coverage" - run: | - Xvfb :99 -screen 0 1024x768x16 & - export DISPLAY=:99 - touch css/custom.css - npm run test:coverage - - name: "Upload coverage results to codecov" - uses: codecov/codecov-action@v3 - with: - files: ./coverage/lcov.info - fail_ci_if_error: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 049e6c803c..c1349a6fb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ _This release is scheduled to be released on 2024-01-01._ ### Removed +- Removed Codecov workflow (not working anymore, other workflow required) (#3107) + ### Updated ### Fixed diff --git a/README.md b/README.md index a7b795a6a8..2437c6b66e 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,6 @@ GitHub Actions Build Status - - CodeCov Status - From a0b444d6c4f29cf794b7c2adf394fee8110b6f65 Mon Sep 17 00:00:00 2001 From: Veeck Date: Sun, 15 Oct 2023 13:25:44 +0200 Subject: [PATCH 169/204] Fix API version in yr weather provider call (#3223) Fixes #3227 and also - removes unused code - de-duplicates code fragments - fixes typos - inlines code - adds more weather util tests @martingron and @Justheretoreportanissue would you be so kind to check if I didnt mess anything up? --- CHANGELOG.md | 3 +- modules/default/weather/providers/yr.js | 52 ++--- package-lock.json | 189 +++++++++--------- package.json | 10 +- .../default/weather/weather_utils_spec.js | 7 + 5 files changed, 120 insertions(+), 141 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1349a6fb3..be0f9867d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ _This release is scheduled to be released on 2024-01-01._ ### Fixed - Fix the option eventClass on customEvents. +- Fix yr API version in locationforecast call (#3227) ## [2.25.0] - 2023-10-01 @@ -75,6 +76,7 @@ Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not al - Fix `Uncaught SyntaxError: Identifier 'getCorsUrl' has already been declared (at utils.js:1:1)` when using `clock` and `weather` module (#3204) - Fix overriding `config.js` when running tests (#3201) - Fix issue in weathergov provider with probability of precipitation not showing up on hourly or daily forecast +- Fix yr weather provider after changes in yr API (#3189) ## [2.24.0] - 2023-07-01 @@ -117,7 +119,6 @@ Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not al - Fix date not shown when clock in analog mode (#3100) - Fix envcanada today percentage-of-precipitation (#3106) - Fix updatenotification where no branch is checked out but e.g. a version tag (#3130) -- Fix yr weather provider after changes in yr API (#3189) ## [2.23.0] - 2023-04-04 diff --git a/modules/default/weather/providers/yr.js b/modules/default/weather/providers/yr.js index be876a6dc8..c82e8e6254 100644 --- a/modules/default/weather/providers/yr.js +++ b/modules/default/weather/providers/yr.js @@ -18,6 +18,8 @@ WeatherProvider.register("yr", { defaults: { useCorsProxy: true, apiBase: "https://api.met.no/weatherapi", + forecastApiVersion: "3.0", + sunriseApiVersion: "3.0", altitude: 0, currentForecastHours: 1 //1, 6 or 12 }, @@ -44,8 +46,7 @@ WeatherProvider.register("yr", { }, async getCurrentWeather() { - const getRequests = [this.getWeatherData(), this.getStellarData()]; - const [weatherData, stellarData] = await Promise.all(getRequests); + const [weatherData, stellarData] = await Promise.all([this.getWeatherData(), this.getStellarData()]); if (!stellarData) { Log.warn("No stellar data available."); } @@ -170,7 +171,7 @@ WeatherProvider.register("yr", { }); }, - getForecastUrl() { + getConfigOptions() { if (!this.config.lat) { Log.error("Latitude not provided."); throw new Error("Latitude not provided."); @@ -183,6 +184,11 @@ WeatherProvider.register("yr", { let lat = this.config.lat.toString(); let lon = this.config.lon.toString(); const altitude = this.config.altitude ?? 0; + return { lat, lon, altitude }; + }, + + getForecastUrl() { + let { lat, lon, altitude } = this.getConfigOptions(); if (lat.includes(".") && lat.split(".")[1].length > 4) { Log.warn("Latitude is too specific for weather data. Do not use more than four decimals. Trimming to maximum length."); @@ -195,18 +201,13 @@ WeatherProvider.register("yr", { lon = `${lonParts[0]}.${lonParts[1].substring(0, 4)}`; } - return `${this.config.apiBase}/locationforecast/2.0/complete?&altitude=${altitude}&lat=${lat}&lon=${lon}`; + return `${this.config.apiBase}/locationforecast/${this.config.forecastApiVersion}/complete?&altitude=${altitude}&lat=${lat}&lon=${lon}`; }, cacheWeatherData(weatherData) { localStorage.setItem("weatherData", JSON.stringify(weatherData)); }, - getAuthenticationString() { - if (!this.config.authenticationEmail) throw new Error("Authentication email not provided."); - return `${this.config.applicaitionName} ${this.config.authenticationEmail}`; - }, - getStellarData() { // If a user has several Yr-modules, for instance one current and one forecast, the API calls must be synchronized across classes. // This is to avoid multiple similar calls to the API. @@ -302,7 +303,7 @@ WeatherProvider.register("yr", { getStellarDataFromYr(date, days = 1) { const requestHeaders = [{ name: "Accept", value: "application/json" }]; - return this.fetchData(this.getStellarDatatUrl(date, days), "json", requestHeaders) + return this.fetchData(this.getStellarDataUrl(date, days), "json", requestHeaders) .then((data) => { Log.debug("Got stellar data from yr."); return data; @@ -313,19 +314,8 @@ WeatherProvider.register("yr", { }); }, - getStellarDatatUrl(date, days) { - if (!this.config.lat) { - Log.error("Latitude not provided."); - throw new Error("Latitude not provided."); - } - if (!this.config.lon) { - Log.error("Longitude not provided."); - throw new Error("Longitude not provided."); - } - - let lat = this.config.lat.toString(); - let lon = this.config.lon.toString(); - const altitude = this.config.altitude ?? 0; + getStellarDataUrl(date, days) { + let { lat, lon, altitude } = this.getConfigOptions(); if (lat.includes(".") && lat.split(".")[1].length > 4) { Log.warn("Latitude is too specific for stellar data. Do not use more than four decimals. Trimming to maximum length."); @@ -352,7 +342,7 @@ WeatherProvider.register("yr", { if (hours.length < 2) { hours = `0${hours}`; } - return `${this.config.apiBase}/sunrise/2.3/sun?lat=${lat}&lon=${lon}&date=${date}&offset=${utcOffsetPrefix}${hours}%3A${minutes}`; + return `${this.config.apiBase}/sunrise/${this.config.sunriseApiVersion}/sun?lat=${lat}&lon=${lon}&date=${date}&offset=${utcOffsetPrefix}${hours}%3A${minutes}`; }, cacheStellarData(data) { @@ -472,15 +462,6 @@ WeatherProvider.register("yr", { return weatherTypes.hasOwnProperty(weatherType) ? weatherTypes[weatherType] : null; }, - getStellarTimesFrom(stellarData, date) { - for (const time of stellarData.location.time) { - if (time.date === date) { - return time; - } - } - return undefined; - }, - getForecastForXHoursFrom(weather) { if (this.config.currentForecastHours === 1) { if (weather.next_1_hours) { @@ -522,8 +503,7 @@ WeatherProvider.register("yr", { }, async getWeatherForecast(type) { - const getRequests = [this.getWeatherData(), this.getStellarData()]; - const [weatherData, stellarData] = await Promise.all(getRequests); + const [weatherData, stellarData] = await Promise.all([this.getWeatherData(), this.getStellarData()]); if (!weatherData.properties.timeseries || !weatherData.properties.timeseries[0]) { Log.error("No weather data available."); return; @@ -573,7 +553,7 @@ WeatherProvider.register("yr", { return days; }, Object.create(null)); - Object.keys(days).forEach(function (time, index) { + Object.keys(days).forEach(function (time) { let minTemperature = undefined; let maxTemperature = undefined; diff --git a/package-lock.json b/package-lock.json index ca509ac59a..a656f52a6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "colors": "^1.4.0", "console-stamp": "^3.1.2", "envsub": "^4.1.0", - "eslint": "^8.50.0", + "eslint": "^8.51.0", "express": "^4.18.2", "express-ipfilter": "^1.3.1", "feedme": "^2.0.2", @@ -31,16 +31,16 @@ "eslint-plugin-import": "^2.28.1", "eslint-plugin-jest": "^27.4.2", "eslint-plugin-jsdoc": "^46.8.2", - "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-prettier": "^5.0.1", "express-basic-auth": "^1.2.1", "husky": "^8.0.3", "jest": "^29.7.0", "jsdom": "^22.1.0", "lint-staged": "^14.0.1", "lodash": "^4.17.21", - "playwright": "^1.38.1", + "playwright": "^1.39.0", "prettier": "^3.0.3", - "sinon": "^16.0.0", + "sinon": "^16.1.0", "stylelint": "^15.10.3", "stylelint-config-standard": "^34.0.0", "stylelint-prettier": "^4.0.2", @@ -50,7 +50,7 @@ "node": ">=18" }, "optionalDependencies": { - "electron": "^26.2.4" + "electron": "^26.4.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -159,18 +159,18 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", - "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", + "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz", - "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", + "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", @@ -178,10 +178,10 @@ "@babel/generator": "^7.23.0", "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-module-transforms": "^7.23.0", - "@babel/helpers": "^7.23.0", + "@babel/helpers": "^7.23.2", "@babel/parser": "^7.23.0", "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.0", + "@babel/traverse": "^7.23.2", "@babel/types": "^7.23.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -354,13 +354,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.23.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz", - "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", + "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", "dev": true, "dependencies": { "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.0", + "@babel/traverse": "^7.23.2", "@babel/types": "^7.23.0" }, "engines": { @@ -656,9 +656,9 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", - "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.22.13", @@ -841,9 +841,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.0.tgz", - "integrity": "sha512-zJmuCWj2VLBt4c25CfBIbMZLGLyhkvs7LznyVX5HfpzeocThgIj5XQK4L+g3U36mMcx8bPMhGyPpwCATamC4jQ==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", + "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -871,9 +871,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", - "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", + "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -1639,9 +1639,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.18.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.1.tgz", - "integrity": "sha512-3G42sxmm0fF2+Vtb9TJQpnjmP+uKlWvFa8KoEGquh4gqRmoUG/N0ufuhikw6HEsdG2G2oIKhog1GCTfz9v5NdQ==" + "version": "18.18.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.4.tgz", + "integrity": "sha512-t3rNFBgJRugIhackit2mVcLfF6IRc0JE4oeizPQL8Zrm8n2WY/0wOdpOPhdtG0V9Q2TlW/axbF1MJ6z+Yj/kKQ==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.2", @@ -1671,9 +1671,9 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.26", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.26.tgz", - "integrity": "sha512-Y3vDy2X6zw/ZCumcwLpdhM5L7jmyGpmBCTYMHDLqT2IKVMYRRLdv6ZakA+wxhra6Z/3bwhNbNl9bDGXaFU+6rw==", + "version": "17.0.28", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.28.tgz", + "integrity": "sha512-N3e3fkS86hNhtk6BEnc0rj3zcehaxx8QWhCROJkqpl5Zaoi7nAic3jH8q94jVD3zu5LGk+PUB6KAiDmimYOEQw==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -2614,9 +2614,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001541", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001541.tgz", - "integrity": "sha512-bLOsqxDgTqUBkzxbNlSBt8annkDpQB9NdzdTbO2ooJ+eC/IQcvDspDc058g84ejCelF7vHUx57KIOjEecOHXaw==", + "version": "1.0.30001547", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz", + "integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==", "dev": true, "funding": [ { @@ -2658,9 +2658,9 @@ } }, "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -3549,9 +3549,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron": { - "version": "26.2.4", - "resolved": "https://registry.npmjs.org/electron/-/electron-26.2.4.tgz", - "integrity": "sha512-weMUSMyDho5E0DPQ3breba3D96IxwNvtYHjMd/4/wNN3BdI5s3+0orNnPVGJFcLhSvKoxuKUqdVonUocBPwlQA==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-26.4.0.tgz", + "integrity": "sha512-FUEFwmIlflLxImRtTmDp8CWpH4KqlyAwga6vauaz6+882SmyC3bJRhgqOIT5s6rMbW25WezNiaqfKqHDJjz3pw==", "hasInstallScript": true, "optional": true, "dependencies": { @@ -3567,9 +3567,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.537", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.537.tgz", - "integrity": "sha512-W1+g9qs9hviII0HAwOdehGYkr+zt7KKdmCcJcjH0mYg6oL8+ioT3Skjmt7BLoAQqXhjf40AXd+HlR4oAWMlXjA==", + "version": "1.4.551", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.551.tgz", + "integrity": "sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==", "dev": true }, "node_modules/emittery": { @@ -3608,9 +3608,9 @@ } }, "node_modules/engine.io": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.2.tgz", - "integrity": "sha512-IXsMcGpw/xRfjra46sVZVHiSWo/nJ/3g1337q9KNXtS6YRzbW5yIzTCb9DjhrBe7r3GZQR0I4+nq+4ODk5g/cA==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.3.tgz", + "integrity": "sha512-IML/R4eG/pUS5w7OfcDE0jKrljWS9nwnEfsxWCIJF5eO6AHo6+Hlv+lQbdlAYsiJPHzUthLm1RUjnBzWOs45cw==", "dependencies": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", @@ -3845,14 +3845,14 @@ } }, "node_modules/eslint": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", - "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", + "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.50.0", + "@eslint/js": "8.51.0", "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -4089,9 +4089,9 @@ "dev": true }, "node_modules/eslint-plugin-prettier": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", - "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", @@ -4546,11 +4546,11 @@ } }, "node_modules/flat-cache": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", - "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", "dependencies": { - "flatted": "^3.2.7", + "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" }, @@ -4872,9 +4872,9 @@ } }, "node_modules/globals": { - "version": "13.22.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz", - "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==", + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dependencies": { "type-fest": "^0.20.2" }, @@ -5002,12 +5002,9 @@ } }, "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", "engines": { "node": ">= 0.4.0" } @@ -5771,9 +5768,9 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", - "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", "dev": true, "dependencies": { "@babel/core": "^7.12.3", @@ -6583,9 +6580,9 @@ "dev": true }, "node_modules/keyv": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", - "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dependencies": { "json-buffer": "3.0.1" } @@ -7898,12 +7895,12 @@ } }, "node_modules/playwright": { - "version": "1.38.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz", - "integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==", + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", + "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", "dev": true, "dependencies": { - "playwright-core": "1.38.1" + "playwright-core": "1.39.0" }, "bin": { "playwright": "cli.js" @@ -7916,9 +7913,9 @@ } }, "node_modules/playwright-core": { - "version": "1.38.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz", - "integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==", + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", + "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -8376,9 +8373,9 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", - "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { "is-core-module": "^2.13.0", @@ -8800,9 +8797,9 @@ "dev": true }, "node_modules/sinon": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.0.0.tgz", - "integrity": "sha512-B8AaZZm9CT5pqe4l4uWJztfD/mOTa7dL8Qo0W4+s+t74xECOgSZDDQCBjNgIK3+n4kyxQrSTv2V5ul8K25qkiQ==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.1.0.tgz", + "integrity": "sha512-ZSgzF0vwmoa8pq0GEynqfdnpEDyP1PkYmEChnkjW0Vyh8IDlyFEJ+fkMhCP0il6d5cJjPl2PUsnUSAuP5sttOQ==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0", @@ -8980,9 +8977,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.15.tgz", - "integrity": "sha512-lpT8hSQp9jAKp9mhtBU4Xjon8LPGBvLIuBiSVhMEtmLecTh2mO0tlqrAMp47tBXzMr13NJMQ2lf7RpQGLJ3HsQ==", + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", "dev": true }, "node_modules/sprintf-js": { @@ -9962,25 +9959,19 @@ } }, "node_modules/v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", + "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" + "convert-source-map": "^2.0.0" }, "engines": { "node": ">=10.12.0" } }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", diff --git a/package.json b/package.json index d9913871e2..181e6a9cf1 100644 --- a/package.json +++ b/package.json @@ -53,29 +53,29 @@ "eslint-plugin-import": "^2.28.1", "eslint-plugin-jest": "^27.4.2", "eslint-plugin-jsdoc": "^46.8.2", - "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-prettier": "^5.0.1", "express-basic-auth": "^1.2.1", "husky": "^8.0.3", "jest": "^29.7.0", "jsdom": "^22.1.0", "lint-staged": "^14.0.1", "lodash": "^4.17.21", - "playwright": "^1.38.1", + "playwright": "^1.39.0", "prettier": "^3.0.3", - "sinon": "^16.0.0", + "sinon": "^16.1.0", "stylelint": "^15.10.3", "stylelint-config-standard": "^34.0.0", "stylelint-prettier": "^4.0.2", "suncalc": "^1.9.0" }, "optionalDependencies": { - "electron": "^26.2.4" + "electron": "^26.4.0" }, "dependencies": { "colors": "^1.4.0", "console-stamp": "^3.1.2", "envsub": "^4.1.0", - "eslint": "^8.50.0", + "eslint": "^8.51.0", "express": "^4.18.2", "express-ipfilter": "^1.3.1", "feedme": "^2.0.2", diff --git a/tests/unit/modules/default/weather/weather_utils_spec.js b/tests/unit/modules/default/weather/weather_utils_spec.js index 8eae07cf1d..4af4efddd2 100644 --- a/tests/unit/modules/default/weather/weather_utils_spec.js +++ b/tests/unit/modules/default/weather/weather_utils_spec.js @@ -2,6 +2,12 @@ const weather = require("../../../../../modules/default/weather/weatherutils"); const WeatherUtils = require("../../../../../modules/default/weather/weatherutils"); describe("Weather utils tests", () => { + describe("windspeed conversion", () => { + it("should convert temp correctly from Celsius to Fahrenheit", () => { + expect(Math.round(WeatherUtils.convertTemp(10, "imperial"))).toBe(50); + }); + }); + describe("windspeed conversion", () => { it("should convert windspeed correctly from mps to beaufort", () => { expect(Math.round(WeatherUtils.convertWind(5, "beaufort"))).toBe(3); @@ -32,6 +38,7 @@ describe("Weather utils tests", () => { describe("wind direction conversion", () => { it("should convert wind direction correctly from cardinal to value", () => { expect(WeatherUtils.convertWindDirection("SSE")).toBe(157); + expect(WeatherUtils.convertWindDirection("XXX")).toBe(null); }); }); From 0e2da630d5b85468781bed6f923828868d08354a Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Thu, 19 Oct 2023 22:31:02 +0200 Subject: [PATCH 170/204] fix cloneObject() function to respect RegExp (#3240) fixes #3237 --- CHANGELOG.md | 1 + js/class.js | 4 ++++ tests/unit/classes/class_spec.js | 7 +++++++ 3 files changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index be0f9867d3..b9cc8a9ca1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ _This release is scheduled to be released on 2024-01-01._ - Fix the option eventClass on customEvents. - Fix yr API version in locationforecast call (#3227) +- Fix cloneObject() function to respect RegExp (#3237) ## [2.25.0] - 2023-10-01 diff --git a/js/class.js b/js/class.js index 4e76935942..cffd72d479 100644 --- a/js/class.js +++ b/js/class.js @@ -90,6 +90,10 @@ function cloneObject(obj) { return obj; } + if (obj.constructor.name === "RegExp") { + return new RegExp(obj); + } + const temp = obj.constructor(); // give temp the original obj's constructor for (const key in obj) { temp[key] = cloneObject(obj[key]); diff --git a/tests/unit/classes/class_spec.js b/tests/unit/classes/class_spec.js index 5122840f3f..402fcaef8e 100644 --- a/tests/unit/classes/class_spec.js +++ b/tests/unit/classes/class_spec.js @@ -49,6 +49,13 @@ describe("File js/class", () => { expect(obj).toBe(expected); }); + it("should clone regex", () => { + const expected = /.*Magic/; + const obj = clone(expected); + expect(obj).toEqual(expected); + expect(expected === obj).toBe(false); + }); + it("should clone undefined", () => { const expected = undefined; const obj = clone(expected); From 764ca3ac5c3241e4a0010bee2a31f115d8ae138d Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Thu, 19 Oct 2023 23:44:31 +0200 Subject: [PATCH 171/204] update electron to v27 (#3241) and - update other deps - update package-lock.json version to v3 in /vendor and /fonts --- CHANGELOG.md | 2 + fonts/package-lock.json | 22 +-- fonts/package.json | 2 +- package-lock.json | 317 +++++++++++++++++++++------------------ package.json | 8 +- vendor/package-lock.json | 62 +------- 6 files changed, 181 insertions(+), 232 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9cc8a9ca1..50ba147c95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ _This release is scheduled to be released on 2024-01-01._ ### Updated +- Update electron to v27 and update other dependencies + ### Fixed - Fix the option eventClass on customEvents. diff --git a/fonts/package-lock.json b/fonts/package-lock.json index 1324ec673c..d101b819c7 100644 --- a/fonts/package-lock.json +++ b/fonts/package-lock.json @@ -1,6 +1,6 @@ { "name": "magicmirror-fonts", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -8,7 +8,7 @@ "license": "MIT", "dependencies": { "@fontsource/roboto": "^5.0.8", - "@fontsource/roboto-condensed": "^5.0.8" + "@fontsource/roboto-condensed": "^5.0.12" } }, "node_modules/@fontsource/roboto": { @@ -17,21 +17,9 @@ "integrity": "sha512-XxPltXs5R31D6UZeLIV1td3wTXU3jzd3f2DLsXI8tytMGBkIsGcc9sIyiupRtA8y73HAhuSCeweOoBqf6DbWCA==" }, "node_modules/@fontsource/roboto-condensed": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@fontsource/roboto-condensed/-/roboto-condensed-5.0.8.tgz", - "integrity": "sha512-xAXYY+ys24OZ/eOfXJZILPu2xOB7c0ZruM4cd4TSzX3WGj4dZbXYwCEowLldKbZye6LTqiltpFLP/g/Ne0qGLg==" - } - }, - "dependencies": { - "@fontsource/roboto": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.0.8.tgz", - "integrity": "sha512-XxPltXs5R31D6UZeLIV1td3wTXU3jzd3f2DLsXI8tytMGBkIsGcc9sIyiupRtA8y73HAhuSCeweOoBqf6DbWCA==" - }, - "@fontsource/roboto-condensed": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@fontsource/roboto-condensed/-/roboto-condensed-5.0.8.tgz", - "integrity": "sha512-xAXYY+ys24OZ/eOfXJZILPu2xOB7c0ZruM4cd4TSzX3WGj4dZbXYwCEowLldKbZye6LTqiltpFLP/g/Ne0qGLg==" + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/@fontsource/roboto-condensed/-/roboto-condensed-5.0.12.tgz", + "integrity": "sha512-IdSGZUnXwiWrU9ID1zf+yIx3iB7WvUbShFxMqID2V50koKUF0qEoKZMtVUtU9m6LtQxZVriv24p0E7gNWfWH5w==" } } } diff --git a/fonts/package.json b/fonts/package.json index 0bb46d5715..f7b8534090 100644 --- a/fonts/package.json +++ b/fonts/package.json @@ -11,6 +11,6 @@ }, "dependencies": { "@fontsource/roboto": "^5.0.8", - "@fontsource/roboto-condensed": "^5.0.8" + "@fontsource/roboto-condensed": "^5.0.12" } } diff --git a/package-lock.json b/package-lock.json index a656f52a6c..3cf6cc868e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,12 +36,12 @@ "husky": "^8.0.3", "jest": "^29.7.0", "jsdom": "^22.1.0", - "lint-staged": "^14.0.1", + "lint-staged": "^15.0.2", "lodash": "^4.17.21", "playwright": "^1.39.0", "prettier": "^3.0.3", - "sinon": "^16.1.0", - "stylelint": "^15.10.3", + "sinon": "^16.1.3", + "stylelint": "^15.11.0", "stylelint-config-standard": "^34.0.0", "stylelint-prettier": "^4.0.2", "suncalc": "^1.9.0" @@ -50,7 +50,7 @@ "node": ">=18" }, "optionalDependencies": { - "electron": "^26.4.0" + "electron": "^27.0.1" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -879,11 +879,11 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", - "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.12.tgz", + "integrity": "sha512-NlGesA1usRNn6ctHCZ21M4/dKPgW9Nn1FypRdIKKgZOKzkVV4T1FlK5mBiLhHBCDmEbdQG0idrcXlbZfksJ+RA==", "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.0", "debug": "^4.1.1", "minimatch": "^3.0.5" }, @@ -904,9 +904,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.0.tgz", + "integrity": "sha512-9S9QrXY2K0L4AGDcSgTi9vgiCcG8VcBv4Mp7/1hDPYoswIy6Z6KO5blYto82BT8M0MZNRWmCFLpCs3HlpYGGdw==" }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -1339,9 +1339,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1507,9 +1507,9 @@ } }, "node_modules/@types/babel__core": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", - "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.3.tgz", + "integrity": "sha512-54fjTSeSHwfan8AyHWrKbfBWiEUrNTZsUwPTDSNaaP1QDQIZbeNUg3a59E9D+375MzUw/x1vx2/0F5LBz+AeYA==", "dev": true, "dependencies": { "@babel/parser": "^7.20.7", @@ -1520,18 +1520,18 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz", - "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.6.tgz", + "integrity": "sha512-66BXMKb/sUWbMdBNdMvajU7i/44RkrA3z/Yt1c7R5xejt8qh84iU54yUWCtm0QwGJlDcf/gg4zd/x4mpLAlb/w==", "dev": true, "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz", - "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==", + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.3.tgz", + "integrity": "sha512-ciwyCLeuRfxboZ4isgdNZi/tkt06m8Tw6uGbBSBgWrnnZGNXiEyM27xc/PjXGQLqlZ6ylbgHMnm7ccF9tCkOeQ==", "dev": true, "dependencies": { "@babel/parser": "^7.1.0", @@ -1539,9 +1539,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz", - "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==", + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.3.tgz", + "integrity": "sha512-Lsh766rGEFbaxMIDH7Qa+Yha8cMVI3qAK6CHt3OR0YfxOIn5Z54iHiyDRycHrBqeIiqGa20Kpsv1cavfBKkRSw==", "dev": true, "dependencies": { "@babel/types": "^7.20.7" @@ -1565,56 +1565,56 @@ "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" }, "node_modules/@types/cors": { - "version": "2.8.14", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.14.tgz", - "integrity": "sha512-RXHUvNWYICtbP6s18PnOCaqToK8y14DnLd75c6HfyKf228dxy7pHNOQkxPtvXKp/hINFMDjbYzsj63nnpPMSRQ==", + "version": "2.8.15", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.15.tgz", + "integrity": "sha512-n91JxbNLD8eQIuXDIChAN1tCKNWCEgpceU9b7ZMbFA+P+Q4yIeh80jizFLEvolRPc1ES0VdwFlGv+kJTSirogw==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/graceful-fs": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.7.tgz", - "integrity": "sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.8.tgz", + "integrity": "sha512-NhRH7YzWq8WiNKVavKPBmtLYZHxNY19Hh+az28O/phfp68CF45pMFud+ZzJ8ewnxnC5smIdF3dqFeiSUQ5I+pw==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/http-cache-semantics": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz", - "integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", + "integrity": "sha512-V46MYLFp08Wf2mmaBhvgjStM3tPa+2GAdy/iqoX+noX1//zje2x4XmrIU0cAwyClATsTmahbtoQ2EwP7I5WSiA==", "optional": true }, "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-zONci81DZYCZjiLe0r6equvZut0b+dBRPBN5kBDjsONnutYNtJMoWQ9uR2RkL1gLG9NMTzvf+29e5RFfPbeKhQ==", "dev": true }, "node_modules/@types/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-gPQuzaPR5h/djlAv2apEG1HVOyj1IUs7GpfMZixU0/0KXT3pm64ylHuMUI1/Akh+sq/iikxg6Z2j+fcMDXaaTQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.2.tgz", + "integrity": "sha512-8toY6FgdltSdONav1XtUHl4LN1yTmLza+EuDazb/fEmRNCwjyqNVIQWs2IfC74IqjHkREs/nQ2FWq5kZU9IC0w==", "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-kv43F9eb3Lhj+lr/Hn6OcLCs/sSM8bt+fIaP11rCYngfV6NVjzWXJ17owQtDQTL9tQ8WSLUrGsSJ6rJz0F1w1A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.3.tgz", + "integrity": "sha512-1nESsePMBlf0RPRffLZi5ujYh7IH1BWL4y9pr+Bn3cJBdxz+RTP8bUFljLz9HvzhhOSWKdyBZ4DIivdL6rvgZg==", "dev": true, "dependencies": { "@types/istanbul-lib-report": "*" } }, "node_modules/@types/json-schema": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", - "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, "node_modules/@types/json5": { @@ -1633,62 +1633,62 @@ } }, "node_modules/@types/minimist": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.3.tgz", - "integrity": "sha512-ZYFzrvyWUNhaPomn80dsMNgMeXxNWZBdkuG/hWlUvXvbdUH8ZERNBGXnU87McuGcWDsyzX2aChCv/SVN348k3A==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.4.tgz", + "integrity": "sha512-Kfe/D3hxHTusnPNRbycJE1N77WHDsdS4AjUYIzlDzhDrS47NrwuL3YW4VITxwR7KCVpzwgy4Rbj829KSSQmwXQ==", "dev": true }, "node_modules/@types/node": { - "version": "18.18.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.4.tgz", - "integrity": "sha512-t3rNFBgJRugIhackit2mVcLfF6IRc0JE4oeizPQL8Zrm8n2WY/0wOdpOPhdtG0V9Q2TlW/axbF1MJ6z+Yj/kKQ==" + "version": "18.18.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.6.tgz", + "integrity": "sha512-wf3Vz+jCmOQ2HV1YUJuCWdL64adYxumkrxtc+H1VUQlnQI04+5HtH+qZCOE21lBE7gIrt+CwX2Wv8Acrw5Ak6w==" }, "node_modules/@types/normalize-package-data": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.2.tgz", - "integrity": "sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.3.tgz", + "integrity": "sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==", "dev": true }, "node_modules/@types/responselike": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.1.tgz", - "integrity": "sha512-TiGnitEDxj2X0j+98Eqk5lv/Cij8oHd32bU4D/Yw6AOq7vvTk0gSD2GPj0G/HkvhMoVsdlhYF4yqqlyPBTM6Sg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha512-/4YQT5Kp6HxUDb4yhRkm0bJ7TbjvTddqX7PZ5hz6qV3pxSo72f/6YPRo+Mu2DU307tm9IioO69l7uAwn5XNcFA==", "optional": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", "dev": true }, "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.2.tgz", + "integrity": "sha512-g7CK9nHdwjK2n0ymT2CW698FuWJRIx+RP6embAzZ2Qi8/ilIrA1Imt2LVSeHUzKvpoi7BhmmQcXz95eS0f2JXw==", "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.28", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.28.tgz", - "integrity": "sha512-N3e3fkS86hNhtk6BEnc0rj3zcehaxx8QWhCROJkqpl5Zaoi7nAic3jH8q94jVD3zu5LGk+PUB6KAiDmimYOEQw==", + "version": "17.0.29", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.29.tgz", + "integrity": "sha512-nacjqA3ee9zRF/++a3FUY1suHTFKZeHba2n8WeDw9cCVdmzmHpIxyzOJBcpHvvEmS8E9KqWlSnWHUkOrkhWcvA==", "dev": true, "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==", + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.2.tgz", + "integrity": "sha512-5qcvofLPbfjmBfKaLfj/+f+Sbd6pN4zl7w7VSVI5uz7m9QZTuB2aZAa2uo1wHFBNN2x6g/SoTkXmd8mQnQF2Cw==", "dev": true }, "node_modules/@types/yauzl": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.1.tgz", - "integrity": "sha512-CHzgNU3qYBnp/O4S3yv2tXPlvMTq0YWSTVg2/JYLqWZGHwwgJGAwd00poay/11asPq8wLFwHzubyInqHIFmmiw==", + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.2.tgz", + "integrity": "sha512-Km7XAtUIduROw7QPgvcft0lIupeG8a8rdKL8RiSyKvlE7dYY31fEn41HVuQsRFDuROA8tA4K2UVL+WdfFmErBA==", "optional": true, "dependencies": { "@types/node": "*" @@ -2614,9 +2614,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001547", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz", - "integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==", + "version": "1.0.30001551", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001551.tgz", + "integrity": "sha512-vtBAez47BoGMMzlbYhfXrMV1kvRF2WP/lqiMuDu1Sb4EE4LKEgjopFDSRtZfdVnslNRpOqV/woE+Xgrwj6VQlg==", "dev": true, "funding": [ { @@ -2999,12 +2999,12 @@ } }, "node_modules/css-functions-list": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.0.tgz", - "integrity": "sha512-d/jBMPyYybkkLVypgtGv12R+pIFw4/f/IHtCTxWpZc8ofTYOPigIgmA6vu5rMHartZC+WuXhBUHfnyNUIQSYrg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.1.tgz", + "integrity": "sha512-Nj5YcaGgBtuUmn1D7oHqPW0c9iui7xsTsj5lIX8ZgevdfhmjFfKB3r8moHJtNJnctnYXJyYX5I1pp90HM4TPgQ==", "dev": true, "engines": { - "node": ">=12.22" + "node": ">=12 || >=16" } }, "node_modules/css-tree": { @@ -3354,9 +3354,9 @@ } }, "node_modules/define-data-property": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", - "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", "dependencies": { "get-intrinsic": "^1.2.1", "gopd": "^1.0.1", @@ -3549,9 +3549,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron": { - "version": "26.4.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-26.4.0.tgz", - "integrity": "sha512-FUEFwmIlflLxImRtTmDp8CWpH4KqlyAwga6vauaz6+882SmyC3bJRhgqOIT5s6rMbW25WezNiaqfKqHDJjz3pw==", + "version": "27.0.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-27.0.1.tgz", + "integrity": "sha512-AjDGgpf2thNxVXoNqEG+0GCUK4upAEa2B+IoM5Yk9YrOLd6uUOEMfGI9rhPtj+jC14iKOvBdefY2uAzcDC0qng==", "hasInstallScript": true, "optional": true, "dependencies": { @@ -3567,9 +3567,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.551", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.551.tgz", - "integrity": "sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==", + "version": "1.4.560", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.560.tgz", + "integrity": "sha512-HhJH/pWAxTaPZl7R3mJ6gCd8MfjQdil9RAWk84qHaLsmPTadydfAmq0a1x8kZtOGQ6pZrWhOYj5uZ8I0meZIgg==", "dev": true }, "node_modules/emittery": { @@ -4653,9 +4653,12 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { "version": "1.1.6", @@ -6606,9 +6609,9 @@ } }, "node_modules/known-css-properties": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.28.0.tgz", - "integrity": "sha512-9pSL5XB4J+ifHP0e0jmmC98OGC1nL8/JjS+fi6mnTlIf//yt/MfVLtKg7S6nCtj/8KTcWX7nRlY0XywoYY1ISQ==", + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.29.0.tgz", + "integrity": "sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==", "dev": true }, "node_modules/leac": { @@ -6656,27 +6659,27 @@ "dev": true }, "node_modules/lint-staged": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-14.0.1.tgz", - "integrity": "sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.0.2.tgz", + "integrity": "sha512-vnEy7pFTHyVuDmCAIFKR5QDO8XLVlPFQQyujQ/STOxe40ICWqJ6knS2wSJ/ffX/Lw0rz83luRDh+ET7toN+rOw==", "dev": true, "dependencies": { "chalk": "5.3.0", - "commander": "11.0.0", + "commander": "11.1.0", "debug": "4.3.4", - "execa": "7.2.0", + "execa": "8.0.1", "lilconfig": "2.1.0", - "listr2": "6.6.1", + "listr2": "7.0.2", "micromatch": "4.0.5", "pidtree": "0.6.0", "string-argv": "0.3.2", - "yaml": "2.3.1" + "yaml": "2.3.3" }, "bin": { "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=18.12.0" }, "funding": { "url": "https://opencollective.com/lint-staged" @@ -6695,56 +6698,56 @@ } }, "node_modules/lint-staged/node_modules/commander": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", - "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "engines": { "node": ">=16" } }, "node_modules/lint-staged/node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", - "signal-exit": "^3.0.7", + "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" }, "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + "node": ">=16.17" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, "node_modules/lint-staged/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lint-staged/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, "engines": { - "node": ">=14.18.0" + "node": ">=16.17.0" } }, "node_modules/lint-staged/node_modules/is-stream": { @@ -6813,6 +6816,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lint-staged/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/lint-staged/node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -6826,9 +6841,9 @@ } }, "node_modules/listr2": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", - "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-7.0.2.tgz", + "integrity": "sha512-rJysbR9GKIalhTbVL2tYbF2hVyDnrf7pFUZBwjPaMIdadYHmeT+EVi/Bu3qd7ETQPahTotg2WRCatXwRBW554g==", "dev": true, "dependencies": { "cli-truncate": "^3.1.0", @@ -6840,14 +6855,6 @@ }, "engines": { "node": ">=16.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } } }, "node_modules/locate-path": { @@ -7480,9 +7487,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.0.tgz", + "integrity": "sha512-HQ4J+ic8hKrgIt3mqk6cVOVrW2ozL4KdvHlqpBv9vDYWx9ysAgENAdvy4FoGF+KFdhR7nQTNm5J0ctAeOwn+3g==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8797,9 +8804,9 @@ "dev": true }, "node_modules/sinon": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.1.0.tgz", - "integrity": "sha512-ZSgzF0vwmoa8pq0GEynqfdnpEDyP1PkYmEChnkjW0Vyh8IDlyFEJ+fkMhCP0il6d5cJjPl2PUsnUSAuP5sttOQ==", + "version": "16.1.3", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.1.3.tgz", + "integrity": "sha512-mjnWWeyxcAf9nC0bXcPmiDut+oE8HYridTNzBbF98AYVLmWwGRp2ISEpyhYflG1ifILT+eNn3BmKUJPxjXUPlA==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0", @@ -9206,9 +9213,9 @@ "dev": true }, "node_modules/stylelint": { - "version": "15.10.3", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.10.3.tgz", - "integrity": "sha512-aBQMMxYvFzJJwkmg+BUUg3YfPyeuCuKo2f+LOw7yYbU8AZMblibwzp9OV4srHVeQldxvSFdz0/Xu8blq2AesiA==", + "version": "15.11.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.11.0.tgz", + "integrity": "sha512-78O4c6IswZ9TzpcIiQJIN49K3qNoXTM8zEJzhaTE/xRTCZswaovSEVIa/uwbOltZrk16X4jAxjaOhzz/hTm1Kw==", "dev": true, "dependencies": { "@csstools/css-parser-algorithms": "^2.3.1", @@ -9218,12 +9225,12 @@ "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^8.2.0", - "css-functions-list": "^3.2.0", + "css-functions-list": "^3.2.1", "css-tree": "^2.3.1", "debug": "^4.3.4", "fast-glob": "^3.3.1", "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^7.0.0", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", @@ -9232,13 +9239,13 @@ "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.28.0", + "known-css-properties": "^0.29.0", "mathml-tag-names": "^2.1.3", "meow": "^10.1.5", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.27", + "postcss": "^8.4.28", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", "postcss-selector-parser": "^6.0.13", @@ -9318,6 +9325,18 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-7.0.1.tgz", + "integrity": "sha512-uLfFktPmRetVCbHe5UPuekWrQ6hENufnA46qEGbfACkK5drjTTdQYUragRgMjHldcbYG+nslUerqMPjbBSHXjQ==", + "dev": true, + "dependencies": { + "flat-cache": "^3.1.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/stylelint/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -10232,9 +10251,9 @@ "dev": true }, "node_modules/yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz", + "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==", "dev": true, "engines": { "node": ">= 14" diff --git a/package.json b/package.json index 181e6a9cf1..20fb6cc9f4 100644 --- a/package.json +++ b/package.json @@ -58,18 +58,18 @@ "husky": "^8.0.3", "jest": "^29.7.0", "jsdom": "^22.1.0", - "lint-staged": "^14.0.1", + "lint-staged": "^15.0.2", "lodash": "^4.17.21", "playwright": "^1.39.0", "prettier": "^3.0.3", - "sinon": "^16.1.0", - "stylelint": "^15.10.3", + "sinon": "^16.1.3", + "stylelint": "^15.11.0", "stylelint-config-standard": "^34.0.0", "stylelint-prettier": "^4.0.2", "suncalc": "^1.9.0" }, "optionalDependencies": { - "electron": "^26.4.0" + "electron": "^27.0.1" }, "dependencies": { "colors": "^1.4.0", diff --git a/vendor/package-lock.json b/vendor/package-lock.json index 30ff41b062..a43b2cf36b 100644 --- a/vendor/package-lock.json +++ b/vendor/package-lock.json @@ -1,6 +1,6 @@ { "name": "magicmirror-vendors", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -101,65 +101,5 @@ "resolved": "https://registry.npmjs.org/weathericons/-/weathericons-2.1.0.tgz", "integrity": "sha512-V45viuyuQQOuoePTqzxvP/wBpYALWkD695fkFvqpn+BiMyo64fFlyDbP2A8umZyFyz1cXFPNw1pWqeaSaQqJlQ==" } - }, - "dependencies": { - "@fortawesome/fontawesome-free": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.2.tgz", - "integrity": "sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg==" - }, - "a-sync-waterfall": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", - "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" - }, - "animate.css": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/animate.css/-/animate.css-4.1.1.tgz", - "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==" - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" - }, - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" - }, - "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" - }, - "moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", - "requires": { - "moment": "^2.29.4" - } - }, - "nunjucks": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", - "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", - "requires": { - "a-sync-waterfall": "^1.0.0", - "asap": "^2.0.3", - "commander": "^5.1.0" - } - }, - "suncalc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.9.0.tgz", - "integrity": "sha512-vMJ8Byp1uIPoj+wb9c1AdK4jpkSKVAywgHX0lqY7zt6+EWRRC3Z+0Ucfjy/0yxTVO1hwwchZe4uoFNqrIC24+A==" - }, - "weathericons": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/weathericons/-/weathericons-2.1.0.tgz", - "integrity": "sha512-V45viuyuQQOuoePTqzxvP/wBpYALWkD695fkFvqpn+BiMyo64fFlyDbP2A8umZyFyz1cXFPNw1pWqeaSaQqJlQ==" - } } } From f46b226940159429bbb2549cdfda42d076d8e4c3 Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Fri, 20 Oct 2023 06:41:31 +0200 Subject: [PATCH 172/204] fix newsfeed module for feeds using "a10:updated" tag (#3242) solves #3238 --- CHANGELOG.md | 1 + modules/default/newsfeed/newsfeedfetcher.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50ba147c95..298825f02d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ _This release is scheduled to be released on 2024-01-01._ - Fix the option eventClass on customEvents. - Fix yr API version in locationforecast call (#3227) - Fix cloneObject() function to respect RegExp (#3237) +- Fix newsfeed module for feeds using "a10:updated" tag (#3238) ## [2.25.0] - 2023-10-01 diff --git a/modules/default/newsfeed/newsfeedfetcher.js b/modules/default/newsfeed/newsfeedfetcher.js index a0d871fac5..0f1b5d6c6d 100644 --- a/modules/default/newsfeed/newsfeedfetcher.js +++ b/modules/default/newsfeed/newsfeedfetcher.js @@ -48,7 +48,7 @@ const NewsfeedFetcher = function (url, reloadInterval, encoding, logFeedWarnings parser.on("item", (item) => { const title = item.title; let description = item.description || item.summary || item.content || ""; - const pubdate = item.pubdate || item.published || item.updated || item["dc:date"]; + const pubdate = item.pubdate || item.published || item.updated || item["dc:date"] || item["a10:updated"]; const url = item.url || item.link || ""; if (title && pubdate) { From bbc27f5ae2cbb69ac49e1d3c36d4d9cf2ba48b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bugsounet=20-=20C=C3=A9dric?= Date: Sat, 21 Oct 2023 19:41:17 +0200 Subject: [PATCH 173/204] Avoid fade out/in on updateDom when many calendars are used (#3220) related to #3185 * I have limited updated dom to one update -> `updateDom()` is activated by a timer which resets when a new event arrives (`CALENDAR_EVENTS`) * I have set no speed to self update. I think it's not necessary -> update it directly If somebody can test and tell me result In all case, I will patch my prod mirror for testing --- CHANGELOG.md | 1 + modules/default/calendar/calendar.js | 56 +++++++++++++++++++++------- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 298825f02d..b609b91dec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ _This release is scheduled to be released on 2024-01-01._ ### Fixed +- Avoid fade out/in on updateDom when many calendars are used - Fix the option eventClass on customEvents. - Fix yr API version in locationforecast call (#3227) - Fix cloneObject() function to respect RegExp (#3237) diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index faff12e539..c923ee50de 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -68,7 +68,8 @@ Module.register("calendar", { coloredSymbol: false, coloredBackground: false, limitDaysNeverSkip: false, - flipDateHeaderTitle: false + flipDateHeaderTitle: false, + updateOnFetch: true }, requiresVersion: "2.1.0", @@ -93,8 +94,6 @@ Module.register("calendar", { // Override start method. start: function () { - const ONE_MINUTE = 60 * 1000; - Log.info(`Starting module: ${this.name}`); if (this.config.colored) { @@ -117,6 +116,9 @@ Module.register("calendar", { // indicate no data available yet this.loaded = false; + // data holder of calendar url. Avoid fade out/in on updateDom (one for each calendar update) + this.calendarDisplayer = {}; + this.config.calendars.forEach((calendar) => { calendar.url = calendar.url.replace("webcal://", "http://"); @@ -153,16 +155,7 @@ Module.register("calendar", { this.addCalendar(calendar.url, calendar.auth, calendarConfig); }); - // Refresh the DOM every minute if needed: When using relative date format for events that start - // or end in less than an hour, the date shows minute granularity and we want to keep that accurate. - setTimeout( - () => { - setInterval(() => { - this.updateDom(1); - }, ONE_MINUTE); - }, - ONE_MINUTE - (new Date() % ONE_MINUTE) - ); + this.selfUpdate(); }, // Override socket notification handler. @@ -184,6 +177,18 @@ Module.register("calendar", { if (this.config.broadcastEvents) { this.broadcastEvents(); } + + if (!this.config.updateOnFetch) { + if (this.calendarDisplayer[payload.url] === undefined) { + // calendar will never displayed, so display it + this.updateDom(this.config.animationSpeed); + // set this calendar as displayed + this.calendarDisplayer[payload.url] = true; + } else { + Log.debug("[Calendar] DOM not updated waiting self update()"); + } + return; + } } } else if (notification === "CALENDAR_ERROR") { let error_message = this.translate(payload.error_type); @@ -859,5 +864,30 @@ Module.register("calendar", { } this.sendNotification("CALENDAR_EVENTS", eventList); + }, + + /** + * Refresh the DOM every minute if needed: When using relative date format for events that start + * or end in less than an hour, the date shows minute granularity and we want to keep that accurate. + * -- + * When updateOnFetch is not set, it will Avoid fade out/in on updateDom when many calendars are used + * and it's allow to refresh The DOM every minute with animation speed too + * (because updateDom is not set in CALENDAR_EVENTS for this case) + */ + selfUpdate: function () { + const ONE_MINUTE = 60 * 1000; + setTimeout( + () => { + setInterval(() => { + Log.debug("[Calendar] self update"); + if (this.config.updateOnFetch) { + this.updateDom(1); + } else { + this.updateDom(this.config.animationSpeed); + } + }, ONE_MINUTE); + }, + ONE_MINUTE - (new Date() % ONE_MINUTE) + ); } }); From 6815dfa02b72b60acc2c112f5df2ea94ef6db46d Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Sat, 21 Oct 2023 21:17:24 +0200 Subject: [PATCH 174/204] fix ISSUE_TEMPLATE (#3243) fix for #3167 --- .github/{ISSUE_TEMPLATE/custom.md => ISSUE_TEMPLATE.md} | 2 +- CHANGELOG.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) rename .github/{ISSUE_TEMPLATE/custom.md => ISSUE_TEMPLATE.md} (96%) diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE.md similarity index 96% rename from .github/ISSUE_TEMPLATE/custom.md rename to .github/ISSUE_TEMPLATE.md index 7cbf0076b4..f24edfc4b0 100644 --- a/.github/ISSUE_TEMPLATE/custom.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,6 +1,6 @@ Hello and thank you for opening an issue. -**Please make sure that you have read the following lines before submitting your Issue:** +**⚠️ Please make sure that you have read the following lines before submitting your Issue:** ## I'm not sure if this is a bug diff --git a/CHANGELOG.md b/CHANGELOG.md index b609b91dec..02ee53d263 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ _This release is scheduled to be released on 2024-01-01._ - Fix yr API version in locationforecast call (#3227) - Fix cloneObject() function to respect RegExp (#3237) - Fix newsfeed module for feeds using "a10:updated" tag (#3238) +- Fix issue template (3167) ## [2.25.0] - 2023-10-01 From f80889d95373191e3f9e5f157b7405562a4b67c0 Mon Sep 17 00:00:00 2001 From: Veeck Date: Mon, 23 Oct 2023 21:37:52 +0200 Subject: [PATCH 175/204] Update github test action (#3247) ... add node 21 to the tests and also update dependencies --- .github/workflows/automated-tests.yaml | 4 +- CHANGELOG.md | 8 +- fonts/package-lock.json | 8 +- fonts/package.json | 2 +- package-lock.json | 253 ++++++++++++++----------- package.json | 10 +- 6 files changed, 160 insertions(+), 125 deletions(-) diff --git a/.github/workflows/automated-tests.yaml b/.github/workflows/automated-tests.yaml index 959f43f5cd..fd3e1e898b 100644 --- a/.github/workflows/automated-tests.yaml +++ b/.github/workflows/automated-tests.yaml @@ -18,12 +18,12 @@ jobs: timeout-minutes: 30 strategy: matrix: - node-version: [18.x, 20.x] + node-version: [18.x, 20.x, 21.x] steps: - name: "Checkout code" uses: actions/checkout@v4 - name: "Use Node.js ${{ matrix.node-version }}" - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: "npm" diff --git a/CHANGELOG.md b/CHANGELOG.md index 02ee53d263..c683576a4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,13 +11,15 @@ _This release is scheduled to be released on 2024-01-01._ ### Added +- Added node 21 to the test matrix + ### Removed - Removed Codecov workflow (not working anymore, other workflow required) (#3107) ### Updated -- Update electron to v27 and update other dependencies +- Update electron to v27 and update other dependencies as well as github actions ### Fixed @@ -60,7 +62,7 @@ Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not al - Update issue template - Update dev/dependencies incl. electron to v26 - Replace pretty-quick by lint-staged () -- Update engine node >=18. v16 reached it's end of life. (#3170) +- Update engine node >=18. v16 reached its end of life. (#3170) - Update typescript definition for modules - Cleaned up nunjuck templates - Replace `node-fetch` with internal fetch (#2649) and remove `digest-fetch` @@ -73,7 +75,7 @@ Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not al - Fix engine check on npm install (#3135) - Fix undefined formatTime method in clock module (#3143) - Fix clientonly startup fails after async added (#3151) -- Fix electron width/heigth when using xrandr under bullseye +- Fix electron width/height when using xrandr under bullseye - Fix time issue with certain recurring events in calendar module - Fix ipWhiteList test (#3179) - Fix newsfeed: Convert HTML entities, codes and tag in description (#3191) diff --git a/fonts/package-lock.json b/fonts/package-lock.json index d101b819c7..212a858be9 100644 --- a/fonts/package-lock.json +++ b/fonts/package-lock.json @@ -8,7 +8,7 @@ "license": "MIT", "dependencies": { "@fontsource/roboto": "^5.0.8", - "@fontsource/roboto-condensed": "^5.0.12" + "@fontsource/roboto-condensed": "^5.0.13" } }, "node_modules/@fontsource/roboto": { @@ -17,9 +17,9 @@ "integrity": "sha512-XxPltXs5R31D6UZeLIV1td3wTXU3jzd3f2DLsXI8tytMGBkIsGcc9sIyiupRtA8y73HAhuSCeweOoBqf6DbWCA==" }, "node_modules/@fontsource/roboto-condensed": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/@fontsource/roboto-condensed/-/roboto-condensed-5.0.12.tgz", - "integrity": "sha512-IdSGZUnXwiWrU9ID1zf+yIx3iB7WvUbShFxMqID2V50koKUF0qEoKZMtVUtU9m6LtQxZVriv24p0E7gNWfWH5w==" + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/@fontsource/roboto-condensed/-/roboto-condensed-5.0.13.tgz", + "integrity": "sha512-ULmjcNq/DMNswAEgOsWLsjvjxxovuiLJSXPaMHE2FNYvjKiD2Gh5Hzh0QujuBdTxMK6Jn/iCKgfCAtWq5mGXrg==" } } } diff --git a/fonts/package.json b/fonts/package.json index f7b8534090..b39110f8f0 100644 --- a/fonts/package.json +++ b/fonts/package.json @@ -11,6 +11,6 @@ }, "dependencies": { "@fontsource/roboto": "^5.0.8", - "@fontsource/roboto-condensed": "^5.0.12" + "@fontsource/roboto-condensed": "^5.0.13" } } diff --git a/package-lock.json b/package-lock.json index 3cf6cc868e..5a7fb819b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "colors": "^1.4.0", "console-stamp": "^3.1.2", "envsub": "^4.1.0", - "eslint": "^8.51.0", + "eslint": "^8.52.0", "express": "^4.18.2", "express-ipfilter": "^1.3.1", "feedme": "^2.0.2", @@ -28,8 +28,8 @@ }, "devDependencies": { "eslint-config-prettier": "^9.0.0", - "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jest": "^27.4.2", + "eslint-plugin-import": "^2.29.0", + "eslint-plugin-jest": "^27.4.3", "eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-prettier": "^5.0.1", "express-basic-auth": "^1.2.1", @@ -40,7 +40,7 @@ "lodash": "^4.17.21", "playwright": "^1.39.0", "prettier": "^3.0.3", - "sinon": "^16.1.3", + "sinon": "^17.0.0", "stylelint": "^15.11.0", "stylelint-config-standard": "^34.0.0", "stylelint-prettier": "^4.0.2", @@ -50,7 +50,7 @@ "node": ">=18" }, "optionalDependencies": { - "electron": "^27.0.1" + "electron": "^27.0.2" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -871,19 +871,19 @@ } }, "node_modules/@eslint/js": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", - "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.12", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.12.tgz", - "integrity": "sha512-NlGesA1usRNn6ctHCZ21M4/dKPgW9Nn1FypRdIKKgZOKzkVV4T1FlK5mBiLhHBCDmEbdQG0idrcXlbZfksJ+RA==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.0", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" }, @@ -904,9 +904,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.0.tgz", - "integrity": "sha512-9S9QrXY2K0L4AGDcSgTi9vgiCcG8VcBv4Mp7/1hDPYoswIy6Z6KO5blYto82BT8M0MZNRWmCFLpCs3HlpYGGdw==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==" }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -1882,6 +1882,11 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + }, "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -2543,12 +2548,13 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2614,9 +2620,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001551", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001551.tgz", - "integrity": "sha512-vtBAez47BoGMMzlbYhfXrMV1kvRF2WP/lqiMuDu1Sb4EE4LKEgjopFDSRtZfdVnslNRpOqV/woE+Xgrwj6VQlg==", + "version": "1.0.30001553", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001553.tgz", + "integrity": "sha512-N0ttd6TrFfuqKNi+pMgWJTb9qrdJu4JSpgPFLe/lrD19ugC6fZgF0pUewRowDwzdDnb9V41mFcdlYgl/PyKf4A==", "dev": true, "funding": [ { @@ -3549,9 +3555,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron": { - "version": "27.0.1", - "resolved": "https://registry.npmjs.org/electron/-/electron-27.0.1.tgz", - "integrity": "sha512-AjDGgpf2thNxVXoNqEG+0GCUK4upAEa2B+IoM5Yk9YrOLd6uUOEMfGI9rhPtj+jC14iKOvBdefY2uAzcDC0qng==", + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/electron/-/electron-27.0.2.tgz", + "integrity": "sha512-4fbcHQ40ZDlqhr5Pamm+M5BF7ry2lGqjFTWTJ/mrBwuiPWu6xhV/RWgUhKBaLqKNfAaNl3eMxV3Jc82gv6JauQ==", "hasInstallScript": true, "optional": true, "dependencies": { @@ -3567,9 +3573,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.560", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.560.tgz", - "integrity": "sha512-HhJH/pWAxTaPZl7R3mJ6gCd8MfjQdil9RAWk84qHaLsmPTadydfAmq0a1x8kZtOGQ6pZrWhOYj5uZ8I0meZIgg==", + "version": "1.4.564", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.564.tgz", + "integrity": "sha512-bGAx9+teIzL5I4esQwCMtiXtb78Ysc8xOKTPOvmafbJZ4SQ40kDO1ym3yRcGSkfaBtV81fGgHOgPoe6DsmpmkA==", "dev": true }, "node_modules/emittery": { @@ -3724,25 +3730,25 @@ } }, "node_modules/es-abstract": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", - "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dependencies": { "array-buffer-byte-length": "^1.0.0", "arraybuffer.prototype.slice": "^1.0.2", "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.5", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.1", + "get-intrinsic": "^1.2.2", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", + "hasown": "^2.0.0", "internal-slot": "^1.0.5", "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", @@ -3752,7 +3758,7 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.5.1", @@ -3766,7 +3772,7 @@ "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -3776,25 +3782,25 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "node_modules/es-to-primitive": { @@ -3845,17 +3851,18 @@ } }, "node_modules/eslint": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", - "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.51.0", - "@humanwhocodes/config-array": "^0.11.11", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -3956,26 +3963,26 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.28.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz", - "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==", + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", + "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", "dev": true, "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.findlastindex": "^1.2.2", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", + "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.8.0", - "has": "^1.0.3", - "is-core-module": "^2.13.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.fromentries": "^2.0.6", - "object.groupby": "^1.0.0", - "object.values": "^1.1.6", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", "semver": "^6.3.1", "tsconfig-paths": "^3.14.2" }, @@ -4008,9 +4015,9 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "27.4.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.4.2.tgz", - "integrity": "sha512-3Nfvv3wbq2+PZlRTf2oaAWXWwbdBejFRBR2O8tAO67o+P8zno+QGbcDYaAXODlreXVg+9gvWhKKmG2rgfb8GEg==", + "version": "27.4.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.4.3.tgz", + "integrity": "sha512-7S6SmmsHsgIm06BAGCAxL+ABd9/IB3MWkz2pudj6Qqor2y1qQpWPfuFU4SG9pWj4xDjF0e+D7Llh5useuSzAZw==", "dev": true, "dependencies": { "@typescript-eslint/utils": "^5.10.0" @@ -4704,14 +4711,14 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5004,14 +5011,6 @@ "node": ">=6" } }, - "node_modules/has": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", - "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -5029,11 +5028,11 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dependencies": { - "get-intrinsic": "^1.1.1" + "get-intrinsic": "^1.2.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5075,6 +5074,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/helmet": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.0.0.tgz", @@ -5364,12 +5374,12 @@ "dev": true }, "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", "side-channel": "^1.0.4" }, "engines": { @@ -5469,12 +5479,12 @@ } }, "node_modules/is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7332,9 +7342,9 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "node_modules/nise": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", - "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.5.tgz", + "integrity": "sha512-VJuPIfUFaXNRzETTQEEItTOP8Y171ijr+JLq42wHes3DiryR8vT+1TXQW/Rx8JNUhyYYWyIvjXTU6dOhJcs9Nw==", "dev": true, "dependencies": { "@sinonjs/commons": "^2.0.0", @@ -7487,9 +7497,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.0.tgz", - "integrity": "sha512-HQ4J+ic8hKrgIt3mqk6cVOVrW2ozL4KdvHlqpBv9vDYWx9ysAgENAdvy4FoGF+KFdhR7nQTNm5J0ctAeOwn+3g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8747,6 +8757,20 @@ "node": ">= 0.8.0" } }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/set-function-name": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", @@ -8804,16 +8828,16 @@ "dev": true }, "node_modules/sinon": { - "version": "16.1.3", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.1.3.tgz", - "integrity": "sha512-mjnWWeyxcAf9nC0bXcPmiDut+oE8HYridTNzBbF98AYVLmWwGRp2ISEpyhYflG1ifILT+eNn3BmKUJPxjXUPlA==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.0.tgz", + "integrity": "sha512-p4lJiYKBoOEVUxxVIC9H1MM2znG1/c8gud++I2BauJA5hsz7hHsst35eurNWXTusBsIq66FzOQbZ/uMdpvbPIQ==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^10.3.0", + "@sinonjs/fake-timers": "^11.2.2", "@sinonjs/samsam": "^8.0.0", "diff": "^5.1.0", - "nise": "^5.1.4", + "nise": "^5.1.5", "supports-color": "^7.2.0" }, "funding": { @@ -8821,6 +8845,15 @@ "url": "https://opencollective.com/sinon" } }, + "node_modules/sinon/node_modules/@sinonjs/fake-timers": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, "node_modules/sinon/node_modules/diff": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", @@ -10103,12 +10136,12 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", "dependencies": { "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.4", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.0" diff --git a/package.json b/package.json index 20fb6cc9f4..22133965f6 100644 --- a/package.json +++ b/package.json @@ -50,8 +50,8 @@ "homepage": "https://magicmirror.builders", "devDependencies": { "eslint-config-prettier": "^9.0.0", - "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jest": "^27.4.2", + "eslint-plugin-import": "^2.29.0", + "eslint-plugin-jest": "^27.4.3", "eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-prettier": "^5.0.1", "express-basic-auth": "^1.2.1", @@ -62,20 +62,20 @@ "lodash": "^4.17.21", "playwright": "^1.39.0", "prettier": "^3.0.3", - "sinon": "^16.1.3", + "sinon": "^17.0.0", "stylelint": "^15.11.0", "stylelint-config-standard": "^34.0.0", "stylelint-prettier": "^4.0.2", "suncalc": "^1.9.0" }, "optionalDependencies": { - "electron": "^27.0.1" + "electron": "^27.0.2" }, "dependencies": { "colors": "^1.4.0", "console-stamp": "^3.1.2", "envsub": "^4.1.0", - "eslint": "^8.51.0", + "eslint": "^8.52.0", "express": "^4.18.2", "express-ipfilter": "^1.3.1", "feedme": "^2.0.2", From 04f0df269a6c8350e2ecdc0a509c0614bf862413 Mon Sep 17 00:00:00 2001 From: Veeck Date: Tue, 24 Oct 2023 00:46:25 +0200 Subject: [PATCH 176/204] Fix yr weather provider api version (#3248) Fixes #3227 once more --- CHANGELOG.md | 2 +- modules/default/weather/providers/yr.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c683576a4a..7d51dd18d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ _This release is scheduled to be released on 2024-01-01._ - Avoid fade out/in on updateDom when many calendars are used - Fix the option eventClass on customEvents. -- Fix yr API version in locationforecast call (#3227) +- Fix yr API version in locationforecast and sunrise call (#3227) - Fix cloneObject() function to respect RegExp (#3237) - Fix newsfeed module for feeds using "a10:updated" tag (#3238) - Fix issue template (3167) diff --git a/modules/default/weather/providers/yr.js b/modules/default/weather/providers/yr.js index c82e8e6254..150683c274 100644 --- a/modules/default/weather/providers/yr.js +++ b/modules/default/weather/providers/yr.js @@ -18,7 +18,7 @@ WeatherProvider.register("yr", { defaults: { useCorsProxy: true, apiBase: "https://api.met.no/weatherapi", - forecastApiVersion: "3.0", + forecastApiVersion: "2.0", sunriseApiVersion: "3.0", altitude: 0, currentForecastHours: 1 //1, 6 or 12 From a8d06ae74e39e7e692ddd6b0daadd3fe869e9cc8 Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Tue, 31 Oct 2023 19:37:34 +0100 Subject: [PATCH 177/204] hotfix for failing unit test (#3258) I know this isn't a real solution, but it's the quickest way to get it working again. A real solution must be found later. --- CHANGELOG.md | 3 ++- tests/unit/modules/default/utils_spec.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d51dd18d7..9819331484 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,8 @@ _This release is scheduled to be released on 2024-01-01._ - Fix yr API version in locationforecast and sunrise call (#3227) - Fix cloneObject() function to respect RegExp (#3237) - Fix newsfeed module for feeds using "a10:updated" tag (#3238) -- Fix issue template (3167) +- Fix issue template (#3167) +- Hotfix for failing unit test (#3254) ## [2.25.0] - 2023-10-01 diff --git a/tests/unit/modules/default/utils_spec.js b/tests/unit/modules/default/utils_spec.js index bc0baaf015..3f3ac7ef09 100644 --- a/tests/unit/modules/default/utils_spec.js +++ b/tests/unit/modules/default/utils_spec.js @@ -160,7 +160,7 @@ describe("Default modules utils tests", () => { }, time ) - ).toBe("07:13"); + ).toBe("08:13"); }); }); }); From 3a01acd3899caea6db42b68543dbd701cfa7f051 Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Wed, 1 Nov 2023 00:02:53 +0100 Subject: [PATCH 178/204] fix for failing unit test, use UTC as timezone (#3254) (#3259) fixes #3254 --- CHANGELOG.md | 2 +- tests/unit/helpers/global-setup.js | 2 +- tests/unit/modules/default/utils_spec.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9819331484..59b52e7145 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,7 @@ _This release is scheduled to be released on 2024-01-01._ - Fix cloneObject() function to respect RegExp (#3237) - Fix newsfeed module for feeds using "a10:updated" tag (#3238) - Fix issue template (#3167) -- Hotfix for failing unit test (#3254) +- Fix for failing unit test (#3254) ## [2.25.0] - 2023-10-01 diff --git a/tests/unit/helpers/global-setup.js b/tests/unit/helpers/global-setup.js index fc064b46bc..132a02bbd6 100644 --- a/tests/unit/helpers/global-setup.js +++ b/tests/unit/helpers/global-setup.js @@ -1,3 +1,3 @@ module.exports = async () => { - process.env.TZ = "Europe/Berlin"; + process.env.TZ = "UTC"; }; diff --git a/tests/unit/modules/default/utils_spec.js b/tests/unit/modules/default/utils_spec.js index 3f3ac7ef09..1d1a618550 100644 --- a/tests/unit/modules/default/utils_spec.js +++ b/tests/unit/modules/default/utils_spec.js @@ -160,7 +160,7 @@ describe("Default modules utils tests", () => { }, time ) - ).toBe("08:13"); + ).toBe("09:13"); }); }); }); From 2a6e2aacdc3ffb093c2027f475255ff31577aa5f Mon Sep 17 00:00:00 2001 From: kaennchenstruggle <54073894+kaennchenstruggle@users.noreply.github.com> Date: Wed, 1 Nov 2023 00:07:56 +0100 Subject: [PATCH 179/204] Calendar translate (#3249) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hello and thank you for wanting to contribute to the MagicMirror² project **Please make sure that you have followed these 4 rules before submitting your Pull Request:** > 1. Base your pull requests against the `develop` branch. DONE ;D > 2. Include these infos in the description: > - Does the pull request solve a **related** issue? NO > - What does the pull request accomplish? Use a list if needed. For calendar entries containing a year (e.g. DOB) in the title, the age can be calculated. Example before: ![grafik](https://github.com/MichMich/MagicMirror/assets/54073894/67ca65f4-24c3-46a8-bee8-0519e4bba3f5) after: ![grafik](https://github.com/MichMich/MagicMirror/assets/54073894/0b4af07d-d3d9-4644-a4a6-e8c402598208) Achieved by adding a new keyword `transform` to customEvents ``` customEvents: [ {keyword: 'Geburtstag', symbol: 'birthday-cake', color: 'Gold', transform: { search: '^([^\']*) \'(\\d{4})$' , replace: '$1 ($2.)', yearmatchgroup: 2}}, {keyword: 'in Hamburg', transform: { search: ' in Hamburg$' , replace: ''}} ], ``` and therewith obsoleting `titleReplace`; a backward compatibility part is already included. If `yearmatchgroup` is unset, behaviour is as in previous code (some additions to which RegExes are accepted, though) If `yearmatchgroup` is set, it is considered the RegEx match group id, which will be used for calculating the age. > - If it includes major visual changes please add screenshots. NO > 3. Please run `npm run lint:prettier` before submitting so that style issues are fixed. DONE > 4. Don't forget to add an entry about your changes to the CHANGELOG.md file. DONE > Thanks again and have a nice day! You too and if any questions, feel free to let me know. --------- Co-authored-by: veeck --- CHANGELOG.md | 2 ++ modules/default/calendar/calendar.js | 16 +++++++-- modules/default/calendar/calendarutils.js | 36 +++++++++++++------ .../default/calendar/calendar_utils_spec.js | 13 ++++--- 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59b52e7145..9a170d3316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,12 @@ _This release is scheduled to be released on 2024-01-01._ ### Added - Added node 21 to the test matrix +- Added transform object to calendar:customEvents ### Removed - Removed Codecov workflow (not working anymore, other workflow required) (#3107) +- Removed titleReplace from calendar, replaced + extended by customEvents (backward compatibility included) ### Updated diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index c923ee50de..287a424a73 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -155,6 +155,14 @@ Module.register("calendar", { this.addCalendar(calendar.url, calendar.auth, calendarConfig); }); + // for backward compatibility titleReplace + if (typeof this.config.titleReplace !== "undefined") { + Log.warn("Deprecation warning: Please consider upgrading your calendar titleReplace configuration to customEvents."); + for (const [titlesearchstr, titlereplacestr] of Object.entries(this.config.titleReplace)) { + this.config.customEvents.push({ keyword: ".*", transform: { search: titlesearchstr, replace: titlereplacestr } }); + } + } + this.selfUpdate(); }, @@ -326,11 +334,16 @@ Module.register("calendar", { } } - // Color events if custom color or eventClass are specified + var transformedTitle = event.title; + + // Color events if custom color or eventClass are specified, transform title if required if (this.config.customEvents.length > 0) { for (let ev in this.config.customEvents) { let needle = new RegExp(this.config.customEvents[ev].keyword, "gi"); if (needle.test(event.title)) { + if (typeof this.config.customEvents[ev].transform === "object") { + transformedTitle = CalendarUtils.titleTransform(transformedTitle, [this.config.customEvents[ev].transform]); + } if (typeof this.config.customEvents[ev].color !== "undefined" && this.config.customEvents[ev].color !== "") { // Respect parameter ColoredSymbolOnly also for custom events if (this.config.coloredText) { @@ -348,7 +361,6 @@ Module.register("calendar", { } } - const transformedTitle = CalendarUtils.titleTransform(event.title, this.config.titleReplace); titleWrapper.innerHTML = CalendarUtils.shorten(transformedTitle, this.config.maxTitleLength, this.config.wrapEvents, this.config.maxTitleLines) + repeatingCountTitle; const titleClass = this.titleClassForUrl(event.url); diff --git a/modules/default/calendar/calendarutils.js b/modules/default/calendar/calendarutils.js index e953b6352c..1bfdf15992 100644 --- a/modules/default/calendar/calendarutils.js +++ b/modules/default/calendar/calendarutils.js @@ -90,23 +90,39 @@ const CalendarUtils = { /** * Transforms the title of an event for usage. * Replaces parts of the text as defined in config.titleReplace. - * Shortens title based on config.maxTitleLength and config.wrapEvents * @param {string} title The title to transform. - * @param {object} titleReplace Pairs of strings to be replaced in the title + * @param {object} titleReplace object definition of parts to be replaced in the title + * object definition: + * search: {string,required} RegEx in format //x or simple string to be searched. For (birthday) year calcluation, the element matching the year must be in a RegEx group + * replace: {string,required} Replacement string, may contain match group references (latter is required for year calculation) + * yearmatchgroup: {number,optional} match group for year element * @returns {string} The transformed title. */ titleTransform: function (title, titleReplace) { let transformedTitle = title; - for (let needle in titleReplace) { - const replacement = titleReplace[needle]; + for (let tr in titleReplace) { + let transform = titleReplace[tr]; + if (typeof transform === "object") { + if (typeof transform.search !== "undefined" && transform.search !== "" && typeof transform.replace !== "undefined") { + let regParts = transform.search.match(/^\/(.+)\/([gim]*)$/); + let needle = new RegExp(transform.search, "g"); + if (regParts) { + // the parsed pattern is a regexp with flags. + needle = new RegExp(regParts[1], regParts[2]); + } - const regParts = needle.match(/^\/(.+)\/([gim]*)$/); - if (regParts) { - // the parsed pattern is a regexp. - needle = new RegExp(regParts[1], regParts[2]); + let replacement = transform.replace; + if (typeof transform.yearmatchgroup !== "undefined" && transform.yearmatchgroup !== "") { + const yearmatch = [...title.matchAll(needle)]; + if (yearmatch[0].length >= transform.yearmatchgroup + 1 && yearmatch[0][transform.yearmatchgroup] * 1 >= 1900) { + let calcage = new Date().getFullYear() - yearmatch[0][transform.yearmatchgroup] * 1; + let searchstr = `$${transform.yearmatchgroup}`; + replacement = replacement.replace(searchstr, calcage); + } + } + transformedTitle = transformedTitle.replace(needle, replacement); + } } - - transformedTitle = transformedTitle.replace(needle, replacement); } return transformedTitle; } diff --git a/tests/unit/modules/default/calendar/calendar_utils_spec.js b/tests/unit/modules/default/calendar/calendar_utils_spec.js index 7cf896c137..ff37299fe9 100644 --- a/tests/unit/modules/default/calendar/calendar_utils_spec.js +++ b/tests/unit/modules/default/calendar/calendar_utils_spec.js @@ -138,11 +138,16 @@ describe("Calendar utils tests", () => { describe("titleTransform and shorten combined", () => { it("should replace the birthday and wrap nicely", () => { - const transformedTitle = CalendarUtils.titleTransform("Michael Teeuw's birthday", { - "De verjaardag van ": "", - "'s birthday": "" - }); + const transformedTitle = CalendarUtils.titleTransform("Michael Teeuw's birthday", [{ search: "'s birthday", replace: "" }]); expect(CalendarUtils.shorten(transformedTitle, 10, true, 2)).toBe("Michael
Teeuw"); }); }); + + describe("titleTransform with yearmatchgroup", () => { + it("should replace the birthday and wrap nicely", () => { + const transformedTitle = CalendarUtils.titleTransform("Luciella '2000", [{ search: "^([^']*) '(\\d{4})$", replace: "$1 ($2.)", yearmatchgroup: 2 }]); + const expectedResult = `Luciella (${new Date().getFullYear() - 2000}.)`; + expect(transformedTitle).toBe(expectedResult); + }); + }); }); From fe882bf92aae5c552e5f6e57f9a0fbadb4951cb1 Mon Sep 17 00:00:00 2001 From: jkriegshauser Date: Tue, 31 Oct 2023 23:42:47 -0700 Subject: [PATCH 180/204] Fix issue #3250: Respect deleted (excluded) calendar events (#3251) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hello and thank you for wanting to contribute to the MagicMirror² project **Please make sure that you have followed these 4 rules before submitting your Pull Request:** > 1. Base your pull requests against the `develop` branch. > 2. Include these infos in the description: > > - Does the pull request solve a **related** issue? > - If so, can you reference the issue like this `Fixes #`? > - What does the pull request accomplish? Use a list if needed. > - If it includes major visual changes please add screenshots. > > 3. Please run `npm run lint:prettier` before submitting so that > style issues are fixed. > 4. Don't forget to add an entry about your changes to > the CHANGELOG.md file. **Note**: Sometimes the development moves very fast. It is highly recommended that you update your branch of `develop` before creating a pull request to send us your changes. This makes everyone's lives easier (including yours) and helps us out on the development team. Thanks again and have a nice day! --- .prettierignore | 3 ++ CHANGELOG.md | 1 + .../default/calendar/calendarfetcherutils.js | 12 ++++--- tests/configs/modules/calendar/exdate.js | 36 +++++++++++++++++++ tests/e2e/modules/calendar_spec.js | 11 ++++++ tests/mocks/calendar_test_exdate.ics | 34 ++++++++++++++++++ 6 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 tests/configs/modules/calendar/exdate.js create mode 100644 tests/mocks/calendar_test_exdate.ics diff --git a/.prettierignore b/.prettierignore index b92f216692..b115c7552e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,6 @@ +.eslintignore +.prettierignore /config /coverage package-lock.json +**.ics diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a170d3316..7be7d3d059 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ _This release is scheduled to be released on 2024-01-01._ - Fix newsfeed module for feeds using "a10:updated" tag (#3238) - Fix issue template (#3167) - Fix for failing unit test (#3254) +- Fix calendar events sometimes not respecting deleted events (#3250) ## [2.25.0] - 2023-10-01 diff --git a/modules/default/calendar/calendarfetcherutils.js b/modules/default/calendar/calendarfetcherutils.js index 34cc2578c6..3a90ae7ece 100644 --- a/modules/default/calendar/calendarfetcherutils.js +++ b/modules/default/calendar/calendarfetcherutils.js @@ -305,11 +305,6 @@ const CalendarFetcherUtils = { // Loop through the set of date entries to see which recurrences should be added to our event list. for (let d in dates) { let date = dates[d]; - // Remove the time information of each date by using its substring, using the following method: - // .toISOString().substring(0,10). - // since the date is given as ISOString with YYYY-MM-DDTHH:MM:SS.SSSZ - // (see https://momentjs.com/docs/#/displaying/as-iso-string/). - const dateKey = date.toISOString().substring(0, 10); let curEvent = event; let showRecurrence = true; @@ -402,6 +397,13 @@ const CalendarFetcherUtils = { let adjustDays = CalendarFetcherUtils.calculateTimezoneAdjustment(event, date); + // Remove the time information of each date by using its substring, using the following method: + // .toISOString().substring(0,10). + // since the date is given as ISOString with YYYY-MM-DDTHH:MM:SS.SSSZ + // (see https://momentjs.com/docs/#/displaying/as-iso-string/). + // This must be done after `date` is adjusted + const dateKey = date.toISOString().substring(0, 10); + // For each date that we're checking, it's possible that there is a recurrence override for that one day. if (curEvent.recurrences !== undefined && curEvent.recurrences[dateKey] !== undefined) { // We found an override, so for this recurrence, use a potentially different title, start date, and duration. diff --git a/tests/configs/modules/calendar/exdate.js b/tests/configs/modules/calendar/exdate.js new file mode 100644 index 0000000000..574b24fef8 --- /dev/null +++ b/tests/configs/modules/calendar/exdate.js @@ -0,0 +1,36 @@ +/* MagicMirror² Test calendar exdate + * + * By jkriegshauser + * MIT Licensed. + * + * NOTE: calendar_test_exdate.ics has exdate entries for the next 20 years, but without some + * way to set a debug date for tests, this test may become flaky on specific days (i.e. could + * not test easily on leap-years, the BYDAY specified in exdate, etc.) or when the 20 years + * elapses if this project is still in active development ;) + * See issue #3250 + */ +let config = { + timeFormat: 12, + + modules: [ + { + module: "calendar", + position: "bottom_bar", + config: { + maximumEntries: 100, + calendars: [ + { + maximumEntries: 100, + maximumNumberOfDays: 364, + url: "http://localhost:8080/tests/mocks/calendar_test_exdate.ics" + } + ] + } + } + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/e2e/modules/calendar_spec.js b/tests/e2e/modules/calendar_spec.js index 299bdf664e..398552884d 100644 --- a/tests/e2e/modules/calendar_spec.js +++ b/tests/e2e/modules/calendar_spec.js @@ -84,6 +84,17 @@ describe("Calendar module", () => { }); }); + describe("exdate check", () => { + beforeAll(async () => { + await helpers.startApplication("tests/configs/modules/calendar/exdate.js"); + await helpers.getDocument(); + }); + + it("should show the recurring event 51 times (excluded once) in a 364-day (inclusive) period", async () => { + await testElementLength(".calendar .event", 51); + }); + }); + describe("Events from multiple calendars", () => { beforeAll(async () => { await helpers.startApplication("tests/configs/modules/calendar/show-duplicates-in-calendar.js"); diff --git a/tests/mocks/calendar_test_exdate.ics b/tests/mocks/calendar_test_exdate.ics new file mode 100644 index 0000000000..8f1f8b9f32 --- /dev/null +++ b/tests/mocks/calendar_test_exdate.ics @@ -0,0 +1,34 @@ +BEGIN:VEVENT +DTSTART;TZID=UTC:20231025T181000 +DTEND;TZID=UTC:20231025T195000 +RRULE:FREQ=WEEKLY;BYDAY=WE +EXDATE;TZID=UTC:20231101T181000 +EXDATE;TZID=UTC:20241030T181000 +EXDATE;TZID=UTC:20251029T181000 +EXDATE;TZID=UTC:20261028T181000 +EXDATE;TZID=UTC:20271027T181000 +EXDATE;TZID=UTC:20281025T181000 +EXDATE;TZID=UTC:20291024T181000 +EXDATE;TZID=UTC:20301023T181000 +EXDATE;TZID=UTC:20311022T181000 +EXDATE;TZID=UTC:20321020T181000 +EXDATE;TZID=UTC:20331019T181000 +EXDATE;TZID=UTC:20341018T181000 +EXDATE;TZID=UTC:20351017T181000 +EXDATE;TZID=UTC:20361015T181000 +EXDATE;TZID=UTC:20371014T181000 +EXDATE;TZID=UTC:20381013T181000 +EXDATE;TZID=UTC:20391012T181000 +EXDATE;TZID=UTC:20401010T181000 +EXDATE;TZID=UTC:20411009T181000 +EXDATE;TZID=UTC:20421008T181000 +EXDATE;TZID=UTC:20431007T181000 +DTSTAMP:20231025T233434Z +UID:sdflbkasuhdb5fkauglkb@google.com +CREATED:20230306T193128Z +LAST-MODIFIED:20231024T222515Z +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:My Event +TRANSP:OPAQUE +END:VEVENT From 296df06c211d382392e7c98d0fd368e382a4a04f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 19:48:50 +0100 Subject: [PATCH 181/204] Bump eslint-plugin-jest from 27.4.3 to 27.6.0 (#3260) Bumps [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) from 27.4.3 to 27.6.0.
Release notes

Sourced from eslint-plugin-jest's releases.

v27.6.0

27.6.0 (2023-10-26)

Features

  • include plugin meta information for ESLint v9 (#1454) (4d57146)

v27.5.0

27.5.0 (2023-10-26)

Features

  • valid-title: allow ignoring tests with non-string titles (#1460) (ea89da9)
Changelog

Sourced from eslint-plugin-jest's changelog.

27.6.0 (2023-10-26)

Features

  • include plugin meta information for ESLint v9 (#1454) (4d57146)

27.5.0 (2023-10-26)

Features

  • valid-title: allow ignoring tests with non-string titles (#1460) (ea89da9)
Commits
  • 6dfbf15 chore(release): 27.6.0 [skip ci]
  • 4d57146 feat: include plugin meta information for ESLint v9 (#1454)
  • 55ad336 chore: update moduleResolution and module to node16 (#1455)
  • 9cc9592 chore: replace eslint-plugin-node with eslint-plugin-n (#1462)
  • 1d5bdd1 chore(release): 27.5.0 [skip ci]
  • ea89da9 feat(valid-title): allow ignoring tests with non-string titles (#1460)
  • f2af519 chore: run CI on Node 21 (#1461)
  • d8b10b4 chore: update permissions granted on CI
  • 4295882 chore(deps): lock file maintenance
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=eslint-plugin-jest&package-manager=npm_and_yarn&previous-version=27.4.3&new-version=27.6.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5a7fb819b8..e9c93e65d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "devDependencies": { "eslint-config-prettier": "^9.0.0", "eslint-plugin-import": "^2.29.0", - "eslint-plugin-jest": "^27.4.3", + "eslint-plugin-jest": "^27.6.0", "eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-prettier": "^5.0.1", "express-basic-auth": "^1.2.1", @@ -4015,9 +4015,9 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "27.4.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.4.3.tgz", - "integrity": "sha512-7S6SmmsHsgIm06BAGCAxL+ABd9/IB3MWkz2pudj6Qqor2y1qQpWPfuFU4SG9pWj4xDjF0e+D7Llh5useuSzAZw==", + "version": "27.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.6.0.tgz", + "integrity": "sha512-MTlusnnDMChbElsszJvrwD1dN3x6nZl//s4JD23BxB6MgR66TZlL064su24xEIS3VACfAoHV1vgyMgPw8nkdng==", "dev": true, "dependencies": { "@typescript-eslint/utils": "^5.10.0" diff --git a/package.json b/package.json index 22133965f6..e3ed2312a3 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "devDependencies": { "eslint-config-prettier": "^9.0.0", "eslint-plugin-import": "^2.29.0", - "eslint-plugin-jest": "^27.4.3", + "eslint-plugin-jest": "^27.6.0", "eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-prettier": "^5.0.1", "express-basic-auth": "^1.2.1", From b3001916096fd872eee7d85661a03c2f0b8ada29 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Tue, 7 Nov 2023 12:48:00 -0600 Subject: [PATCH 182/204] fix crash on rrule.between returned bad dates #3256 (#3257) Fixes: #3256 BUT.. the testcase is inconclusive.. as the code FAILS without the fix, BUT somehow RETURNS 0 entries.. in real life run the node helper fails, and all calendar processing stops. --- CHANGELOG.md | 1 + .../default/calendar/calendarfetcherutils.js | 6 +++- tests/configs/modules/calendar/bad_rrule.js | 27 +++++++++++++++++ tests/mocks/bad_rrule.ics | 20 +++++++++++++ .../calendar_fetcher_utils_bad_rrule.js | 29 +++++++++++++++++++ 5 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 tests/configs/modules/calendar/bad_rrule.js create mode 100644 tests/mocks/bad_rrule.ics create mode 100644 tests/unit/modules/default/calendar/calendar_fetcher_utils_bad_rrule.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 7be7d3d059..0af44ec6ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ _This release is scheduled to be released on 2024-01-01._ - Fix cloneObject() function to respect RegExp (#3237) - Fix newsfeed module for feeds using "a10:updated" tag (#3238) - Fix issue template (#3167) +- Fix #3256 filter out bad results from rrule.between - Fix for failing unit test (#3254) - Fix calendar events sometimes not respecting deleted events (#3250) diff --git a/modules/default/calendar/calendarfetcherutils.js b/modules/default/calendar/calendarfetcherutils.js index 3a90ae7ece..e10a7904e6 100644 --- a/modules/default/calendar/calendarfetcherutils.js +++ b/modules/default/calendar/calendarfetcherutils.js @@ -283,8 +283,12 @@ const CalendarFetcherUtils = { futureLocal = futureMoment.toDate(); // future } Log.debug(`Search for recurring events between: ${pastLocal} and ${futureLocal}`); - const dates = rule.between(pastLocal, futureLocal, true, limitFunction); + let dates = rule.between(pastLocal, futureLocal, true, limitFunction); Log.debug(`Title: ${event.summary}, with dates: ${JSON.stringify(dates)}`); + dates = dates.filter((d) => { + if (JSON.stringify(d) === "null") return false; + else return true; + }); // The "dates" array contains the set of dates within our desired date range range that are valid // for the recurrence rule. *However*, it's possible for us to have a specific recurrence that // had its date changed from outside the range to inside the range. For the time being, diff --git a/tests/configs/modules/calendar/bad_rrule.js b/tests/configs/modules/calendar/bad_rrule.js new file mode 100644 index 0000000000..5a89b5e616 --- /dev/null +++ b/tests/configs/modules/calendar/bad_rrule.js @@ -0,0 +1,27 @@ +/* MagicMirror² Test ics with out of date event causing bad return from rrule.between + * + * By Sam Detweiler + * MIT Licensed. + */ +let config = { + timeFormat: 12, + logLevel: ["INFO", "LOG", "WARN", "ERROR", "DEBUG"], + modules: [ + { + module: "calendar", + position: "bottom_bar", + config: { + calendars: [ + { + url: "http://localhost:8080/tests/mocks/bad_rrule.ics" + } + ] + } + } + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/mocks/bad_rrule.ics b/tests/mocks/bad_rrule.ics new file mode 100644 index 0000000000..2515fde6ec --- /dev/null +++ b/tests/mocks/bad_rrule.ics @@ -0,0 +1,20 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTAMP:20210413T203456Z +UID:E689AEB8C02C4E2CADD8C7D3D303CEAD0 +DTSTART;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210415T190000 +DTEND;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210415T210000 +CLASS:PUBLIC +LOCATION:albert heijn +SUMMARY:xxx xxxx +SEQUENCE:10 +RRULE:FREQ=DAILY;UNTIL=20210418T170000Z +EXDATE;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210417T190000 +EXDATE;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210416T190000 +EXDATE;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210415T190000 +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER;RELATED=START:-PT15M +END:VALARM +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/tests/unit/modules/default/calendar/calendar_fetcher_utils_bad_rrule.js b/tests/unit/modules/default/calendar/calendar_fetcher_utils_bad_rrule.js new file mode 100644 index 0000000000..bb4911a325 --- /dev/null +++ b/tests/unit/modules/default/calendar/calendar_fetcher_utils_bad_rrule.js @@ -0,0 +1,29 @@ +global.moment = require("moment-timezone"); + +const CalendarFetcherUtils = require("../../../../../modules/default/calendar/calendarfetcherutils"); + +describe("Calendar fetcher utils test", () => { + const defaultConfig = { + excludedEvents: [] + }; + + describe("filterEvents", () => { + it("no events, not crash", () => { + const minusOneHour = moment().subtract(1, "hours").toDate(); + const minusTwoHours = moment().subtract(2, "hours").toDate(); + const plusOneHour = moment().add(1, "hours").toDate(); + const plusTwoHours = moment().add(2, "hours").toDate(); + + const filteredEvents = CalendarFetcherUtils.filterEvents( + { + pastEvent: { type: "VEVENT", start: minusTwoHours, end: minusOneHour, summary: "pastEvent" }, + ongoingEvent: { type: "VEVENT", start: minusOneHour, end: plusOneHour, summary: "ongoingEvent" }, + upcomingEvent: { type: "VEVENT", start: plusOneHour, end: plusTwoHours, summary: "upcomingEvent" } + }, + defaultConfig + ); + + expect(filteredEvents.length).toEqual(0); + }); + }); +}); From 3fe5ad4b3dbd7879630443c63acc7f1e69302a0f Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Thu, 9 Nov 2023 22:57:15 +0100 Subject: [PATCH 183/204] remove failing unit test (#3265) see https://github.com/MichMich/MagicMirror/issues/3254#issuecomment-1800120812 --- CHANGELOG.md | 2 +- tests/unit/modules/default/utils_spec.js | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0af44ec6ee..8937e011d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ _This release is scheduled to be released on 2024-01-01._ - Removed Codecov workflow (not working anymore, other workflow required) (#3107) - Removed titleReplace from calendar, replaced + extended by customEvents (backward compatibility included) +- Removed failing unit test (#3254) ### Updated @@ -32,7 +33,6 @@ _This release is scheduled to be released on 2024-01-01._ - Fix newsfeed module for feeds using "a10:updated" tag (#3238) - Fix issue template (#3167) - Fix #3256 filter out bad results from rrule.between -- Fix for failing unit test (#3254) - Fix calendar events sometimes not respecting deleted events (#3250) ## [2.25.0] - 2023-10-01 diff --git a/tests/unit/modules/default/utils_spec.js b/tests/unit/modules/default/utils_spec.js index 1d1a618550..3430874c83 100644 --- a/tests/unit/modules/default/utils_spec.js +++ b/tests/unit/modules/default/utils_spec.js @@ -150,17 +150,5 @@ describe("Default modules utils tests", () => { ) ).toBe("1:13"); }); - - it("should convert correctly into another timezone", () => { - expect( - formatTime( - { - timeFormat: 24, - timezone: "America/Toronto" - }, - time - ) - ).toBe("09:13"); - }); }); }); From 203e8647d428f1313b757a2a37ace240322b1bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bugsounet=20-=20C=C3=A9dric?= Date: Fri, 10 Nov 2023 12:43:34 +0100 Subject: [PATCH 184/204] 3rd party modules updater for updatenotification (#3150) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added my (modified) updater main core into updatenotification default module Missing: callback display in MM² (i will code it after) new part of configuration added: ``` updates: [ // array of module update commands { // with embed npm script "MMM-Test": "npm run update" }, { // with "complex" process "MMM-OtherSample": "rm -rf package-lock.json && git reset --hard && git pull && npm install" }, { // with git pull && npm install "MMM-OtherSample2": "git pull && npm install" }, { // with a simple git pull "MMM-OtherSample3": "git pull" } ], updateTimeout: 2 * 60 * 1000, // max update duration updateAutorestart: false // autoRestart MM when update done ? ``` @khassel: i need your help I don't use docker, maybe you can help me for this: How can i check if MM² is running inside a docker ? (from MM² main core) Actually, I check if we use pm2 or not. I have to check if docker is used or not too last time you tell me: "you can't use updater with docker", so I want to check and deny any update if docker used --------- Co-authored-by: bugsounet --- CHANGELOG.md | 1 + .../default/updatenotification/node_helper.js | 22 +- .../updatenotification/update_helper.js | 224 ++++++++++++++++++ .../updatenotification/updatenotification.js | 38 ++- .../updatenotification/updatenotification.njk | 26 ++ package-lock.json | 6 + package.json | 1 + translations/de.json | 5 +- translations/en.json | 5 +- translations/fr.json | 5 +- 10 files changed, 323 insertions(+), 10 deletions(-) create mode 100644 modules/default/updatenotification/update_helper.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 8937e011d2..a9a7b915f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ _This release is scheduled to be released on 2024-01-01._ ### Added +- Added updatenotification Updater (for 3rd party modules) - Added node 21 to the test matrix - Added transform object to calendar:customEvents diff --git a/modules/default/updatenotification/node_helper.js b/modules/default/updatenotification/node_helper.js index f3b05fc84a..4061d2ad10 100644 --- a/modules/default/updatenotification/node_helper.js +++ b/modules/default/updatenotification/node_helper.js @@ -1,6 +1,7 @@ const NodeHelper = require("node_helper"); const defaultModules = require("../defaultmodules"); const GitHelper = require("./git_helper"); +const UpdateHelper = require("./update_helper"); const ONE_MINUTE = 60 * 1000; @@ -11,6 +12,7 @@ module.exports = NodeHelper.create({ updateProcessStarted: false, gitHelper: new GitHelper(), + updateHelper: null, async configureModules(modules) { for (const moduleName of modules) { @@ -28,6 +30,8 @@ module.exports = NodeHelper.create({ switch (notification) { case "CONFIG": this.config = payload; + this.updateHelper = new UpdateHelper(this.config); + await this.updateHelper.check_PM2_Process(); break; case "MODULES": // if this is the 1st time thru the update check process @@ -51,12 +55,22 @@ module.exports = NodeHelper.create({ const repos = await this.gitHelper.getRepos(); for (const repo of repos) { - this.sendSocketNotification("STATUS", repo); + this.sendSocketNotification("REPO_STATUS", repo); } - if (this.config.sendUpdatesNotifications) { - const updates = await this.gitHelper.checkUpdates(); - if (updates.length) this.sendSocketNotification("UPDATES", updates); + const updates = await this.gitHelper.checkUpdates(); + + if (this.config.sendUpdatesNotifications && updates.length) { + this.sendSocketNotification("UPDATES", updates); + } + + if (updates.length) { + const updateResult = await this.updateHelper.parse(updates); + for (const update of updateResult) { + if (update.inProgress) { + this.sendSocketNotification("UPDATE_STATUS", update); + } + } } this.scheduleNextFetch(this.config.updateInterval); diff --git a/modules/default/updatenotification/update_helper.js b/modules/default/updatenotification/update_helper.js new file mode 100644 index 0000000000..532472075c --- /dev/null +++ b/modules/default/updatenotification/update_helper.js @@ -0,0 +1,224 @@ +const Exec = require("child_process").exec; +const Spawn = require("child_process").spawn; +const commandExists = require("command-exists"); +const Log = require("logger"); + +/* class Updater + * Allow to self updating 3rd party modules from command defined in config + * + * [constructor] read value in config: + * updates: [ // array of modules update commands + * { + * : + * }, + * { + * ... + * } + * ], + * updateTimeout: 2 * 60 * 1000, // max update duration + * updateAutorestart: false // autoRestart MM when update done ? + * + * [main command]: parse(): + * parse if module update is needed + * --> Apply ONLY one update (first of the module list) + * --> auto-restart MagicMirror or wait manual restart by user + * return array with modules update state information for `updatenotification` module displayer information + * [ + * { + * name = , // name of the module + * updateCommand = , // update command (if found) + * inProgress = , // an update if in progress for this module + * error = , // an error if detected when updating + * updated = , // updated successfully + * needRestart = // manual restart of MagicMirror is required by user + * }, + * { + * ... + * } + * ] + */ + +class Updater { + constructor(config) { + this.updates = config.updates; + this.timeout = config.updateTimeout; + this.autoRestart = config.updateAutorestart; + this.moduleList = {}; + this.updating = false; + this.usePM2 = false; + this.PM2 = null; + this.version = global.version; + this.root_path = global.root_path; + Log.info("updatenotification: Updater Class Loaded!"); + } + + // [main command] parse if module update is needed + async parse(modules) { + var parser = modules.map(async (module) => { + if (this.moduleList[module.module] === undefined) { + this.moduleList[module.module] = {}; + this.moduleList[module.module].name = module.module; + this.moduleList[module.module].updateCommand = await this.applyCommand(module.module); + this.moduleList[module.module].inProgress = false; + this.moduleList[module.module].error = null; + this.moduleList[module.module].updated = false; + this.moduleList[module.module].needRestart = false; + } + if (!this.moduleList[module.module].inProgress) { + if (!this.updating) { + if (!this.moduleList[module.module].updateCommand) { + this.updating = false; + } else { + this.updating = true; + this.moduleList[module.module].inProgress = true; + Object.assign(this.moduleList[module.module], await this.updateProcess(this.moduleList[module.module])); + } + } + } + }); + + await Promise.all(parser); + let updater = Object.values(this.moduleList); + Log.debug("updatenotification Update Result:", updater); + return updater; + } + + // module updater with his proper command + // return object as result + //{ + // error: , // if error detected + // updated: , // if updated successfully + // needRestart: // if magicmirror restart required + //}; + updateProcess(module) { + let Result = { + error: false, + updated: false, + needRestart: false + }; + let Command = null; + const Path = `${this.root_path}/modules/`; + const modulePath = Path + module.name; + + if (module.updateCommand) { + Command = module.updateCommand; + } else { + Log.warn(`updatenotification: Update of ${module.name} is not supported.`); + return Result; + } + Log.info(`updatenotification: Updating ${module.name}...`); + + return new Promise((resolve) => { + Exec(Command, { cwd: modulePath, timeout: this.timeout }, (error, stdout, stderr) => { + if (error) { + Log.error(`updatenotification: exec error: ${error}`); + Result.error = true; + } else { + Log.info(`updatenotification: Update logs of ${module.name}: ${stdout}`); + Result.updated = true; + if (this.autoRestart) { + Log.info("updatenotification: Update done"); + setTimeout(() => this.restart(), 3000); + } else { + Log.info("updatenotification: Update done, don't forget to restart MagicMirror!"); + Result.needRestart = true; + } + } + resolve(Result); + }); + }); + } + + // restart rules (pm2 or npm start) + restart() { + if (this.usePM2) this.pm2Restart(); + else this.npmRestart(); + } + + // restart MagicMiror with "pm2" + pm2Restart() { + Log.info("updatenotification: PM2 will restarting MagicMirror..."); + Exec(`pm2 restart ${this.PM2}`, (err, std, sde) => { + if (err) { + Log.error("updatenotification:[PM2] restart Error", err); + } + }); + } + + // restart MagicMiror with "npm start" + npmRestart() { + Log.info("updatenotification: Restarting MagicMirror..."); + const out = process.stdout; + const err = process.stderr; + const subprocess = Spawn("npm start", { cwd: this.root_path, shell: true, detached: true, stdio: ["ignore", out, err] }); + subprocess.unref(); + process.exit(); + } + + // Check using pm2 + check_PM2_Process() { + Log.info("updatenotification: Checking PM2 using..."); + return new Promise((resolve) => { + commandExists("pm2") + .then(async () => { + var PM2_List = await this.PM2_GetList(); + if (!PM2_List) { + Log.error("updatenotification: [PM2] Can't get process List!"); + this.usePM2 = false; + resolve(false); + return; + } + PM2_List.forEach((pm) => { + if (pm.pm2_env.version === this.version && pm.pm2_env.status === "online" && pm.pm2_env.PWD.includes(this.root_path)) { + this.PM2 = pm.name; + this.usePM2 = true; + Log.info("updatenotification: You are using pm2 with", this.PM2); + resolve(true); + } + }); + if (!this.PM2) { + Log.info("updatenotification: You are not using pm2"); + this.usePM2 = false; + resolve(false); + } + }) + .catch(() => { + Log.info("updatenotification: You are not using pm2"); + this.usePM2 = false; + resolve(false); + }); + }); + } + + // Get the list of pm2 process + PM2_GetList() { + return new Promise((resolve) => { + Exec("pm2 jlist", (err, std, sde) => { + if (err) { + resolve(null); + return; + } + let result = JSON.parse(std); + resolve(result); + }); + }); + } + + // check if module is MagicMirror + isMagicMirror(module) { + if (module === "MagicMirror") return true; + return false; + } + + // search update module command + applyCommand(module) { + if (this.isMagicMirror(module.module)) return null; + let command = null; + this.updates.forEach((updater) => { + if (updater[module]) command = updater[module]; + }); + return command; + } +} + +module.exports = Updater; diff --git a/modules/default/updatenotification/updatenotification.js b/modules/default/updatenotification/updatenotification.js index 73327ec844..7cfc1ceae8 100644 --- a/modules/default/updatenotification/updatenotification.js +++ b/modules/default/updatenotification/updatenotification.js @@ -9,11 +9,16 @@ Module.register("updatenotification", { updateInterval: 10 * 60 * 1000, // every 10 minutes refreshInterval: 24 * 60 * 60 * 1000, // one day ignoreModules: [], - sendUpdatesNotifications: false + sendUpdatesNotifications: false, + updates: [], + updateTimeout: 2 * 60 * 1000, // max update duration + updateAutorestart: false // autoRestart MM when update done ? }, suspended: false, moduleList: {}, + needRestart: false, + updates: {}, start() { Log.info(`Starting module: ${this.name}`); @@ -47,12 +52,15 @@ Module.register("updatenotification", { socketNotificationReceived(notification, payload) { switch (notification) { - case "STATUS": + case "REPO_STATUS": this.updateUI(payload); break; case "UPDATES": this.sendNotification("UPDATES", payload); break; + case "UPDATE_STATUS": + this.updatesNotifier(payload); + break; } }, @@ -65,7 +73,7 @@ Module.register("updatenotification", { }, getTemplateData() { - return { moduleList: this.moduleList, suspended: this.suspended }; + return { moduleList: this.moduleList, updatesList: this.updates, suspended: this.suspended, needRestart: this.needRestart }; }, updateUI(payload) { @@ -96,5 +104,29 @@ Module.register("updatenotification", { const remoteRef = status.tracking.replace(/.*\//, ""); return `${text}`; }); + }, + + updatesNotifier(payload, done = true) { + if (this.updates[payload.name] === undefined) { + this.updates[payload.name] = { + name: payload.name, + done: done + }; + + if (payload.error) { + this.sendSocketNotification("UPDATE_ERROR", payload.name); + this.updates[payload.name].done = false; + } else { + if (payload.updated) { + delete this.moduleList[payload.name]; + this.updates[payload.name].done = true; + } + if (payload.needRestart) { + this.needRestart = true; + } + } + + this.updateDom(2); + } } }); diff --git a/modules/default/updatenotification/updatenotification.njk b/modules/default/updatenotification/updatenotification.njk index 415688d6e1..fbc43f22fb 100644 --- a/modules/default/updatenotification/updatenotification.njk +++ b/modules/default/updatenotification/updatenotification.njk @@ -1,4 +1,13 @@ {% if not suspended %} + {% if needRestart %} +
+ + + {% set restartTextLabel = "UPDATE_NOTIFICATION_NEED-RESTART" %} + {{ restartTextLabel | translate() | safe }} + +
+ {% endif %} {% for name, status in moduleList %}
@@ -12,4 +21,21 @@ {{ subTextLabel | translate({COMMIT_COUNT: status.behind, BRANCH_NAME: status.current}) | diffLink(status) | safe }}
{% endfor %} + {% for name, status in updatesList %} +
+ {% if status.done %} + + + {% set updateTextLabel = "UPDATE_NOTIFICATION_DONE" %} + {{ updateTextLabel | translate({MODULE_NAME: name}) | safe }} + + {% else %} + + + {% set updateTextLabel = "UPDATE_NOTIFICATION_ERROR" %} + {{ updateTextLabel | translate({MODULE_NAME: name}) | safe }} + + {% endif %} +
+ {% endfor %} {% endif %} diff --git a/package-lock.json b/package-lock.json index e9c93e65d3..9dd7980e71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "license": "MIT", "dependencies": { "colors": "^1.4.0", + "command-exists": "^1.2.9", "console-stamp": "^3.1.2", "envsub": "^4.1.0", "eslint": "^8.52.0", @@ -2860,6 +2861,11 @@ "node": ">= 0.8" } }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", diff --git a/package.json b/package.json index e3ed2312a3..473aa71cfc 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ }, "dependencies": { "colors": "^1.4.0", + "command-exists": "^1.2.9", "console-stamp": "^3.1.2", "envsub": "^4.1.0", "eslint": "^8.52.0", diff --git a/translations/de.json b/translations/de.json index e0ea340e87..d0f5eed7cb 100644 --- a/translations/de.json +++ b/translations/de.json @@ -43,5 +43,8 @@ "UPDATE_NOTIFICATION": "Aktualisierung für MagicMirror² verfügbar.", "UPDATE_NOTIFICATION_MODULE": "Aktualisierung für das Modul „{MODULE_NAME}“ verfügbar.", "UPDATE_INFO_SINGLE": "Die aktuelle Installation ist ein Commit hinter dem {BRANCH_NAME}-Branch.", - "UPDATE_INFO_MULTIPLE": "Die aktuelle Installation ist {COMMIT_COUNT} Commits hinter dem {BRANCH_NAME}-Branch." + "UPDATE_INFO_MULTIPLE": "Die aktuelle Installation ist {COMMIT_COUNT} Commits hinter dem {BRANCH_NAME}-Branch.", + "UPDATE_NOTIFICATION_DONE": "Aktualisierung für das Modul {MODULE_NAME} abgeschlossen.", + "UPDATE_NOTIFICATION_ERROR": "Fehler bei der Aktualisierung für das Modul {MODULE_NAME}.", + "UPDATE_NOTIFICATION_NEED-RESTART": "MagicMirror muss neu gestartet werden." } diff --git a/translations/en.json b/translations/en.json index eb9200c6c2..e10801e5b8 100644 --- a/translations/en.json +++ b/translations/en.json @@ -41,5 +41,8 @@ "UPDATE_NOTIFICATION": "MagicMirror² update available.", "UPDATE_NOTIFICATION_MODULE": "Update available for {MODULE_NAME} module.", "UPDATE_INFO_SINGLE": "The current installation is {COMMIT_COUNT} commit behind on the {BRANCH_NAME} branch.", - "UPDATE_INFO_MULTIPLE": "The current installation is {COMMIT_COUNT} commits behind on the {BRANCH_NAME} branch." + "UPDATE_INFO_MULTIPLE": "The current installation is {COMMIT_COUNT} commits behind on the {BRANCH_NAME} branch.", + "UPDATE_NOTIFICATION_DONE": "Update done for {MODULE_NAME} module", + "UPDATE_NOTIFICATION_ERROR": "Update error for {MODULE_NAME} module", + "UPDATE_NOTIFICATION_NEED-RESTART": "Restarting of MagicMirror is required." } diff --git a/translations/fr.json b/translations/fr.json index 319e0fda3d..b7f9d02746 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -43,5 +43,8 @@ "UPDATE_NOTIFICATION": "Une mise à jour de MagicMirror² est disponible", "UPDATE_NOTIFICATION_MODULE": "Une mise à jour est disponible pour le module {MODULE_NAME}.", "UPDATE_INFO_SINGLE": "L'installation actuelle est {COMMIT_COUNT} commit en retard sur la branche {BRANCH_NAME}.", - "UPDATE_INFO_MULTIPLE": "L'installation actuelle est {COMMIT_COUNT} commits en retard sur la branche {BRANCH_NAME}." + "UPDATE_INFO_MULTIPLE": "L'installation actuelle est {COMMIT_COUNT} commits en retard sur la branche {BRANCH_NAME}.", + "UPDATE_NOTIFICATION_DONE": "Mise à jour effectuée pour le module {MODULE_NAME}", + "UPDATE_NOTIFICATION_ERROR": "Erreur lors de la mise à jour du module {MODULE_NAME}", + "UPDATE_NOTIFICATION_NEED-RESTART": "Le redémarrage de MagicMirror est nécessaire." } From 70ddd8063267c92403c3e962a595f30e0e0c2f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bugsounet=20-=20C=C3=A9dric?= Date: Sat, 11 Nov 2023 09:13:16 +0100 Subject: [PATCH 185/204] Use `html-to-text` instead of regex for transform description (#3264) I try to use only `html-to-text` library it's will solve issue #3235 @rejas, @sdetweil, @khassel: Can you do tests with your own feeds? Thanks for feedbacks --- CHANGELOG.md | 1 + modules/default/newsfeed/newsfeedfetcher.js | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9a7b915f6..3140c95139 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ _This release is scheduled to be released on 2024-01-01._ ### Updated - Update electron to v27 and update other dependencies as well as github actions +- Update newsfeed: Use `html-to-text` instead of regex for transform description ### Fixed diff --git a/modules/default/newsfeed/newsfeedfetcher.js b/modules/default/newsfeed/newsfeedfetcher.js index 0f1b5d6c6d..0c52e347ce 100644 --- a/modules/default/newsfeed/newsfeedfetcher.js +++ b/modules/default/newsfeed/newsfeedfetcher.js @@ -52,10 +52,15 @@ const NewsfeedFetcher = function (url, reloadInterval, encoding, logFeedWarnings const url = item.url || item.link || ""; if (title && pubdate) { - const regex = /(<([^>]+)>)/gi; - description = description.toString().replace(regex, ""); // Convert HTML entities, codes and tag - description = htmlToText(description, { wordwrap: false }); + description = htmlToText(description, { + wordwrap: false, + selectors: [ + { selector: "a", options: { ignoreHref: true, noAnchorUrl: true } }, + { selector: "br", format: "inlineSurround", options: { prefix: " " } }, + { selector: "img", format: "skip" } + ] + }); items.push({ title: title, From 247115d2e4726746db9ebb7612464b34f76d7f8c Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Wed, 15 Nov 2023 12:44:08 -0600 Subject: [PATCH 186/204] fix electron start loadurl on windows when address="0.0.0.0" (#3268) > - Does the pull request solve a **related** issue? Fixes #2550 > - What does the pull request accomplish? Use a list if needed. changes the loadUrl to use localhost, as electron and MM are on this same system the mm 'server' is still listening on all adapters, including localhost --- CHANGELOG.md | 1 + js/electron.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3140c95139..ebfba62ece 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ _This release is scheduled to be released on 2024-01-01._ - Fix issue template (#3167) - Fix #3256 filter out bad results from rrule.between - Fix calendar events sometimes not respecting deleted events (#3250) +- Fix electron loadurl locally on Windows when address "0.0.0.0" (#2550) ## [2.25.0] - 2023-10-01 diff --git a/js/electron.js b/js/electron.js index 43f637acbb..18c5bfe29b 100644 --- a/js/electron.js +++ b/js/electron.js @@ -77,7 +77,7 @@ function createWindow() { prefix = "http://"; } - let address = (config.address === void 0) | (config.address === "") ? (config.address = "localhost") : config.address; + let address = (config.address === void 0) | (config.address === "") | (config.address === "0.0.0.0") ? (config.address = "localhost") : config.address; mainWindow.loadURL(`${prefix}${address}:${config.port}`); // Open the DevTools if run with "npm start dev" From 679a413788775a5ab2d6b7d1c38853349b077bed Mon Sep 17 00:00:00 2001 From: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com> Date: Mon, 20 Nov 2023 08:03:29 +0100 Subject: [PATCH 187/204] Review eslint config (#3269) - Remove "prettier" from plugin array, because it's already enabled by "plugin:prettier/recommended" - Remove "jsdoc" from plugin array, because it's already enabled by "plugin:jsdoc/recommended" - Enable recommended import rules - Add two additional import rules Note: To avoid overloading this PR I'll tackle the jest part with another PR after this one has been dealt with. --- .eslintrc.json | 6 ++++-- CHANGELOG.md | 1 + js/app.js | 1 + js/server_functions.js | 1 + modules/default/calendar/calendarfetcherutils.js | 1 + tests/e2e/animateCSS_spec.js | 2 +- tests/e2e/helpers/basic-auth.js | 1 + 7 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index bc12bf8288..8329999632 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,6 @@ { - "extends": ["eslint:recommended", "plugin:prettier/recommended", "plugin:jsdoc/recommended"], - "plugins": ["prettier", "import", "jsdoc", "jest"], + "extends": ["eslint:recommended", "plugin:import/recommended", "plugin:jsdoc/recommended", "plugin:prettier/recommended"], + "plugins": ["jest"], "env": { "browser": true, "es2022": true, @@ -24,6 +24,8 @@ "rules": { "eqeqeq": "error", "import/order": "error", + "import/extensions": "error", + "import/newline-after-import": "error", "no-param-reassign": "error", "no-prototype-builtins": "off", "no-throw-literal": "error", diff --git a/CHANGELOG.md b/CHANGELOG.md index ebfba62ece..4d60c9b542 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ _This release is scheduled to be released on 2024-01-01._ - Update electron to v27 and update other dependencies as well as github actions - Update newsfeed: Use `html-to-text` instead of regex for transform description +- Review ESLint config (#3269) ### Fixed diff --git a/js/app.js b/js/app.js index 184940189c..0d675845f5 100644 --- a/js/app.js +++ b/js/app.js @@ -12,6 +12,7 @@ const fs = require("fs"); const path = require("path"); const envsub = require("envsub"); const Log = require("logger"); + const Server = require(`${__dirname}/server`); const Utils = require(`${__dirname}/utils`); const defaultModules = require(`${__dirname}/../modules/default/defaultmodules`); diff --git a/js/server_functions.js b/js/server_functions.js index 5693ad41c4..a11e81a544 100644 --- a/js/server_functions.js +++ b/js/server_functions.js @@ -1,6 +1,7 @@ const fs = require("fs"); const path = require("path"); const Log = require("logger"); + const startUp = new Date(); /** diff --git a/modules/default/calendar/calendarfetcherutils.js b/modules/default/calendar/calendarfetcherutils.js index e10a7904e6..142032897c 100644 --- a/modules/default/calendar/calendarfetcherutils.js +++ b/modules/default/calendar/calendarfetcherutils.js @@ -10,6 +10,7 @@ */ const path = require("path"); const moment = require("moment"); + const zoneTable = require(path.join(__dirname, "windowsZones.json")); const Log = require("../../../js/logger"); diff --git a/tests/e2e/animateCSS_spec.js b/tests/e2e/animateCSS_spec.js index 3f7cac8354..b0f138c1a0 100644 --- a/tests/e2e/animateCSS_spec.js +++ b/tests/e2e/animateCSS_spec.js @@ -5,7 +5,7 @@ * 09/2023 * MIT Licensed. */ -const helpers = require("./helpers/global-setup.js"); +const helpers = require("./helpers/global-setup"); describe("AnimateCSS integration Test", () => { // define config file for testing diff --git a/tests/e2e/helpers/basic-auth.js b/tests/e2e/helpers/basic-auth.js index 8307464e23..c793085ba9 100644 --- a/tests/e2e/helpers/basic-auth.js +++ b/tests/e2e/helpers/basic-auth.js @@ -1,6 +1,7 @@ const path = require("path"); const auth = require("express-basic-auth"); const express = require("express"); + const app = express(); const basicAuth = auth({ From 7098f1e41f6ae7911962a1bceedcc6362381a3b8 Mon Sep 17 00:00:00 2001 From: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com> Date: Mon, 20 Nov 2023 20:11:41 +0100 Subject: [PATCH 188/204] Enable and apply ESLint Jest rules (#3270) Jest was in the plugin array of the ESLint configuration, but no rules were enabled. So ESLint hasn't checked any Jest rules yet. So I activated the recommended Jest rules and added a few more. Then I fixed the issues (mostly automatically). I have deactivated the rules "jest/expect-expect" and "jest/no-done-callback" for the time being, as they would have entailed major changes. I didn't want to make the PR too big. I'm not a Jest expert, but the changes so far look good to me. What do you think of that @khassel? :slightly_smiling_face: --- .eslintrc.json | 11 +++++- CHANGELOG.md | 1 + tests/e2e/animateCSS_spec.js | 8 ++-- tests/e2e/env_spec.js | 2 +- tests/e2e/fonts_spec.js | 2 +- tests/e2e/helpers/global-setup.js | 2 +- tests/e2e/helpers/weather-functions.js | 2 +- tests/e2e/modules/alert_spec.js | 2 +- tests/e2e/modules/calendar_spec.js | 8 ++-- tests/e2e/modules/clock_spec.js | 14 +++---- tests/e2e/modules/compliments_spec.js | 6 +-- tests/e2e/modules/helloworld_spec.js | 4 +- tests/e2e/modules/newsfeed_spec.js | 16 ++++---- tests/e2e/modules/weather_current_spec.js | 2 +- tests/e2e/modules/weather_forecast_spec.js | 12 +++--- tests/e2e/modules_display_spec.js | 4 +- tests/e2e/modules_empty_spec.js | 4 +- tests/e2e/modules_position_spec.js | 2 +- tests/e2e/translations_spec.js | 6 +-- tests/electron/env_spec.js | 6 +-- tests/electron/helpers/global-setup.js | 4 +- tests/electron/helpers/weather-setup.js | 2 +- tests/electron/modules/calendar_spec.js | 2 +- tests/electron/modules/compliments_spec.js | 2 +- tests/unit/classes/translator_spec.js | 2 +- tests/unit/functions/server_functions_spec.js | 38 +++++++++---------- .../unit/functions/updatenotification_spec.js | 8 ++-- tests/unit/global_vars/root_path_spec.js | 4 +- .../calendar_fetcher_utils_bad_rrule.js | 2 +- .../calendar/calendar_fetcher_utils_spec.js | 4 +- tests/unit/modules/default/utils_spec.js | 26 ++++++------- .../default/weather/weather_utils_spec.js | 10 ++--- 32 files changed, 113 insertions(+), 105 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 8329999632..4612874331 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,6 @@ { - "extends": ["eslint:recommended", "plugin:import/recommended", "plugin:jsdoc/recommended", "plugin:prettier/recommended"], - "plugins": ["jest"], + "extends": ["eslint:recommended", "plugin:import/recommended", "plugin:jest/recommended", "plugin:jsdoc/recommended", "plugin:prettier/recommended"], + "plugins": [], "env": { "browser": true, "es2022": true, @@ -26,6 +26,13 @@ "import/order": "error", "import/extensions": "error", "import/newline-after-import": "error", + "jest/consistent-test-it": "warn", + "jest/expect-expect": "off", + "jest/no-done-callback": "off", + "jest/prefer-expect-resolves": "warn", + "jest/prefer-mock-promise-shorthand": "warn", + "jest/prefer-to-be": "warn", + "jest/prefer-to-have-length": "warn", "no-param-reassign": "error", "no-prototype-builtins": "off", "no-throw-literal": "error", diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d60c9b542..e5ed21ec6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ _This release is scheduled to be released on 2024-01-01._ - Added updatenotification Updater (for 3rd party modules) - Added node 21 to the test matrix - Added transform object to calendar:customEvents +- Added ESLint rules for jest ### Removed diff --git a/tests/e2e/animateCSS_spec.js b/tests/e2e/animateCSS_spec.js index b0f138c1a0..91d03c176a 100644 --- a/tests/e2e/animateCSS_spec.js +++ b/tests/e2e/animateCSS_spec.js @@ -25,22 +25,22 @@ describe("AnimateCSS integration Test", () => { const doTest = async (animationIn, animationOut) => { await helpers.getDocument(); let elem = await helpers.waitForElement(`.compliments`); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); let styles = window.getComputedStyle(elem); if (animationIn && animationIn !== "") { expect(styles._values["animation-name"]).toBe(animationIn); } else { - expect(styles._values["animation-name"]).toBe(undefined); + expect(styles._values["animation-name"]).toBeUndefined(); } if (animationOut && animationOut !== "") { elem = await helpers.waitForElement(`.compliments.animate__animated.animate__${animationOut}`); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); styles = window.getComputedStyle(elem); expect(styles._values["animation-name"]).toBe(animationOut); } else { - expect(styles._values["animation-name"]).toBe(undefined); + expect(styles._values["animation-name"]).toBeUndefined(); } }; diff --git a/tests/e2e/env_spec.js b/tests/e2e/env_spec.js index 2a9945a85f..eebc8912f0 100644 --- a/tests/e2e/env_spec.js +++ b/tests/e2e/env_spec.js @@ -21,7 +21,7 @@ describe("App environment", () => { it("should show the title MagicMirror²", async () => { const elem = await helpers.waitForElement("title"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toBe("MagicMirror²"); }); }); diff --git a/tests/e2e/fonts_spec.js b/tests/e2e/fonts_spec.js index 706ed4a909..458ff2baaf 100644 --- a/tests/e2e/fonts_spec.js +++ b/tests/e2e/fonts_spec.js @@ -20,7 +20,7 @@ describe("All font files from roboto.css should be downloadable", () => { await helpers.stopApplication(); }); - test.each(fontFiles)("should return 200 HTTP code for file '%s'", async (fontFile) => { + it.each(fontFiles)("should return 200 HTTP code for file '%s'", async (fontFile) => { const fontUrl = `http://localhost:8080/fonts/${fontFile}`; const res = await fetch(fontUrl); expect(res.status).toBe(200); diff --git a/tests/e2e/helpers/global-setup.js b/tests/e2e/helpers/global-setup.js index d20156e5fd..251c55035f 100644 --- a/tests/e2e/helpers/global-setup.js +++ b/tests/e2e/helpers/global-setup.js @@ -82,6 +82,6 @@ exports.waitForAllElements = (selector) => { exports.testMatch = async (element, regex) => { const elem = await this.waitForElement(element); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toMatch(regex); }; diff --git a/tests/e2e/helpers/weather-functions.js b/tests/e2e/helpers/weather-functions.js index 02713754cf..2f84ea033e 100644 --- a/tests/e2e/helpers/weather-functions.js +++ b/tests/e2e/helpers/weather-functions.js @@ -3,7 +3,7 @@ const helpers = require("./global-setup"); exports.getText = async (element, result) => { const elem = await helpers.waitForElement(element); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect( elem.textContent .trim() diff --git a/tests/e2e/modules/alert_spec.js b/tests/e2e/modules/alert_spec.js index c118e17ef3..f3c95a6c3c 100644 --- a/tests/e2e/modules/alert_spec.js +++ b/tests/e2e/modules/alert_spec.js @@ -11,7 +11,7 @@ describe("Alert module", () => { it("should show the welcome message", async () => { const elem = await helpers.waitForElement(".ns-box .ns-box-inner .light.bright.small"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toContain("Welcome, start was successful!"); }); }); diff --git a/tests/e2e/modules/calendar_spec.js b/tests/e2e/modules/calendar_spec.js index 398552884d..71111508c6 100644 --- a/tests/e2e/modules/calendar_spec.js +++ b/tests/e2e/modules/calendar_spec.js @@ -9,17 +9,17 @@ describe("Calendar module", () => { */ const testElementLength = async (element, result, not) => { const elem = await helpers.waitForAllElements(element); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); if (not === "not") { - expect(elem.length).not.toBe(result); + expect(elem).not.toHaveLength(result); } else { - expect(elem.length).toBe(result); + expect(elem).toHaveLength(result); } }; const testTextContain = async (element, text) => { const elem = await helpers.waitForElement(element, "undefinedLoading"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toContain(text); }; diff --git a/tests/e2e/modules/clock_spec.js b/tests/e2e/modules/clock_spec.js index d1a940f12e..03205689c7 100644 --- a/tests/e2e/modules/clock_spec.js +++ b/tests/e2e/modules/clock_spec.js @@ -72,7 +72,7 @@ describe("Clock module", () => { it("should not show the time when digital clock is shown", async () => { const elem = document.querySelector(".clock .digital .time"); - expect(elem).toBe(null); + expect(elem).toBeNull(); }); }); @@ -84,12 +84,12 @@ describe("Clock module", () => { it("should show the sun times", async () => { const elem = await helpers.waitForElement(".clock .digital .sun"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); }); it("should show the moon times", async () => { const elem = await helpers.waitForElement(".clock .digital .moon"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); }); }); @@ -108,7 +108,7 @@ describe("Clock module", () => { const currentWeekNumber = moment().week(); const weekToShow = `Week ${currentWeekNumber}`; const elem = await helpers.waitForElement(".clock .week"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toBe(weekToShow); }); }); @@ -121,7 +121,7 @@ describe("Clock module", () => { it("should show the analog clock face", async () => { const elem = helpers.waitForElement(".clock-circle"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); }); }); @@ -133,9 +133,9 @@ describe("Clock module", () => { it("should show the analog clock face and the date", async () => { const elemClock = helpers.waitForElement(".clock-circle"); - await expect(elemClock).not.toBe(null); + await expect(elemClock).not.toBeNull(); const elemDate = helpers.waitForElement(".clock .date"); - await expect(elemDate).not.toBe(null); + await expect(elemDate).not.toBeNull(); }); }); }); diff --git a/tests/e2e/modules/compliments_spec.js b/tests/e2e/modules/compliments_spec.js index 97c044f3e6..e96d34f661 100644 --- a/tests/e2e/modules/compliments_spec.js +++ b/tests/e2e/modules/compliments_spec.js @@ -7,9 +7,9 @@ describe("Compliments module", () => { */ const doTest = async (complimentsArray) => { let elem = await helpers.waitForElement(".compliments"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); elem = await helpers.waitForElement(".module-content"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(complimentsArray).toContain(elem.textContent); }; @@ -18,7 +18,7 @@ describe("Compliments module", () => { }); describe("Feature anytime in compliments module", () => { - describe("Set anytime and empty compliments for morning, evening and afternoon ", () => { + describe("Set anytime and empty compliments for morning, evening and afternoon", () => { beforeAll(async () => { await helpers.startApplication("tests/configs/modules/compliments/compliments_anytime.js"); await helpers.getDocument(); diff --git a/tests/e2e/modules/helloworld_spec.js b/tests/e2e/modules/helloworld_spec.js index ecdcdf2076..413c60e509 100644 --- a/tests/e2e/modules/helloworld_spec.js +++ b/tests/e2e/modules/helloworld_spec.js @@ -13,7 +13,7 @@ describe("Test helloworld module", () => { it("Test message helloworld module", async () => { const elem = await helpers.waitForElement(".helloworld"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toContain("Test HelloWorld Module"); }); }); @@ -26,7 +26,7 @@ describe("Test helloworld module", () => { it("Test message helloworld module", async () => { const elem = await helpers.waitForElement(".helloworld"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toContain("Hello World!"); }); }); diff --git a/tests/e2e/modules/newsfeed_spec.js b/tests/e2e/modules/newsfeed_spec.js index 5d419954d8..141b880661 100644 --- a/tests/e2e/modules/newsfeed_spec.js +++ b/tests/e2e/modules/newsfeed_spec.js @@ -13,20 +13,20 @@ describe("Newsfeed module", () => { it("should show the newsfeed title", async () => { const elem = await helpers.waitForElement(".newsfeed .newsfeed-source"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toContain("Rodrigo Ramirez Blog"); }); it("should show the newsfeed article", async () => { const elem = await helpers.waitForElement(".newsfeed .newsfeed-title"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toContain("QPanel"); }); it("should NOT show the newsfeed description", async () => { await helpers.waitForElement(".newsfeed"); const elem = document.querySelector(".newsfeed .newsfeed-desc"); - expect(elem).toBe(null); + expect(elem).toBeNull(); }); }); @@ -38,14 +38,14 @@ describe("Newsfeed module", () => { it("should not show articles with prohibited words", async () => { const elem = await helpers.waitForElement(".newsfeed .newsfeed-title"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toContain("Problema VirtualBox"); }); it("should show the newsfeed description", async () => { const elem = await helpers.waitForElement(".newsfeed .newsfeed-desc"); - expect(elem).not.toBe(null); - expect(elem.textContent.length).not.toBe(0); + expect(elem).not.toBeNull(); + expect(elem.textContent).not.toHaveLength(0); }); }); @@ -57,7 +57,7 @@ describe("Newsfeed module", () => { it("should show malformed url warning", async () => { const elem = await helpers.waitForElement(".newsfeed .small", "No news at the moment."); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toContain("Error in the Newsfeed module. Malformed url."); }); }); @@ -70,7 +70,7 @@ describe("Newsfeed module", () => { it("should show empty items info message", async () => { const elem = await helpers.waitForElement(".newsfeed .small"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toContain("No news at the moment."); }); }); diff --git a/tests/e2e/modules/weather_current_spec.js b/tests/e2e/modules/weather_current_spec.js index d97f83094e..5ba30fc5b6 100644 --- a/tests/e2e/modules/weather_current_spec.js +++ b/tests/e2e/modules/weather_current_spec.js @@ -49,7 +49,7 @@ describe("Weather module", () => { it("should render windDirection with an arrow", async () => { const elem = await helpers.waitForElement(".weather .normal.medium sup i.fa-long-arrow-alt-down"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.outerHTML).toContain("transform:rotate(250deg)"); }); diff --git a/tests/e2e/modules/weather_forecast_spec.js b/tests/e2e/modules/weather_forecast_spec.js index a5ce70d20a..eb5dfc21de 100644 --- a/tests/e2e/modules/weather_forecast_spec.js +++ b/tests/e2e/modules/weather_forecast_spec.js @@ -24,7 +24,7 @@ describe("Weather module: Weather Forecast", () => { for (const [index, icon] of icons.entries()) { it(`should render icon ${icon}`, async () => { const elem = await helpers.waitForElement(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(2) span.wi-${icon}`); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); }); } @@ -46,7 +46,7 @@ describe("Weather module: Weather Forecast", () => { for (const [index, opacity] of opacities.entries()) { it(`should render fading of rows with opacity=${opacity}`, async () => { const elem = await helpers.waitForElement(`.weather table.small tr:nth-child(${index + 1})`); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.outerHTML).toContain(``); }); } @@ -72,14 +72,14 @@ describe("Weather module: Weather Forecast", () => { it("should render custom table class", async () => { const elem = await helpers.waitForElement(".weather table.myTableClass"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); }); it("should render colored rows", async () => { const table = await helpers.waitForElement(".weather table.myTableClass"); - expect(table).not.toBe(null); - expect(table.rows).not.toBe(null); - expect(table.rows.length).toBe(5); + expect(table).not.toBeNull(); + expect(table.rows).not.toBeNull(); + expect(table.rows).toHaveLength(5); }); const precipitations = [undefined, "2.51 mm"]; diff --git a/tests/e2e/modules_display_spec.js b/tests/e2e/modules_display_spec.js index 15520d05c9..c8dba6e9f8 100644 --- a/tests/e2e/modules_display_spec.js +++ b/tests/e2e/modules_display_spec.js @@ -11,14 +11,14 @@ describe("Display of modules", () => { it("should show the test header", async () => { const elem = await helpers.waitForElement("#module_0_helloworld .module-header"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); // textContent gibt hier lowercase zurück, das uppercase wird durch css realisiert, was daher nicht in textContent landet expect(elem.textContent).toBe("test_header"); }); it("should show no header if no header text is specified", async () => { const elem = await helpers.waitForElement("#module_1_helloworld .module-header"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toBe("undefined"); }); }); diff --git a/tests/e2e/modules_empty_spec.js b/tests/e2e/modules_empty_spec.js index dfe6c0813c..535e3141c8 100644 --- a/tests/e2e/modules_empty_spec.js +++ b/tests/e2e/modules_empty_spec.js @@ -11,13 +11,13 @@ describe("Check configuration without modules", () => { it("shows the message MagicMirror² title", async () => { const elem = await helpers.waitForElement("#module_1_helloworld .module-content"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toContain("MagicMirror²"); }); it("shows the url of michael's website", async () => { const elem = await helpers.waitForElement("#module_5_helloworld .module-content"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toContain("www.michaelteeuw.nl"); }); }); diff --git a/tests/e2e/modules_position_spec.js b/tests/e2e/modules_position_spec.js index b23ebeb24e..0aa8379092 100644 --- a/tests/e2e/modules_position_spec.js +++ b/tests/e2e/modules_position_spec.js @@ -15,7 +15,7 @@ describe("Position of modules", () => { const className = position.replace("_", "."); it(`should show text in ${position}`, async () => { const elem = await helpers.waitForElement(`.${className}`); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(elem.textContent).toContain(`Text in ${position}`); }); } diff --git a/tests/e2e/translations_spec.js b/tests/e2e/translations_spec.js index ec1387572f..14e6a75ce7 100644 --- a/tests/e2e/translations_spec.js +++ b/tests/e2e/translations_spec.js @@ -55,7 +55,7 @@ describe("Translations", () => { await MMM.loadTranslations(); - expect(Translator.load.args.length).toBe(1); + expect(Translator.load.args).toHaveLength(1); expect(Translator.load.calledWith(MMM, "translations/en.json", false)).toBe(true); done(); @@ -72,7 +72,7 @@ describe("Translations", () => { await MMM.loadTranslations(); - expect(Translator.load.args.length).toBe(2); + expect(Translator.load.args).toHaveLength(2); expect(Translator.load.calledWith(MMM, "translations/de.json", false)).toBe(true); expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true); @@ -91,7 +91,7 @@ describe("Translations", () => { await MMM.loadTranslations(); - expect(Translator.load.args.length).toBe(1); + expect(Translator.load.args).toHaveLength(1); expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true); done(); diff --git a/tests/electron/env_spec.js b/tests/electron/env_spec.js index 32b9f6fc3d..12c2ee0766 100644 --- a/tests/electron/env_spec.js +++ b/tests/electron/env_spec.js @@ -12,8 +12,8 @@ describe("Electron app environment", () => { it("should open browserwindow", async () => { const module = await helpers.getElement("#module_0_helloworld"); - expect(await module.textContent()).toContain("Test Display Header"); - expect(global.electronApp.windows().length).toBe(1); + await expect(module.textContent()).resolves.toContain("Test Display Header"); + expect(global.electronApp.windows()).toHaveLength(1); }); }); @@ -29,7 +29,7 @@ describe("Development console tests", () => { it("should open browserwindow and dev console", async () => { while (global.electronApp.windows().length < 2) await events.once(global.electronApp, "window"); const pageArray = await global.electronApp.windows(); - expect(pageArray.length).toBe(2); + expect(pageArray).toHaveLength(2); for (const page of pageArray) { expect(["MagicMirror²", "DevTools"]).toContain(await page.title()); } diff --git a/tests/electron/helpers/global-setup.js b/tests/electron/helpers/global-setup.js index f6e071be72..164c7f6630 100644 --- a/tests/electron/helpers/global-setup.js +++ b/tests/electron/helpers/global-setup.js @@ -37,9 +37,9 @@ exports.stopApplication = async () => { }; exports.getElement = async (selector) => { - expect(global.page); + expect(global.page).not.toBeNull(); let elem = global.page.locator(selector); await elem.waitFor(); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); return elem; }; diff --git a/tests/electron/helpers/weather-setup.js b/tests/electron/helpers/weather-setup.js index e939af6763..76ca8376b3 100644 --- a/tests/electron/helpers/weather-setup.js +++ b/tests/electron/helpers/weather-setup.js @@ -3,7 +3,7 @@ const helpers = require("./global-setup"); exports.getText = async (element, result) => { const elem = await helpers.getElement(element); - await expect(elem).not.toBe(null); + await expect(elem).not.toBeNull(); const text = await elem.textContent(); await expect( text diff --git a/tests/electron/modules/calendar_spec.js b/tests/electron/modules/calendar_spec.js index b0b7c27679..174764ce8a 100644 --- a/tests/electron/modules/calendar_spec.js +++ b/tests/electron/modules/calendar_spec.js @@ -7,7 +7,7 @@ describe("Calendar module", () => { */ const doTest = async (cssClass) => { let elem = await helpers.getElement(`.calendar .module-content .event${cssClass}`); - expect(await elem.isVisible()).toBe(true); + await expect(elem.isVisible()).resolves.toBe(true); }; afterEach(async () => { diff --git a/tests/electron/modules/compliments_spec.js b/tests/electron/modules/compliments_spec.js index a6dd384e70..63902e038b 100644 --- a/tests/electron/modules/compliments_spec.js +++ b/tests/electron/modules/compliments_spec.js @@ -8,7 +8,7 @@ describe("Compliments module", () => { const doTest = async (complimentsArray) => { await helpers.getElement(".compliments"); const elem = await helpers.getElement(".module-content"); - expect(elem).not.toBe(null); + expect(elem).not.toBeNull(); expect(complimentsArray).toContain(await elem.textContent()); }; diff --git a/tests/unit/classes/translator_spec.js b/tests/unit/classes/translator_spec.js index 410234acf9..ccd7a3dbda 100644 --- a/tests/unit/classes/translator_spec.js +++ b/tests/unit/classes/translator_spec.js @@ -197,7 +197,7 @@ describe("Translator", () => { }; await Translator.load(mmm, file, false); - expect(Translator.translations[mmm.name]).toBe(undefined); + expect(Translator.translations[mmm.name]).toBeUndefined(); expect(Translator.translationsFallback[mmm.name]).toEqual({ Hello: "Hallo" }); diff --git a/tests/unit/functions/server_functions_spec.js b/tests/unit/functions/server_functions_spec.js index 6242c9a9b5..9895567558 100644 --- a/tests/unit/functions/server_functions_spec.js +++ b/tests/unit/functions/server_functions_spec.js @@ -35,43 +35,43 @@ describe("server_functions tests", () => { }; }); - test("Calls correct URL once", async () => { + it("Calls correct URL once", async () => { const urlToCall = "http://www.test.com/path?param1=value1"; request.url = `/cors?url=${urlToCall}`; await cors(request, corsResponse); - expect(fetchMock.mock.calls.length).toBe(1); + expect(fetchMock.mock.calls).toHaveLength(1); expect(fetchMock.mock.calls[0][0]).toBe(urlToCall); }); - test("Forewards Content-Type if json", async () => { + it("Forewards Content-Type if json", async () => { fetchResponseHeadersGet.mockImplementation(() => "json"); await cors(request, corsResponse); - expect(fetchResponseHeadersGet.mock.calls.length).toBe(1); + expect(fetchResponseHeadersGet.mock.calls).toHaveLength(1); expect(fetchResponseHeadersGet.mock.calls[0][0]).toBe("Content-Type"); - expect(corsResponse.set.mock.calls.length).toBe(1); + expect(corsResponse.set.mock.calls).toHaveLength(1); expect(corsResponse.set.mock.calls[0][0]).toBe("Content-Type"); expect(corsResponse.set.mock.calls[0][1]).toBe("json"); }); - test("Forewards Content-Type if xml", async () => { + it("Forewards Content-Type if xml", async () => { fetchResponseHeadersGet.mockImplementation(() => "xml"); await cors(request, corsResponse); - expect(fetchResponseHeadersGet.mock.calls.length).toBe(1); + expect(fetchResponseHeadersGet.mock.calls).toHaveLength(1); expect(fetchResponseHeadersGet.mock.calls[0][0]).toBe("Content-Type"); - expect(corsResponse.set.mock.calls.length).toBe(1); + expect(corsResponse.set.mock.calls).toHaveLength(1); expect(corsResponse.set.mock.calls[0][0]).toBe("Content-Type"); expect(corsResponse.set.mock.calls[0][1]).toBe("xml"); }); - test("Sends correct data from response", async () => { + it("Sends correct data from response", async () => { const responseData = "some data"; fetchResponseHeadersText.mockImplementation(() => responseData); @@ -82,11 +82,11 @@ describe("server_functions tests", () => { await cors(request, corsResponse); - expect(fetchResponseHeadersText.mock.calls.length).toBe(1); + expect(fetchResponseHeadersText.mock.calls).toHaveLength(1); expect(sentData).toBe(responseData); }); - test("Sends error data from response", async () => { + it("Sends error data from response", async () => { const error = new Error("error data"); fetchResponseHeadersText.mockImplementation(() => { throw error; @@ -99,32 +99,32 @@ describe("server_functions tests", () => { await cors(request, corsResponse); - expect(fetchResponseHeadersText.mock.calls.length).toBe(1); + expect(fetchResponseHeadersText.mock.calls).toHaveLength(1); expect(sentData).toBe(error); }); - test("Fetches with user agent by default", async () => { + it("Fetches with user agent by default", async () => { await cors(request, corsResponse); - expect(fetchMock.mock.calls.length).toBe(1); + expect(fetchMock.mock.calls).toHaveLength(1); expect(fetchMock.mock.calls[0][1]).toHaveProperty("headers"); expect(fetchMock.mock.calls[0][1].headers).toHaveProperty("User-Agent"); }); - test("Fetches with specified headers", async () => { + it("Fetches with specified headers", async () => { const headersParam = "sendheaders=header1:value1,header2:value2"; const urlParam = "http://www.test.com/path?param1=value1"; request.url = `/cors?${headersParam}&url=${urlParam}`; await cors(request, corsResponse); - expect(fetchMock.mock.calls.length).toBe(1); + expect(fetchMock.mock.calls).toHaveLength(1); expect(fetchMock.mock.calls[0][1]).toHaveProperty("headers"); expect(fetchMock.mock.calls[0][1].headers).toHaveProperty("header1", "value1"); expect(fetchMock.mock.calls[0][1].headers).toHaveProperty("header2", "value2"); }); - test("Sends specified headers", async () => { + it("Sends specified headers", async () => { fetchResponseHeadersGet.mockImplementation((input) => input.replace("header", "value")); const expectedheaders = "expectedheaders=header1,header2"; @@ -133,9 +133,9 @@ describe("server_functions tests", () => { await cors(request, corsResponse); - expect(fetchMock.mock.calls.length).toBe(1); + expect(fetchMock.mock.calls).toHaveLength(1); expect(fetchMock.mock.calls[0][1]).toHaveProperty("headers"); - expect(corsResponse.set.mock.calls.length).toBe(3); + expect(corsResponse.set.mock.calls).toHaveLength(3); expect(corsResponse.set.mock.calls[0][0]).toBe("Content-Type"); expect(corsResponse.set.mock.calls[1][0]).toBe("header1"); expect(corsResponse.set.mock.calls[1][1]).toBe("value1"); diff --git a/tests/unit/functions/updatenotification_spec.js b/tests/unit/functions/updatenotification_spec.js index 426154ecdb..23597568b7 100644 --- a/tests/unit/functions/updatenotification_spec.js +++ b/tests/unit/functions/updatenotification_spec.js @@ -103,7 +103,7 @@ describe("Updatenotification", () => { execMock.mockRejectedValueOnce(errorMessage); const repos = await gitHelper.getRepos(); - expect(repos.length).toBe(0); + expect(repos).toHaveLength(0); const { error } = require("logger"); expect(error).toHaveBeenCalledWith(`Failed to retrieve repo info for ${moduleName}: Failed to retrieve status`); @@ -142,7 +142,7 @@ describe("Updatenotification", () => { execMock.mockRejectedValueOnce(errorMessage); const repos = await gitHelper.getRepos(); - expect(repos.length).toBe(0); + expect(repos).toHaveLength(0); const { error } = require("logger"); expect(error).toHaveBeenCalledWith(`Failed to retrieve repo info for ${moduleName}: Failed to retrieve status`); @@ -183,7 +183,7 @@ describe("Updatenotification", () => { execMock.mockRejectedValueOnce(errorMessage); const repos = await gitHelper.getRepos(); - expect(repos.length).toBe(0); + expect(repos).toHaveLength(0); const { error } = require("logger"); expect(error).toHaveBeenCalledWith(`Failed to retrieve repo info for ${moduleName}: Failed to retrieve status`); @@ -224,7 +224,7 @@ describe("Updatenotification", () => { execMock.mockRejectedValueOnce(errorMessage); const repos = await gitHelper.getRepos(); - expect(repos.length).toBe(0); + expect(repos).toHaveLength(0); const { error } = require("logger"); expect(error).toHaveBeenCalledWith(`Failed to retrieve repo info for ${moduleName}: Failed to retrieve status`); diff --git a/tests/unit/global_vars/root_path_spec.js b/tests/unit/global_vars/root_path_spec.js index 78a96d091b..6806f5c3c2 100644 --- a/tests/unit/global_vars/root_path_spec.js +++ b/tests/unit/global_vars/root_path_spec.js @@ -14,11 +14,11 @@ describe("'global.root_path' set in js/app.js", () => { }); it("should not modify global.root_path for testing", () => { - expect(global.root_path).toBe(undefined); + expect(global.root_path).toBeUndefined(); }); it("should not modify global.version for testing", () => { - expect(global.version).toBe(undefined); + expect(global.version).toBeUndefined(); }); it("should expect the global.version equals package.json file", () => { diff --git a/tests/unit/modules/default/calendar/calendar_fetcher_utils_bad_rrule.js b/tests/unit/modules/default/calendar/calendar_fetcher_utils_bad_rrule.js index bb4911a325..e4508332e5 100644 --- a/tests/unit/modules/default/calendar/calendar_fetcher_utils_bad_rrule.js +++ b/tests/unit/modules/default/calendar/calendar_fetcher_utils_bad_rrule.js @@ -23,7 +23,7 @@ describe("Calendar fetcher utils test", () => { defaultConfig ); - expect(filteredEvents.length).toEqual(0); + expect(filteredEvents).toHaveLength(0); }); }); }); diff --git a/tests/unit/modules/default/calendar/calendar_fetcher_utils_spec.js b/tests/unit/modules/default/calendar/calendar_fetcher_utils_spec.js index 06870de95e..344e11d576 100644 --- a/tests/unit/modules/default/calendar/calendar_fetcher_utils_spec.js +++ b/tests/unit/modules/default/calendar/calendar_fetcher_utils_spec.js @@ -26,7 +26,7 @@ describe("Calendar fetcher utils test", () => { defaultConfig ); - expect(filteredEvents.length).toEqual(2); + expect(filteredEvents).toHaveLength(2); expect(filteredEvents[0].title).toBe("ongoingEvent"); expect(filteredEvents[1].title).toBe("upcomingEvent"); }); @@ -45,7 +45,7 @@ describe("Calendar fetcher utils test", () => { defaultConfig ); - expect(filteredEvents.length).toEqual(2); + expect(filteredEvents).toHaveLength(2); expect(filteredEvents[0].title).toBe("ongoingEvent"); expect(filteredEvents[1].title).toBe("upcomingEvent"); }); diff --git a/tests/unit/modules/default/utils_spec.js b/tests/unit/modules/default/utils_spec.js index 3430874c83..9d0467e7f1 100644 --- a/tests/unit/modules/default/utils_spec.js +++ b/tests/unit/modules/default/utils_spec.js @@ -24,16 +24,16 @@ describe("Default modules utils tests", () => { } }); - test("Calls correct URL once", async () => { + it("Calls correct URL once", async () => { urlToCall = "http://www.test.com/path?param1=value1"; await performWebRequest(urlToCall, "json", true); - expect(fetchMock.mock.calls.length).toBe(1); + expect(fetchMock.mock.calls).toHaveLength(1); expect(fetchMock.mock.calls[0][0]).toBe(`${locationProtocol}//${locationHost}/cors?url=${urlToCall}`); }); - test("Sends correct headers", async () => { + it("Sends correct headers", async () => { urlToCall = "http://www.test.com/path?param1=value1"; const headers = [ @@ -43,22 +43,22 @@ describe("Default modules utils tests", () => { await performWebRequest(urlToCall, "json", true, headers); - expect(fetchMock.mock.calls.length).toBe(1); + expect(fetchMock.mock.calls).toHaveLength(1); expect(fetchMock.mock.calls[0][0]).toBe(`${locationProtocol}//${locationHost}/cors?sendheaders=header1:value1,header2:value2&url=${urlToCall}`); }); }); describe("When not using cors proxy", () => { - test("Calls correct URL once", async () => { + it("Calls correct URL once", async () => { urlToCall = "http://www.test.com/path?param1=value1"; await performWebRequest(urlToCall); - expect(fetchMock.mock.calls.length).toBe(1); + expect(fetchMock.mock.calls).toHaveLength(1); expect(fetchMock.mock.calls[0][0]).toBe(urlToCall); }); - test("Sends correct headers", async () => { + it("Sends correct headers", async () => { urlToCall = "http://www.test.com/path?param1=value1"; const headers = [ { name: "header1", value: "value1" }, @@ -68,21 +68,21 @@ describe("Default modules utils tests", () => { await performWebRequest(urlToCall, "json", false, headers); const expectedHeaders = { headers: { header1: "value1", header2: "value2" } }; - expect(fetchMock.mock.calls.length).toBe(1); + expect(fetchMock.mock.calls).toHaveLength(1); expect(fetchMock.mock.calls[0][1]).toStrictEqual(expectedHeaders); }); }); describe("When receiving json format", () => { - test("Returns undefined when no data is received", async () => { + it("Returns undefined when no data is received", async () => { urlToCall = "www.test.com"; const response = await performWebRequest(urlToCall); - expect(response).toBe(undefined); + expect(response).toBeUndefined(); }); - test("Returns object when data is received", async () => { + it("Returns object when data is received", async () => { urlToCall = "www.test.com"; fetchResponse = new Response('{"body": "some content"}'); @@ -91,13 +91,13 @@ describe("Default modules utils tests", () => { expect(response.body).toBe("some content"); }); - test("Returns expected headers when data is received", async () => { + it("Returns expected headers when data is received", async () => { urlToCall = "www.test.com"; fetchResponse = new Response('{"body": "some content"}', { headers: { header1: "value1", header2: "value2" } }); const response = await performWebRequest(urlToCall, "json", false, undefined, ["header1"]); - expect(response.headers.length).toBe(1); + expect(response.headers).toHaveLength(1); expect(response.headers[0].name).toBe("header1"); expect(response.headers[0].value).toBe("value1"); }); diff --git a/tests/unit/modules/default/weather/weather_utils_spec.js b/tests/unit/modules/default/weather/weather_utils_spec.js index 4af4efddd2..a0c41edb05 100644 --- a/tests/unit/modules/default/weather/weather_utils_spec.js +++ b/tests/unit/modules/default/weather/weather_utils_spec.js @@ -2,13 +2,13 @@ const weather = require("../../../../../modules/default/weather/weatherutils"); const WeatherUtils = require("../../../../../modules/default/weather/weatherutils"); describe("Weather utils tests", () => { - describe("windspeed conversion", () => { + describe("windspeed conversion to imperial", () => { it("should convert temp correctly from Celsius to Fahrenheit", () => { expect(Math.round(WeatherUtils.convertTemp(10, "imperial"))).toBe(50); }); }); - describe("windspeed conversion", () => { + describe("windspeed conversion to beaufort", () => { it("should convert windspeed correctly from mps to beaufort", () => { expect(Math.round(WeatherUtils.convertWind(5, "beaufort"))).toBe(3); expect(Math.round(WeatherUtils.convertWind(300, "beaufort"))).toBe(12); @@ -38,16 +38,16 @@ describe("Weather utils tests", () => { describe("wind direction conversion", () => { it("should convert wind direction correctly from cardinal to value", () => { expect(WeatherUtils.convertWindDirection("SSE")).toBe(157); - expect(WeatherUtils.convertWindDirection("XXX")).toBe(null); + expect(WeatherUtils.convertWindDirection("XXX")).toBeNull(); }); }); describe("feelsLike calculation", () => { - it("should return a calculated feelsLike info", () => { + it("should return a calculated feelsLike info (negative value)", () => { expect(WeatherUtils.calculateFeelsLike(0, 20, 40)).toBe(-9.444444444444445); }); - it("should return a calculated feelsLike info", () => { + it("should return a calculated feelsLike info (positiv value)", () => { expect(WeatherUtils.calculateFeelsLike(30, 0, 60)).toBe(32.8320322777777); }); }); From 6ffdc7b55b9b3e1a6c83923afb3697eb4a7b4d7c Mon Sep 17 00:00:00 2001 From: Karsten Hassel Date: Wed, 22 Nov 2023 23:37:17 +0100 Subject: [PATCH 189/204] enable eslint jest/expect-expect and jest/no-done-callback (#3272) follow up to https://github.com/MichMich/MagicMirror/pull/3270 --- .eslintrc.json | 4 +- CHANGELOG.md | 2 +- tests/e2e/animateCSS_spec.js | 10 +- tests/e2e/helpers/global-setup.js | 1 + tests/e2e/helpers/weather-functions.js | 1 + tests/e2e/modules/calendar_spec.js | 37 ++- tests/e2e/modules/clock_es_spec.js | 12 +- tests/e2e/modules/clock_spec.js | 14 +- tests/e2e/modules/compliments_spec.js | 8 +- tests/e2e/modules/weather_current_spec.js | 22 +- tests/e2e/modules/weather_forecast_spec.js | 14 +- tests/e2e/modules/weather_hourly_spec.js | 8 +- tests/e2e/translations_spec.js | 176 ++++++----- tests/electron/helpers/weather-setup.js | 1 + tests/electron/modules/calendar_spec.js | 12 +- tests/electron/modules/compliments_spec.js | 10 +- tests/electron/modules/weather_spec.js | 4 +- tests/unit/classes/class_spec.js | 22 +- tests/unit/classes/translator_spec.js | 344 +++++++++++---------- tests/unit/functions/cmp_versions_spec.js | 22 +- 20 files changed, 391 insertions(+), 333 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 4612874331..72143745db 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -27,8 +27,8 @@ "import/extensions": "error", "import/newline-after-import": "error", "jest/consistent-test-it": "warn", - "jest/expect-expect": "off", - "jest/no-done-callback": "off", + "jest/expect-expect": "warn", + "jest/no-done-callback": "warn", "jest/prefer-expect-resolves": "warn", "jest/prefer-mock-promise-shorthand": "warn", "jest/prefer-to-be": "warn", diff --git a/CHANGELOG.md b/CHANGELOG.md index e5ed21ec6e..f6c4f71848 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ _This release is scheduled to be released on 2024-01-01._ - Added updatenotification Updater (for 3rd party modules) - Added node 21 to the test matrix - Added transform object to calendar:customEvents -- Added ESLint rules for jest +- Added ESLint rules for jest (including jest/expect-expect and jest/no-done-callback) ### Removed diff --git a/tests/e2e/animateCSS_spec.js b/tests/e2e/animateCSS_spec.js index 91d03c176a..b9e31a7620 100644 --- a/tests/e2e/animateCSS_spec.js +++ b/tests/e2e/animateCSS_spec.js @@ -21,6 +21,7 @@ describe("AnimateCSS integration Test", () => { * move similar tests in function doTest * @param {string} [animationIn] animation in name of AnimateCSS to test. * @param {string} [animationOut] animation out name of AnimateCSS to test. + * @returns {boolean} result */ const doTest = async (animationIn, animationOut) => { await helpers.getDocument(); @@ -42,6 +43,7 @@ describe("AnimateCSS integration Test", () => { } else { expect(styles._values["animation-name"]).toBeUndefined(); } + return true; }; afterEach(async () => { @@ -51,28 +53,28 @@ describe("AnimateCSS integration Test", () => { describe("animateIn and animateOut Test", () => { it("with flipInX and flipOutX animation", async () => { await helpers.startApplication(testConfigFile); - await doTest("flipInX", "flipOutX"); + await expect(doTest("flipInX", "flipOutX")).resolves.toBe(true); }); }); describe("use animateOut name for animateIn (vice versa) Test", () => { it("without animation", async () => { await helpers.startApplication(testConfigFileInvertedAnimationName); - await doTest(); + await expect(doTest()).resolves.toBe(true); }); }); describe("false Animation name test", () => { it("without animation", async () => { await helpers.startApplication(testConfigFileFallbackToDefault); - await doTest(); + await expect(doTest()).resolves.toBe(true); }); }); describe("no Animation defined test", () => { it("without animation", async () => { await helpers.startApplication(testConfigByDefault); - await doTest(); + await expect(doTest()).resolves.toBe(true); }); }); }); diff --git a/tests/e2e/helpers/global-setup.js b/tests/e2e/helpers/global-setup.js index 251c55035f..592b3556a8 100644 --- a/tests/e2e/helpers/global-setup.js +++ b/tests/e2e/helpers/global-setup.js @@ -84,4 +84,5 @@ exports.testMatch = async (element, regex) => { const elem = await this.waitForElement(element); expect(elem).not.toBeNull(); expect(elem.textContent).toMatch(regex); + return true; }; diff --git a/tests/e2e/helpers/weather-functions.js b/tests/e2e/helpers/weather-functions.js index 2f84ea033e..9b55f15e6a 100644 --- a/tests/e2e/helpers/weather-functions.js +++ b/tests/e2e/helpers/weather-functions.js @@ -10,6 +10,7 @@ exports.getText = async (element, result) => { .replace(/(\r\n|\n|\r)/gm, "") .replace(/[ ]+/g, " ") ).toBe(result); + return true; }; exports.startApp = async (configFileName, additionalMockData) => { diff --git a/tests/e2e/modules/calendar_spec.js b/tests/e2e/modules/calendar_spec.js index 71111508c6..4d1a231c5f 100644 --- a/tests/e2e/modules/calendar_spec.js +++ b/tests/e2e/modules/calendar_spec.js @@ -6,6 +6,7 @@ describe("Calendar module", () => { * @param {string} element css selector * @param {string} result expected number * @param {string} not reverse result + * @returns {boolean} result */ const testElementLength = async (element, result, not) => { const elem = await helpers.waitForAllElements(element); @@ -15,12 +16,14 @@ describe("Calendar module", () => { } else { expect(elem).toHaveLength(result); } + return true; }; const testTextContain = async (element, text) => { const elem = await helpers.waitForElement(element, "undefinedLoading"); expect(elem).not.toBeNull(); expect(elem.textContent).toContain(text); + return true; }; afterAll(async () => { @@ -34,11 +37,11 @@ describe("Calendar module", () => { }); it("should show the default maximumEntries of 10", async () => { - await testElementLength(".calendar .event", 10); + await expect(testElementLength(".calendar .event", 10)).resolves.toBe(true); }); it("should show the default calendar symbol in each event", async () => { - await testElementLength(".calendar .event .fa-calendar-alt", 0, "not"); + await expect(testElementLength(".calendar .event .fa-calendar-alt", 0, "not")).resolves.toBe(true); }); }); @@ -49,27 +52,27 @@ describe("Calendar module", () => { }); it("should show the custom maximumEntries of 5", async () => { - await testElementLength(".calendar .event", 5); + await expect(testElementLength(".calendar .event", 5)).resolves.toBe(true); }); it("should show the custom calendar symbol in four events", async () => { - await testElementLength(".calendar .event .fa-birthday-cake", 4); + await expect(testElementLength(".calendar .event .fa-birthday-cake", 4)).resolves.toBe(true); }); it("should show a customEvent calendar symbol in one event", async () => { - await testElementLength(".calendar .event .fa-dice", 1); + await expect(testElementLength(".calendar .event .fa-dice", 1)).resolves.toBe(true); }); it("should show a customEvent calendar eventClass in one event", async () => { - await testElementLength(".calendar .event.undo", 1); + await expect(testElementLength(".calendar .event.undo", 1)).resolves.toBe(true); }); it("should show two custom icons for repeating events", async () => { - await testElementLength(".calendar .event .fa-undo", 2); + await expect(testElementLength(".calendar .event .fa-undo", 2)).resolves.toBe(true); }); it("should show two custom icons for day events", async () => { - await testElementLength(".calendar .event .fa-calendar-day", 2); + await expect(testElementLength(".calendar .event .fa-calendar-day", 2)).resolves.toBe(true); }); }); @@ -80,7 +83,7 @@ describe("Calendar module", () => { }); it("should show the recurring birthday event 6 times", async () => { - await testElementLength(".calendar .event", 6); + await expect(testElementLength(".calendar .event", 6)).resolves.toBe(true); }); }); @@ -91,7 +94,7 @@ describe("Calendar module", () => { }); it("should show the recurring event 51 times (excluded once) in a 364-day (inclusive) period", async () => { - await testElementLength(".calendar .event", 51); + await expect(testElementLength(".calendar .event", 51)).resolves.toBe(true); }); }); @@ -102,7 +105,7 @@ describe("Calendar module", () => { }); it("should show multiple events with the same title and start time from different calendars", async () => { - await testElementLength(".calendar .event", 22); + await expect(testElementLength(".calendar .event", 22)).resolves.toBe(true); }); }); @@ -118,7 +121,7 @@ describe("Calendar module", () => { }); it(`should contain text "Mar 25th" in timezone UTC ${-i}`, async () => { - await testTextContain(".calendar", "Mar 25th"); + await expect(testTextContain(".calendar", "Mar 25th")).resolves.toBe(true); }); }); } @@ -135,7 +138,7 @@ describe("Calendar module", () => { }); it("should return TestEvents", async () => { - await testElementLength(".calendar .event", 0, "not"); + await expect(testElementLength(".calendar .event", 0, "not")).resolves.toBe(true); }); }); @@ -146,7 +149,7 @@ describe("Calendar module", () => { }); it("should return TestEvents", async () => { - await testElementLength(".calendar .event", 0, "not"); + await expect(testElementLength(".calendar .event", 0, "not")).resolves.toBe(true); }); }); @@ -157,7 +160,7 @@ describe("Calendar module", () => { }); it("should return TestEvents", async () => { - await testElementLength(".calendar .event", 0, "not"); + await expect(testElementLength(".calendar .event", 0, "not")).resolves.toBe(true); }); }); @@ -168,7 +171,7 @@ describe("Calendar module", () => { }); it("should return TestEvents", async () => { - await testElementLength(".calendar .event", 0, "not"); + await expect(testElementLength(".calendar .event", 0, "not")).resolves.toBe(true); }); }); @@ -184,7 +187,7 @@ describe("Calendar module", () => { }); it("should show Unauthorized error", async () => { - await testTextContain(".calendar", "Error in the calendar module. Authorization failed"); + await expect(testTextContain(".calendar", "Error in the calendar module. Authorization failed")).resolves.toBe(true); }); }); }); diff --git a/tests/e2e/modules/clock_es_spec.js b/tests/e2e/modules/clock_es_spec.js index 4bdc4bccf7..134c795b0a 100644 --- a/tests/e2e/modules/clock_es_spec.js +++ b/tests/e2e/modules/clock_es_spec.js @@ -13,12 +13,12 @@ describe("Clock set to spanish language module", () => { it("shows date with correct format", async () => { const dateRegex = /^(?:lunes|martes|miércoles|jueves|viernes|sábado|domingo), \d{1,2} de (?:enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre) de \d{4}$/; - await helpers.testMatch(".clock .date", dateRegex); + await expect(helpers.testMatch(".clock .date", dateRegex)).resolves.toBe(true); }); it("shows time in 24hr format", async () => { const timeRegex = /^(?:2[0-3]|[01]\d):[0-5]\d[0-5]\d$/; - await helpers.testMatch(".clock .time", timeRegex); + await expect(helpers.testMatch(".clock .time", timeRegex)).resolves.toBe(true); }); }); @@ -30,12 +30,12 @@ describe("Clock set to spanish language module", () => { it("shows date with correct format", async () => { const dateRegex = /^(?:lunes|martes|miércoles|jueves|viernes|sábado|domingo), \d{1,2} de (?:enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre) de \d{4}$/; - await helpers.testMatch(".clock .date", dateRegex); + await expect(helpers.testMatch(".clock .date", dateRegex)).resolves.toBe(true); }); it("shows time in 12hr format", async () => { const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[ap]m$/; - await helpers.testMatch(".clock .time", timeRegex); + await expect(helpers.testMatch(".clock .time", timeRegex)).resolves.toBe(true); }); }); @@ -47,7 +47,7 @@ describe("Clock set to spanish language module", () => { it("shows 12hr time with upper case AM/PM", async () => { const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[AP]M$/; - await helpers.testMatch(".clock .time", timeRegex); + await expect(helpers.testMatch(".clock .time", timeRegex)).resolves.toBe(true); }); }); @@ -59,7 +59,7 @@ describe("Clock set to spanish language module", () => { it("shows week with correct format", async () => { const weekRegex = /^Semana [0-9]{1,2}$/; - await helpers.testMatch(".clock .week", weekRegex); + await expect(helpers.testMatch(".clock .week", weekRegex)).resolves.toBe(true); }); }); }); diff --git a/tests/e2e/modules/clock_spec.js b/tests/e2e/modules/clock_spec.js index 03205689c7..651b20de66 100644 --- a/tests/e2e/modules/clock_spec.js +++ b/tests/e2e/modules/clock_spec.js @@ -14,12 +14,12 @@ describe("Clock module", () => { it("should show the date in the correct format", async () => { const dateRegex = /^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}, \d{4}$/; - await helpers.testMatch(".clock .date", dateRegex); + await expect(helpers.testMatch(".clock .date", dateRegex)).resolves.toBe(true); }); it("should show the time in 24hr format", async () => { const timeRegex = /^(?:2[0-3]|[01]\d):[0-5]\d[0-5]\d$/; - await helpers.testMatch(".clock .time", timeRegex); + await expect(helpers.testMatch(".clock .time", timeRegex)).resolves.toBe(true); }); }); @@ -31,12 +31,12 @@ describe("Clock module", () => { it("should show the date in the correct format", async () => { const dateRegex = /^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}, \d{4}$/; - await helpers.testMatch(".clock .date", dateRegex); + await expect(helpers.testMatch(".clock .date", dateRegex)).resolves.toBe(true); }); it("should show the time in 12hr format", async () => { const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[ap]m$/; - await helpers.testMatch(".clock .time", timeRegex); + await expect(helpers.testMatch(".clock .time", timeRegex)).resolves.toBe(true); }); }); @@ -48,7 +48,7 @@ describe("Clock module", () => { it("should show 12hr time with upper case AM/PM", async () => { const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[AP]M$/; - await helpers.testMatch(".clock .time", timeRegex); + await expect(helpers.testMatch(".clock .time", timeRegex)).resolves.toBe(true); }); }); @@ -60,7 +60,7 @@ describe("Clock module", () => { it("should show 12hr time without seconds am/pm", async () => { const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[ap]m$/; - await helpers.testMatch(".clock .time", timeRegex); + await expect(helpers.testMatch(".clock .time", timeRegex)).resolves.toBe(true); }); }); @@ -101,7 +101,7 @@ describe("Clock module", () => { it("should show the week in the correct format", async () => { const weekRegex = /^Week [0-9]{1,2}$/; - await helpers.testMatch(".clock .week", weekRegex); + await expect(helpers.testMatch(".clock .week", weekRegex)).resolves.toBe(true); }); it("should show the week with the correct number of week of year", async () => { diff --git a/tests/e2e/modules/compliments_spec.js b/tests/e2e/modules/compliments_spec.js index e96d34f661..636a7b059a 100644 --- a/tests/e2e/modules/compliments_spec.js +++ b/tests/e2e/modules/compliments_spec.js @@ -4,6 +4,7 @@ describe("Compliments module", () => { /** * move similar tests in function doTest * @param {Array} complimentsArray The array of compliments. + * @returns {boolean} result */ const doTest = async (complimentsArray) => { let elem = await helpers.waitForElement(".compliments"); @@ -11,6 +12,7 @@ describe("Compliments module", () => { elem = await helpers.waitForElement(".module-content"); expect(elem).not.toBeNull(); expect(complimentsArray).toContain(elem.textContent); + return true; }; afterAll(async () => { @@ -25,7 +27,7 @@ describe("Compliments module", () => { }); it("shows anytime because if configure empty parts of day compliments and set anytime compliments", async () => { - await doTest(["Anytime here"]); + await expect(doTest(["Anytime here"])).resolves.toBe(true); }); }); @@ -36,7 +38,7 @@ describe("Compliments module", () => { }); it("shows anytime compliments", async () => { - await doTest(["Anytime here"]); + await expect(doTest(["Anytime here"])).resolves.toBe(true); }); }); }); @@ -48,7 +50,7 @@ describe("Compliments module", () => { }); it("should show compliments from a remote file", async () => { - await doTest(["Remote compliment file works!"]); + await expect(doTest(["Remote compliment file works!"])).resolves.toBe(true); }); }); }); diff --git a/tests/e2e/modules/weather_current_spec.js b/tests/e2e/modules/weather_current_spec.js index 5ba30fc5b6..cc9ea38f52 100644 --- a/tests/e2e/modules/weather_current_spec.js +++ b/tests/e2e/modules/weather_current_spec.js @@ -15,15 +15,15 @@ describe("Weather module", () => { }); it("should render wind speed and wind direction", async () => { - await weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "12 WSW"); + await expect(weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "12 WSW")).resolves.toBe(true); }); it("should render temperature with icon", async () => { - await weatherFunc.getText(".weather .large.light span.bright", "1.5°"); + await expect(weatherFunc.getText(".weather .large.light span.bright", "1.5°")).resolves.toBe(true); }); it("should render feels like temperature", async () => { - await weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like -5.6°"); + await expect(weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like -5.6°")).resolves.toBe(true); }); }); }); @@ -34,7 +34,7 @@ describe("Weather module", () => { }); it("should render a compliment based on the current weather", async () => { - await weatherFunc.getText(".compliments .module-content span", "snow"); + await expect(weatherFunc.getText(".compliments .module-content span", "snow")).resolves.toBe(true); }); }); @@ -44,7 +44,7 @@ describe("Weather module", () => { }); it("should render windUnits in beaufort", async () => { - await weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "6"); + await expect(weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "6")).resolves.toBe(true); }); it("should render windDirection with an arrow", async () => { @@ -54,15 +54,15 @@ describe("Weather module", () => { }); it("should render humidity", async () => { - await weatherFunc.getText(".weather .normal.medium span:nth-child(3)", "93.7"); + await expect(weatherFunc.getText(".weather .normal.medium span:nth-child(3)", "93.7")).resolves.toBe(true); }); it("should render degreeLabel for temp", async () => { - await weatherFunc.getText(".weather .large.light span.bright", "1°C"); + await expect(weatherFunc.getText(".weather .large.light span.bright", "1°C")).resolves.toBe(true); }); it("should render degreeLabel for feels like", async () => { - await weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like -6°C"); + await expect(weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like -6°C")).resolves.toBe(true); }); }); @@ -72,15 +72,15 @@ describe("Weather module", () => { }); it("should render wind in imperial units", async () => { - await weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "26 WSW"); + await expect(weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "26 WSW")).resolves.toBe(true); }); it("should render temperatures in fahrenheit", async () => { - await weatherFunc.getText(".weather .large.light span.bright", "34,7°"); + await expect(weatherFunc.getText(".weather .large.light span.bright", "34,7°")).resolves.toBe(true); }); it("should render 'feels like' in fahrenheit", async () => { - await weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like 21,9°"); + await expect(weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like 21,9°")).resolves.toBe(true); }); }); }); diff --git a/tests/e2e/modules/weather_forecast_spec.js b/tests/e2e/modules/weather_forecast_spec.js index eb5dfc21de..da4c918875 100644 --- a/tests/e2e/modules/weather_forecast_spec.js +++ b/tests/e2e/modules/weather_forecast_spec.js @@ -16,7 +16,7 @@ describe("Weather module: Weather Forecast", () => { const days = ["Today", "Tomorrow", "Sun", "Mon", "Tue"]; for (const [index, day] of days.entries()) { it(`should render day ${day}`, async () => { - await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day); + await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day)).resolves.toBe(true); }); } @@ -31,14 +31,14 @@ describe("Weather module: Weather Forecast", () => { const maxTemps = ["24.4°", "21.0°", "22.9°", "23.4°", "20.6°"]; for (const [index, temp] of maxTemps.entries()) { it(`should render max temperature ${temp}`, async () => { - await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(3)`, temp); + await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(3)`, temp)).resolves.toBe(true); }); } const minTemps = ["15.3°", "13.6°", "13.8°", "13.9°", "10.9°"]; for (const [index, temp] of minTemps.entries()) { it(`should render min temperature ${temp}`, async () => { - await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(4)`, temp); + await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(4)`, temp)).resolves.toBe(true); }); } @@ -60,7 +60,7 @@ describe("Weather module: Weather Forecast", () => { const days = ["Fri", "Sat", "Sun", "Mon", "Tue"]; for (const [index, day] of days.entries()) { it(`should render day ${day}`, async () => { - await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day); + await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day)).resolves.toBe(true); }); } }); @@ -86,7 +86,7 @@ describe("Weather module: Weather Forecast", () => { for (const [index, precipitation] of precipitations.entries()) { if (precipitation) { it(`should render precipitation amount ${precipitation}`, async () => { - await weatherFunc.getText(`.weather table tr:nth-child(${index + 1}) td.precipitation-amount`, precipitation); + await expect(weatherFunc.getText(`.weather table tr:nth-child(${index + 1}) td.precipitation-amount`, precipitation)).resolves.toBe(true); }); } } @@ -101,7 +101,7 @@ describe("Weather module: Weather Forecast", () => { const temperatures = ["75_9°", "69_8°", "73_2°", "74_1°", "69_1°"]; for (const [index, temp] of temperatures.entries()) { it(`should render custom decimalSymbol = '_' for temp ${temp}`, async () => { - await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(3)`, temp); + await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(3)`, temp)).resolves.toBe(true); }); } }); @@ -111,7 +111,7 @@ describe("Weather module: Weather Forecast", () => { for (const [index, precipitation] of precipitations.entries()) { if (precipitation) { it(`should render precipitation amount ${precipitation}`, async () => { - await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.precipitation-amount`, precipitation); + await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.precipitation-amount`, precipitation)).resolves.toBe(true); }); } } diff --git a/tests/e2e/modules/weather_hourly_spec.js b/tests/e2e/modules/weather_hourly_spec.js index 87fc4411cb..f61cee0546 100644 --- a/tests/e2e/modules/weather_hourly_spec.js +++ b/tests/e2e/modules/weather_hourly_spec.js @@ -16,7 +16,7 @@ describe("Weather module: Weather Hourly Forecast", () => { const minTemps = ["7:00 pm", "8:00 pm", "9:00 pm", "10:00 pm", "11:00 pm"]; for (const [index, hour] of minTemps.entries()) { it(`should render forecast for hour ${hour}`, async () => { - await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.day`, hour); + await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.day`, hour)).resolves.toBe(true); }); } }); @@ -30,7 +30,7 @@ describe("Weather module: Weather Hourly Forecast", () => { const minTemps = ["7:00 pm", "9:00 pm", "11:00 pm", "1:00 am", "3:00 am"]; for (const [index, hour] of minTemps.entries()) { it(`should render forecast for hour ${hour}`, async () => { - await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.day`, hour); + await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.day`, hour)).resolves.toBe(true); }); } }); @@ -46,7 +46,7 @@ describe("Weather module: Weather Hourly Forecast", () => { for (const [index, amount] of amounts.entries()) { if (amount) { it(`should render precipitation amount ${amount}`, async () => { - await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.precipitation-amount`, amount); + await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.precipitation-amount`, amount)).resolves.toBe(true); }); } } @@ -57,7 +57,7 @@ describe("Weather module: Weather Hourly Forecast", () => { for (const [index, pop] of propabilities.entries()) { if (pop) { it(`should render probability ${pop}`, async () => { - await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.precipitation-prob`, pop); + await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.precipitation-prob`, pop)).resolves.toBe(true); }); } } diff --git a/tests/e2e/translations_spec.js b/tests/e2e/translations_spec.js index 14e6a75ce7..212d438646 100644 --- a/tests/e2e/translations_spec.js +++ b/tests/e2e/translations_spec.js @@ -44,74 +44,82 @@ describe("Translations", () => { ); }); - it("should load translation file", (done) => { - dom.window.onload = async () => { - const { Translator, Module, config } = dom.window; - config.language = "en"; - Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); + it("should load translation file", () => { + return new Promise((done) => { + dom.window.onload = async () => { + const { Translator, Module, config } = dom.window; + config.language = "en"; + Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); - Module.register("name", { getTranslations: () => translations }); - const MMM = Module.create("name"); + Module.register("name", { getTranslations: () => translations }); + const MMM = Module.create("name"); - await MMM.loadTranslations(); + await MMM.loadTranslations(); - expect(Translator.load.args).toHaveLength(1); - expect(Translator.load.calledWith(MMM, "translations/en.json", false)).toBe(true); + expect(Translator.load.args).toHaveLength(1); + expect(Translator.load.calledWith(MMM, "translations/en.json", false)).toBe(true); - done(); - }; + done(); + }; + }); }); - it("should load translation + fallback file", (done) => { - dom.window.onload = async () => { - const { Translator, Module } = dom.window; - Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); + it("should load translation + fallback file", () => { + return new Promise((done) => { + dom.window.onload = async () => { + const { Translator, Module } = dom.window; + Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); - Module.register("name", { getTranslations: () => translations }); - const MMM = Module.create("name"); + Module.register("name", { getTranslations: () => translations }); + const MMM = Module.create("name"); - await MMM.loadTranslations(); + await MMM.loadTranslations(); - expect(Translator.load.args).toHaveLength(2); - expect(Translator.load.calledWith(MMM, "translations/de.json", false)).toBe(true); - expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true); + expect(Translator.load.args).toHaveLength(2); + expect(Translator.load.calledWith(MMM, "translations/de.json", false)).toBe(true); + expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true); - done(); - }; + done(); + }; + }); }); - it("should load translation fallback file", (done) => { - dom.window.onload = async () => { - const { Translator, Module, config } = dom.window; - config.language = "--"; - Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); + it("should load translation fallback file", () => { + return new Promise((done) => { + dom.window.onload = async () => { + const { Translator, Module, config } = dom.window; + config.language = "--"; + Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null); - Module.register("name", { getTranslations: () => translations }); - const MMM = Module.create("name"); + Module.register("name", { getTranslations: () => translations }); + const MMM = Module.create("name"); - await MMM.loadTranslations(); + await MMM.loadTranslations(); - expect(Translator.load.args).toHaveLength(1); - expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true); + expect(Translator.load.args).toHaveLength(1); + expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true); - done(); - }; + done(); + }; + }); }); - it("should load no file", (done) => { - dom.window.onload = async () => { - const { Translator, Module } = dom.window; - Translator.load = sinon.stub(); + it("should load no file", () => { + return new Promise((done) => { + dom.window.onload = async () => { + const { Translator, Module } = dom.window; + Translator.load = sinon.stub(); - Module.register("name", {}); - const MMM = Module.create("name"); + Module.register("name", {}); + const MMM = Module.create("name"); - await MMM.loadTranslations(); + await MMM.loadTranslations(); - expect(Translator.load.callCount).toBe(0); + expect(Translator.load.callCount).toBe(0); - done(); - }; + done(); + }; + }); }); }); @@ -124,20 +132,22 @@ describe("Translations", () => { describe("Parsing language files through the Translator class", () => { for (let language in translations) { - it(`should parse ${language}`, (done) => { - const dom = new JSDOM( - `\ + it(`should parse ${language}`, () => { + return new Promise((done) => { + const dom = new JSDOM( + `\ \ + beforeAll(() => { + return new Promise((done) => { + const dom = new JSDOM( + `\ \ + beforeAll(() => { + return new Promise((done) => { + const dom = new JSDOM( + `\ \ + beforeAll(() => { + return new Promise((done) => { + dom = new JSDOM( + `\ \ + it("should load core translations and fallback", () => { + return new Promise((done) => { + const dom = new JSDOM( + `\ \ + it("should load core fallback if language cannot be found", () => { + return new Promise((done) => { + const dom = new JSDOM( + `\ \ + it("should load core translations fallback", () => { + return new Promise((done) => { + const dom = new JSDOM( + `\ \ + it("should load core fallback if language cannot be found", () => { + return new Promise((done) => { + const dom = new JSDOM( + `\ \ + beforeAll(() => { + return new Promise((done) => { + const dom = new JSDOM( + `\