From 7694d6fa867ebeee4695667e2561da0a7ff11622 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Sat, 1 Oct 2022 20:14:26 +0200 Subject: [PATCH 001/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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": {