From d3e47b91a162307169f19bffb7742fcc75dda852 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Mon, 24 Jun 2024 18:34:02 -0500 Subject: [PATCH 01/20] add cron type date/time entries as an option in compliments module --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba1a3c8baf..da82a9ebd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ _This release is scheduled to be released on 2024-07-01._ ### Added +- [compliments] Added support to filter out anytime/morning/afternoon/evening events if a special date format event triggers, specialDayUnique:true +- [compliments] Added support for cron type date/time format entries.. mm hh DD MM dow (minutes/hours/days/months and day of week) see https://crontab.cronhub.io for consuction + ### Removed - [test suite] delete node v18 support From fd105f51d344dcfb5716f5c7e0aec7249ac2c2f8 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Mon, 24 Jun 2024 21:00:56 -0500 Subject: [PATCH 02/20] add support for cron type date/time events in compliments --- CHANGELOG.md | 2 +- modules/default/compliments/compliments.js | 90 ++++++++++++++++--- .../compliments/compliments_cron_entry.js | 19 ++++ .../compliments_specialDayUnique_false.js | 23 +++++ .../compliments_specialDayUnique_true.js | 23 +++++ tests/electron/modules/compliments_spec.js | 36 ++++++++ vendor/package-lock.json | 9 ++ vendor/package.json | 1 + vendor/vendor.js | 3 +- 9 files changed, 191 insertions(+), 15 deletions(-) create mode 100644 tests/configs/modules/compliments/compliments_cron_entry.js create mode 100644 tests/configs/modules/compliments/compliments_specialDayUnique_false.js create mode 100644 tests/configs/modules/compliments/compliments_specialDayUnique_true.js diff --git a/CHANGELOG.md b/CHANGELOG.md index da82a9ebd5..46824f54f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ _This release is scheduled to be released on 2024-07-01._ ### Added - [compliments] Added support to filter out anytime/morning/afternoon/evening events if a special date format event triggers, specialDayUnique:true -- [compliments] Added support for cron type date/time format entries.. mm hh DD MM dow (minutes/hours/days/months and day of week) see https://crontab.cronhub.io for consuction +- [compliments] Added support for cron type date/time format entries.. mm hh DD MM dow (minutes/hours/days/months and day of week) see https://crontab.cronhub.io for construction ### Removed diff --git a/modules/default/compliments/compliments.js b/modules/default/compliments/compliments.js index 39e6c34d49..ec4bb399e0 100644 --- a/modules/default/compliments/compliments.js +++ b/modules/default/compliments/compliments.js @@ -1,3 +1,9 @@ +/* MagicMirror² + * Module: Compliments + * + * By Michael Teeuw https://michaelteeuw.nl + * MIT Licensed. + */ Module.register("compliments", { // Module config defaults. defaults: { @@ -15,15 +21,18 @@ Module.register("compliments", { morningEndTime: 12, afternoonStartTime: 12, afternoonEndTime: 17, - random: true + random: true, + specialDayUnique: false }, lastIndexUsed: -1, // Set currentweather from module currentWeatherType: "", - + cron_regex: /^(((\d+,)+\d+|((\d+|[*])[/]\d+|((JAN|FEB|APR|MA[RY]|JU[LN]|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|APR|MA[RY]|JU[LN]|AUG|SEP|OCT|NOV|DEC))?))|(\d+-\d+)|\d+(-\d+)?[/]\d+(-\d+)?|\d+|[*]|(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?) ?){5}$/i, + date_regex: "[1-9.][0-9.][0-9.]{2}-([0][1-9]|[1][0-2])-([1-2][0-9]|[0][1-9]|[3][0-1])", + pre_defined_types: ["anytime", "morning", "afternoon", "evening"], // Define required scripts. getScripts () { - return ["moment.js"]; + return ["croner.js", "moment.js"]; }, // Define start sequence. @@ -37,11 +46,31 @@ Module.register("compliments", { this.config.compliments = JSON.parse(response); this.updateDom(); } + let minute_sync_delay = 60 - (moment().second()); + // Schedule update timer. sync to the minute start, so minute based events happen on the minute start + setTimeout(() => { + setInterval(() => { + this.updateDom(this.config.fadeSpeed); + }, this.config.updateInterval); }, + minute_sync_delay * 1000); + }, + + /** + * @param {string} cronExpression The cron expression. See https://croner.56k.guru/usage/pattern/ + * @param {Date} [timestamp] The timestamp to check. Defaults to the current time. + * @returns {number} The number of seconds until the next cron run. + */ + getSecondsUntilNextCronRun (cronExpression, timestamp = new Date()) { + // Required for seconds precision + const adjustedTimestamp = new Date(timestamp.getTime() - 1000); + + // https://www.npmjs.com/package/croner + /*eslint-disable-next-line*/ + const cronJob = new Cron(cronExpression); + const nextRunTime = cronJob.nextRun(adjustedTimestamp); - // Schedule update timer. - setInterval(() => { - this.updateDom(this.config.fadeSpeed); - }, this.config.updateInterval); + let secondsDelta = (nextRunTime - adjustedTimestamp) / 1000; + return secondsDelta; }, /** @@ -50,7 +79,7 @@ Module.register("compliments", { * @returns {number} a random index of given array */ randomIndex (compliments) { - if (compliments.length <= 1) { + if (compliments.length === 1) { return 0; } @@ -74,8 +103,9 @@ Module.register("compliments", { * @returns {string[]} array with compliments for the time of the day. */ complimentArray () { - const hour = moment().hour(); - const date = moment().format("YYYY-MM-DD"); + const now = moment(); + const hour = now.hour(); + const date = now.format("YYYY-MM-DD"); let compliments = []; // Add time of day compliments @@ -95,11 +125,45 @@ Module.register("compliments", { // Add compliments for anytime Array.prototype.push.apply(compliments, this.config.compliments.anytime); + + // get the list of just date entry keys + let temp_list = Object.keys(this.config.compliments).filter((k) => { + if (this.pre_defined_types.includes(k)) return false; + else return true; + }); + + let date_compliments = []; // Add compliments for special days - for (let entry in this.config.compliments) { - if (new RegExp(entry).test(date)) { - Array.prototype.push.apply(compliments, this.config.compliments[entry]); + for (let entry of temp_list) { + // check if this could be a cron type entry + if (entry.includes(" ")) { + // make sure the regex is valid + if (new RegExp(this.cron_regex).test(entry)) { + // check if we are in the time range for the cron entry + if (this.getSecondsUntilNextCronRun(entry, now.set("seconds", 0).toDate()) <= 1) { + // if so, use its notice entries + Array.prototype.push.apply(date_compliments, this.config.compliments[entry]); + } + } else Log.error(`compliments cron syntax invalid=${JSON.stringify(entry)}`); + } else if (new RegExp(entry).test(date)) { + Array.prototype.push.apply(date_compliments, this.config.compliments[entry]); + } + } + + // if we found any date compliments + if (date_compliments.length) { + // and the special flag is true + if (this.config.specialDayUnique) { + // clear the non-date compliments if any + compliments.length = 0; } + // put the date based compliments on the list + Array.prototype.push.apply(compliments, date_compliments); + } + // compliments list cannot be empty + if (compliments.length === 0) { + // add a blank one + compliments.push(" "); } return compliments; diff --git a/tests/configs/modules/compliments/compliments_cron_entry.js b/tests/configs/modules/compliments/compliments_cron_entry.js new file mode 100644 index 0000000000..764da08aa6 --- /dev/null +++ b/tests/configs/modules/compliments/compliments_cron_entry.js @@ -0,0 +1,19 @@ +let config = { + modules: [ + { + module: "compliments", + position: "middle_center", + config: { + updateInterval: 1000 * 5, // Update every 5 secs + specialDayUnique: true, + compliments: { + anytime: ["just a test"], + "00-10 16-19 * * fri": ["just pub time"] + } + } + } + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { module.exports = config; } diff --git a/tests/configs/modules/compliments/compliments_specialDayUnique_false.js b/tests/configs/modules/compliments/compliments_specialDayUnique_false.js new file mode 100644 index 0000000000..137eb32b07 --- /dev/null +++ b/tests/configs/modules/compliments/compliments_specialDayUnique_false.js @@ -0,0 +1,23 @@ +let config = { + modules: [ + { + module: "compliments", + position: "middle_center", + config: { + updateInterval: 1000 * 5, // Update every 5 secs + specialDayUnique: false, + compliments: { + anytime: [ + "Typical message 1", + "Typical message 2", + "Typical message 3" + ], + "....-05-06": ["Special day message"] + } + } + } + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { module.exports = config; } diff --git a/tests/configs/modules/compliments/compliments_specialDayUnique_true.js b/tests/configs/modules/compliments/compliments_specialDayUnique_true.js new file mode 100644 index 0000000000..36f68b2f8d --- /dev/null +++ b/tests/configs/modules/compliments/compliments_specialDayUnique_true.js @@ -0,0 +1,23 @@ +let config = { + modules: [ + { + module: "compliments", + position: "middle_center", + config: { + updateInterval: 1000 * 5, // Update every 5 secs + specialDayUnique: true, + compliments: { + anytime: [ + "Typical message 1", + "Typical message 2", + "Typical message 3" + ], + "....-05-06": ["Special day message"] + } + } + } + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { module.exports = config; } diff --git a/tests/electron/modules/compliments_spec.js b/tests/electron/modules/compliments_spec.js index b4c4ed845b..ac162fd9ce 100644 --- a/tests/electron/modules/compliments_spec.js +++ b/tests/electron/modules/compliments_spec.js @@ -43,5 +43,41 @@ describe("Compliments module", () => { await expect(doTest(["Happy new year!"])).resolves.toBe(true); }); }); + + describe("Test only custom date events shown with new property", () => { + it("shows 'Special day message' on May 6", async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_specialDayUnique_true.js", "06 May 2022 10:00:00 GMT"); + await expect(doTest(["Special day message"])).resolves.toBe(true); + }); + }); + + describe("Test all date events shown without neww property", () => { + it("shows 'any message' on May 6", async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_specialDayUnique_false.js", "06 May 2022 10:00:00 GMT"); + await expect(doTest(["Special day message", "Typical message 1", "Typical message 2", "Typical message 3"])).resolves.toBe(true); + }); + }); + + describe("Test only custom cron date event shown with new property", () => { + it("shows 'any message' on May 6", async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "06 May 2022 17:03:00 GMT"); + await expect(doTest(["just pub time"])).resolves.toBe(true); + }); + }); + + describe("Test any event shows after time window", () => { + it("shows 'any message' on May 6", async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "06 May 2022 17:11:00 GMT"); + await expect(doTest(["just a test"])).resolves.toBe(true); + }); + }); + + describe("Test any event shows different day", () => { + it("shows 'any message' on May 5", async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "05 May 2022 17:00:00 GMT"); + await expect(doTest(["just a test"])).resolves.toBe(true); + }); + }); + }); }); diff --git a/vendor/package-lock.json b/vendor/package-lock.json index 2df570b22a..058dae08df 100644 --- a/vendor/package-lock.json +++ b/vendor/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@fortawesome/fontawesome-free": "^6.5.2", "animate.css": "^4.1.1", + "croner": "^8.0.2", "moment": "^2.30.1", "moment-timezone": "^0.5.45", "nunjucks": "^3.2.4", @@ -50,6 +51,14 @@ "node": ">= 6" } }, + "node_modules/croner": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/croner/-/croner-8.0.2.tgz", + "integrity": "sha512-HgSdlSUX8mIgDTTiQpWUP4qY4IFRMsduPCYdca34Pelt8MVdxdaDOzreFtCscA6R+cRZd7UbD1CD3uyx6J3X1A==", + "engines": { + "node": ">=18.0" + } + }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", diff --git a/vendor/package.json b/vendor/package.json index efaa4f151e..e61f8bf380 100644 --- a/vendor/package.json +++ b/vendor/package.json @@ -13,6 +13,7 @@ "dependencies": { "@fortawesome/fontawesome-free": "^6.5.2", "animate.css": "^4.1.1", + "croner": "^8.0.2", "moment": "^2.30.1", "moment-timezone": "^0.5.45", "nunjucks": "^3.2.4", diff --git a/vendor/vendor.js b/vendor/vendor.js index b2d912d4de..ee3f0bb777 100644 --- a/vendor/vendor.js +++ b/vendor/vendor.js @@ -5,7 +5,8 @@ const vendor = { "weather-icons-wind.css": "node_modules/weathericons/css/weather-icons-wind.css", "font-awesome.css": "css/font-awesome.css", "nunjucks.js": "node_modules/nunjucks/browser/nunjucks.min.js", - "suncalc.js": "node_modules/suncalc/suncalc.js" + "suncalc.js": "node_modules/suncalc/suncalc.js", + "croner.js": "node_modules/croner/dist/croner.umd.min.js" }; if (typeof module !== "undefined") { From 83082fb4be4b12f6e3c482ff0d35d1f8284c7ecd Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Mon, 24 Jun 2024 21:14:19 -0500 Subject: [PATCH 03/20] Update CHANGELOG.md fix merge problem --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba86c69b59..e500862a59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,13 +13,9 @@ Thanks to: @kleinmantara (to be continued before release) ### Added -<<<<<<< update_compliments -- [compliments] Added support to filter out anytime/morning/afternoon/evening events if a special date format event triggers, specialDayUnique:true - [compliments] Added support for cron type date/time format entries.. mm hh DD MM dow (minutes/hours/days/months and day of week) see https://crontab.cronhub.io for construction -======= - [calendar] Added config option "showEndsOnlyWithDuration" for default calendar - [compliments] Added `specialDayUnique` config option, defaults to `false`. (#3465) ->>>>>>> develop ### Removed From 3773da251e94e49e0a7b75d177b1d15880381ce2 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Mon, 24 Jun 2024 21:16:04 -0500 Subject: [PATCH 04/20] Update compliments.js conflict edit --- modules/default/compliments/compliments.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/default/compliments/compliments.js b/modules/default/compliments/compliments.js index d9add7d6e6..1dee8989fb 100644 --- a/modules/default/compliments/compliments.js +++ b/modules/default/compliments/compliments.js @@ -134,7 +134,6 @@ Module.register("compliments", { let date_compliments = []; // Add compliments for special days -<<<<<<< update_compliments for (let entry of temp_list) { // check if this could be a cron type entry if (entry.includes(" ")) { From c4b704c8c84de7c23f408125185853794eb86769 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Mon, 24 Jun 2024 21:18:10 -0500 Subject: [PATCH 05/20] Update compliments.js fix merge conflict AGAIN.. --- modules/default/compliments/compliments.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/modules/default/compliments/compliments.js b/modules/default/compliments/compliments.js index 1dee8989fb..ec4bb399e0 100644 --- a/modules/default/compliments/compliments.js +++ b/modules/default/compliments/compliments.js @@ -156,15 +156,6 @@ Module.register("compliments", { if (this.config.specialDayUnique) { // clear the non-date compliments if any compliments.length = 0; -======= - for (let entry in this.config.compliments) { - if (new RegExp(entry).test(date)) { - // Only display compliments configured for the day if specialDayUnique is set to true - if (this.config.specialDayUnique) { - compliments.length = 0; - } - Array.prototype.push.apply(compliments, this.config.compliments[entry]); ->>>>>>> develop } // put the date based compliments on the list Array.prototype.push.apply(compliments, date_compliments); From af0f2a79bed9d5cfafa5605b6f982cfe99a1312f Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Mon, 24 Jun 2024 21:33:40 -0500 Subject: [PATCH 06/20] fix e2e tests, and typo in electron tests --- tests/e2e/modules/compliments_spec.js | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/e2e/modules/compliments_spec.js b/tests/e2e/modules/compliments_spec.js index 0604dab49f..c4d19bee78 100644 --- a/tests/e2e/modules/compliments_spec.js +++ b/tests/e2e/modules/compliments_spec.js @@ -54,4 +54,41 @@ describe("Compliments module", () => { await expect(doTest(["Remote compliment file works!"])).resolves.toBe(true); }); }); + + describe("Feature date in compliments module", () => { + describe("Test only custom date events shown with new property", () => { + it("shows 'Special day message' on May 6", async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_specialDayUnique_true.js", "06 May 2022 10:00:00 GMT"); + await expect(doTest(["Special day message"])).resolves.toBe(true); + }); + }); + + describe("Test all date events shown without neww property", () => { + it("shows 'any message' on May 6", async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_specialDayUnique_false.js", "06 May 2022 10:00:00 GMT"); + await expect(doTest(["Special day message", "Typical message 1", "Typical message 2", "Typical message 3"])).resolves.toBe(true); + }); + }); + + describe("Test only custom cron date event shown with new property", () => { + it("shows 'any message' on May 6", async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "06 May 2022 17:03:00 GMT"); + await expect(doTest(["just pub time"])).resolves.toBe(true); + }); + }); + + describe("Test any event shows after time window", () => { + it("shows 'any message' on May 6", async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "06 May 2022 17:11:00 GMT"); + await expect(doTest(["just a test"])).resolves.toBe(true); + }); + }); + + describe("Test any event shows different day", () => { + it("shows 'any message' on May 5", async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "05 May 2022 17:00:00 GMT"); + await expect(doTest(["just a test"])).resolves.toBe(true); + }); + }); + }); }); From 568fa91ac1a939f1463e13bb1037147e2dc4d4dd Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Mon, 24 Jun 2024 22:09:16 -0500 Subject: [PATCH 07/20] update e2e compliments test --- tests/e2e/modules/compliments_spec.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/e2e/modules/compliments_spec.js b/tests/e2e/modules/compliments_spec.js index cf74bcfed1..a520ebe2f8 100644 --- a/tests/e2e/modules/compliments_spec.js +++ b/tests/e2e/modules/compliments_spec.js @@ -59,6 +59,7 @@ describe("Compliments module", () => { describe("Test only custom date events shown with new property", () => { it("shows 'Special day message' on May 6", async () => { await helpers.startApplication("tests/configs/modules/compliments/compliments_specialDayUnique_true.js", "06 May 2022 10:00:00 GMT"); + await helpers.getDocument(); await expect(doTest(["Special day message"])).resolves.toBe(true); }); }); @@ -66,6 +67,7 @@ describe("Compliments module", () => { describe("Test all date events shown without new property", () => { it("shows 'any message' on May 6", async () => { await helpers.startApplication("tests/configs/modules/compliments/compliments_specialDayUnique_false.js", "06 May 2022 10:00:00 GMT"); + await helpers.getDocument(); await expect(doTest(["Special day message", "Typical message 1", "Typical message 2", "Typical message 3"])).resolves.toBe(true); }); }); @@ -73,6 +75,7 @@ describe("Compliments module", () => { describe("Test only custom cron date event shown with new property", () => { it("shows 'any message' on May 6", async () => { await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "06 May 2022 17:03:00 GMT"); + await helpers.getDocument(); await expect(doTest(["just pub time"])).resolves.toBe(true); }); }); @@ -80,6 +83,7 @@ describe("Compliments module", () => { describe("Test any event shows after time window", () => { it("shows 'any message' on May 6", async () => { await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "06 May 2022 17:11:00 GMT"); + await helpers.getDocument(); await expect(doTest(["just a test"])).resolves.toBe(true); }); }); @@ -87,6 +91,7 @@ describe("Compliments module", () => { describe("Test any event shows different day", () => { it("shows 'any message' on May 5", async () => { await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "05 May 2022 17:00:00 GMT"); + await helpers.getDocument(); await expect(doTest(["just a test"])).resolves.toBe(true); }); }); From b1c81e22c8a50cd901ddf059491b7e1ad3feb1f8 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Tue, 25 Jun 2024 10:34:08 -0500 Subject: [PATCH 08/20] comment out e2e testcases with date as helper doesn't accept date on startApplication() --- tests/e2e/modules/compliments_spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/e2e/modules/compliments_spec.js b/tests/e2e/modules/compliments_spec.js index a520ebe2f8..60f355c18f 100644 --- a/tests/e2e/modules/compliments_spec.js +++ b/tests/e2e/modules/compliments_spec.js @@ -55,6 +55,7 @@ describe("Compliments module", () => { }); }); + /* CAN'T TEST ANY OF THESE AS THE START APPLICATION HELPER IN E2E DOES NOT ACCEPT TIME AS A VALUE describe("Feature date in compliments module", () => { describe("Test only custom date events shown with new property", () => { it("shows 'Special day message' on May 6", async () => { @@ -95,5 +96,5 @@ describe("Compliments module", () => { await expect(doTest(["just a test"])).resolves.toBe(true); }); }); - }); + }); */ }); From 7d38bc02fa5c04bc6823e0b9df4c1e22708b4b49 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Tue, 25 Jun 2024 10:51:16 -0500 Subject: [PATCH 09/20] add coverage for weather based key added dynamically by notification --- modules/default/compliments/compliments.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/default/compliments/compliments.js b/modules/default/compliments/compliments.js index ec4bb399e0..26eb059a25 100644 --- a/modules/default/compliments/compliments.js +++ b/modules/default/compliments/compliments.js @@ -125,7 +125,15 @@ Module.register("compliments", { // Add compliments for anytime Array.prototype.push.apply(compliments, this.config.compliments.anytime); - + // watch out for a weather type being set + // multiple times during our run... + if (this.currentWeatherType.length > 0) { + // if the predefine list doesn't include it (yet) + if (!this.pre_defined_types.includes(this.currentWeatherType)) { + // add it + this.pre_defined_types.push(this.currentWeatherType); + } + } // get the list of just date entry keys let temp_list = Object.keys(this.config.compliments).filter((k) => { if (this.pre_defined_types.includes(k)) return false; From 66aa2ca0ba25da71fd18cf1fff89985a6afa470b Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Tue, 25 Jun 2024 11:13:58 -0500 Subject: [PATCH 10/20] move weather key check to inside existing check, eliminate duplicate check --- modules/default/compliments/compliments.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/modules/default/compliments/compliments.js b/modules/default/compliments/compliments.js index 26eb059a25..c9a12da518 100644 --- a/modules/default/compliments/compliments.js +++ b/modules/default/compliments/compliments.js @@ -120,20 +120,16 @@ Module.register("compliments", { // Add compliments based on weather if (this.currentWeatherType in this.config.compliments) { Array.prototype.push.apply(compliments, this.config.compliments[this.currentWeatherType]); - } - - // Add compliments for anytime - Array.prototype.push.apply(compliments, this.config.compliments.anytime); - - // watch out for a weather type being set - // multiple times during our run... - if (this.currentWeatherType.length > 0) { // if the predefine list doesn't include it (yet) if (!this.pre_defined_types.includes(this.currentWeatherType)) { // add it this.pre_defined_types.push(this.currentWeatherType); } } + + // Add compliments for anytime + Array.prototype.push.apply(compliments, this.config.compliments.anytime); + // get the list of just date entry keys let temp_list = Object.keys(this.config.compliments).filter((k) => { if (this.pre_defined_types.includes(k)) return false; From 9e172282bbf2738cc4d5c9872a05395b2d16ace6 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Wed, 26 Jun 2024 13:12:22 -0500 Subject: [PATCH 11/20] Delete tests/e2e/modules/compliments_spec.js --- tests/e2e/modules/compliments_spec.js | 100 -------------------------- 1 file changed, 100 deletions(-) delete mode 100644 tests/e2e/modules/compliments_spec.js diff --git a/tests/e2e/modules/compliments_spec.js b/tests/e2e/modules/compliments_spec.js deleted file mode 100644 index 60f355c18f..0000000000 --- a/tests/e2e/modules/compliments_spec.js +++ /dev/null @@ -1,100 +0,0 @@ -const helpers = require("../helpers/global-setup"); - -describe("Compliments module", () => { - - /** - * move similar tests in function doTest - * @param {Array} complimentsArray The array of compliments. - * @returns {boolean} result - */ - const doTest = async (complimentsArray) => { - let elem = await helpers.waitForElement(".compliments"); - expect(elem).not.toBeNull(); - elem = await helpers.waitForElement(".module-content"); - expect(elem).not.toBeNull(); - expect(complimentsArray).toContain(elem.textContent); - return true; - }; - - afterAll(async () => { - await helpers.stopApplication(); - }); - - describe("Feature anytime in compliments module", () => { - describe("Set anytime and empty compliments for morning, evening and afternoon", () => { - beforeAll(async () => { - await helpers.startApplication("tests/configs/modules/compliments/compliments_anytime.js"); - await helpers.getDocument(); - }); - - it("shows anytime because if configure empty parts of day compliments and set anytime compliments", async () => { - await expect(doTest(["Anytime here"])).resolves.toBe(true); - }); - }); - - describe("Only anytime present in configuration compliments", () => { - beforeAll(async () => { - await helpers.startApplication("tests/configs/modules/compliments/compliments_only_anytime.js"); - await helpers.getDocument(); - }); - - it("shows anytime compliments", async () => { - await expect(doTest(["Anytime here"])).resolves.toBe(true); - }); - }); - }); - - describe("remoteFile option", () => { - beforeAll(async () => { - await helpers.startApplication("tests/configs/modules/compliments/compliments_remote.js"); - await helpers.getDocument(); - }); - - it("should show compliments from a remote file", async () => { - await expect(doTest(["Remote compliment file works!"])).resolves.toBe(true); - }); - }); - - /* CAN'T TEST ANY OF THESE AS THE START APPLICATION HELPER IN E2E DOES NOT ACCEPT TIME AS A VALUE - describe("Feature date in compliments module", () => { - describe("Test only custom date events shown with new property", () => { - it("shows 'Special day message' on May 6", async () => { - await helpers.startApplication("tests/configs/modules/compliments/compliments_specialDayUnique_true.js", "06 May 2022 10:00:00 GMT"); - await helpers.getDocument(); - await expect(doTest(["Special day message"])).resolves.toBe(true); - }); - }); - - describe("Test all date events shown without new property", () => { - it("shows 'any message' on May 6", async () => { - await helpers.startApplication("tests/configs/modules/compliments/compliments_specialDayUnique_false.js", "06 May 2022 10:00:00 GMT"); - await helpers.getDocument(); - await expect(doTest(["Special day message", "Typical message 1", "Typical message 2", "Typical message 3"])).resolves.toBe(true); - }); - }); - - describe("Test only custom cron date event shown with new property", () => { - it("shows 'any message' on May 6", async () => { - await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "06 May 2022 17:03:00 GMT"); - await helpers.getDocument(); - await expect(doTest(["just pub time"])).resolves.toBe(true); - }); - }); - - describe("Test any event shows after time window", () => { - it("shows 'any message' on May 6", async () => { - await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "06 May 2022 17:11:00 GMT"); - await helpers.getDocument(); - await expect(doTest(["just a test"])).resolves.toBe(true); - }); - }); - - describe("Test any event shows different day", () => { - it("shows 'any message' on May 5", async () => { - await helpers.startApplication("tests/configs/modules/compliments/compliments_cron_entry.js", "05 May 2022 17:00:00 GMT"); - await helpers.getDocument(); - await expect(doTest(["just a test"])).resolves.toBe(true); - }); - }); - }); */ -}); From 8836e8fd2c4f6b5f9b732ce9708de693d82d24e5 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Wed, 26 Jun 2024 18:40:11 -0500 Subject: [PATCH 12/20] fix startup delay for cron entries to pass weather test --- modules/default/compliments/compliments.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/modules/default/compliments/compliments.js b/modules/default/compliments/compliments.js index c9a12da518..087e5f0bfd 100644 --- a/modules/default/compliments/compliments.js +++ b/modules/default/compliments/compliments.js @@ -46,13 +46,22 @@ Module.register("compliments", { this.config.compliments = JSON.parse(response); this.updateDom(); } - let minute_sync_delay = 60 - (moment().second()); - // Schedule update timer. sync to the minute start, so minute based events happen on the minute start + let minute_sync_delay = 1; + // loop thru all the configured when events + for (let m of Object.keys(this.config.compliments)) { + // if one contains a space, its a cron entry + if (m.includes(" ")) { + // we need to synch our interval cycle to the minute + minute_sync_delay = (60 - (moment().second())) * 1000; + break; + } + } + // Schedule update timer. sync to the minute start (if needed), so minute based events happen on the minute start setTimeout(() => { setInterval(() => { this.updateDom(this.config.fadeSpeed); }, this.config.updateInterval); }, - minute_sync_delay * 1000); + minute_sync_delay); }, /** @@ -164,11 +173,6 @@ Module.register("compliments", { // put the date based compliments on the list Array.prototype.push.apply(compliments, date_compliments); } - // compliments list cannot be empty - if (compliments.length === 0) { - // add a blank one - compliments.push(" "); - } return compliments; }, From 4b37d9e05f641f8786767eef2b85b1c6f8c8d35b Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 27 Jun 2024 14:56:03 -0500 Subject: [PATCH 13/20] remove unneccessary updateInterval from testcases for pr #3471 --- .../modules/compliments/compliments_specialDayUnique_false.js | 1 - .../modules/compliments/compliments_specialDayUnique_true.js | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/configs/modules/compliments/compliments_specialDayUnique_false.js b/tests/configs/modules/compliments/compliments_specialDayUnique_false.js index 137eb32b07..5d7df231f1 100644 --- a/tests/configs/modules/compliments/compliments_specialDayUnique_false.js +++ b/tests/configs/modules/compliments/compliments_specialDayUnique_false.js @@ -4,7 +4,6 @@ let config = { module: "compliments", position: "middle_center", config: { - updateInterval: 1000 * 5, // Update every 5 secs specialDayUnique: false, compliments: { anytime: [ diff --git a/tests/configs/modules/compliments/compliments_specialDayUnique_true.js b/tests/configs/modules/compliments/compliments_specialDayUnique_true.js index 36f68b2f8d..cd148d6937 100644 --- a/tests/configs/modules/compliments/compliments_specialDayUnique_true.js +++ b/tests/configs/modules/compliments/compliments_specialDayUnique_true.js @@ -4,7 +4,6 @@ let config = { module: "compliments", position: "middle_center", config: { - updateInterval: 1000 * 5, // Update every 5 secs specialDayUnique: true, compliments: { anytime: [ From 59c2c4932f42df326dac70a6a4a59c04a2d02018 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 27 Jun 2024 14:58:47 -0500 Subject: [PATCH 14/20] remove header comments --- modules/default/compliments/compliments.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/modules/default/compliments/compliments.js b/modules/default/compliments/compliments.js index 087e5f0bfd..9d43246388 100644 --- a/modules/default/compliments/compliments.js +++ b/modules/default/compliments/compliments.js @@ -1,9 +1,3 @@ -/* MagicMirror² - * Module: Compliments - * - * By Michael Teeuw https://michaelteeuw.nl - * MIT Licensed. - */ Module.register("compliments", { // Module config defaults. defaults: { From c748025a7737c5ad6cf9e957c29f50fdf2c8aebc Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 27 Jun 2024 15:39:25 -0500 Subject: [PATCH 15/20] remove updateInterval cron test config --- tests/configs/modules/compliments/compliments_cron_entry.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/configs/modules/compliments/compliments_cron_entry.js b/tests/configs/modules/compliments/compliments_cron_entry.js index 764da08aa6..59a6659293 100644 --- a/tests/configs/modules/compliments/compliments_cron_entry.js +++ b/tests/configs/modules/compliments/compliments_cron_entry.js @@ -4,7 +4,6 @@ let config = { module: "compliments", position: "middle_center", config: { - updateInterval: 1000 * 5, // Update every 5 secs specialDayUnique: true, compliments: { anytime: ["just a test"], From 71497601243ad1207db303214a6c7e5cacc5797a Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 27 Jun 2024 15:52:07 -0500 Subject: [PATCH 16/20] restore missing file --- tests/e2e/modules/compliments_spec.js | 81 +++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tests/e2e/modules/compliments_spec.js diff --git a/tests/e2e/modules/compliments_spec.js b/tests/e2e/modules/compliments_spec.js new file mode 100644 index 0000000000..95b342577e --- /dev/null +++ b/tests/e2e/modules/compliments_spec.js @@ -0,0 +1,81 @@ +const helpers = require("../helpers/global-setup"); + +describe("Compliments module", () => { + + /** + * move similar tests in function doTest + * @param {Array} complimentsArray The array of compliments. + * @returns {boolean} result + */ + const doTest = async (complimentsArray) => { + let elem = await helpers.waitForElement(".compliments"); + expect(elem).not.toBeNull(); + elem = await helpers.waitForElement(".module-content"); + expect(elem).not.toBeNull(); + expect(complimentsArray).toContain(elem.textContent); + return true; + }; + + afterAll(async () => { + await helpers.stopApplication(); + }); + + describe("Feature anytime in compliments module", () => { + describe("Set anytime and empty compliments for morning, evening and afternoon", () => { + beforeAll(async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_anytime.js"); + await helpers.getDocument(); + }); + + it("shows anytime because if configure empty parts of day compliments and set anytime compliments", async () => { + await expect(doTest(["Anytime here"])).resolves.toBe(true); + }); + }); + + describe("Only anytime present in configuration compliments", () => { + beforeAll(async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_only_anytime.js"); + await helpers.getDocument(); + }); + + it("shows anytime compliments", async () => { + await expect(doTest(["Anytime here"])).resolves.toBe(true); + }); + }); + }); + + describe("remoteFile option", () => { + beforeAll(async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_remote.js"); + await helpers.getDocument(); + }); + + it("should show compliments from a remote file", async () => { + await expect(doTest(["Remote compliment file works!"])).resolves.toBe(true); + }); + }); + + describe("Feature specialDayUnique in compliments module", () => { + describe("specialDayUnique is false", () => { + beforeAll(async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_specialDayUnique_false.js"); + await helpers.getDocument(); + }); + + it("compliments array can contain all values", async () => { + await expect(doTest(["Special day message", "Typical message 1", "Typical message 2", "Typical message 3"])).resolves.toBe(true); + }); + }); + + describe("specialDayUnique is true", () => { + beforeAll(async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_specialDayUnique_true.js"); + await helpers.getDocument(); + }); + + it("compliments array contains only special value", async () => { + await expect(doTest(["Special day message"])).resolves.toBe(true); + }); + }); + }); +}); From 5b6542e4cac6b3f5905b762f6adb05dd08a6324f Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 27 Jun 2024 16:19:58 -0500 Subject: [PATCH 17/20] restore date mask for non-date-time e2e tests --- .../modules/compliments/compliments_specialDayUnique_false.js | 2 +- .../modules/compliments/compliments_specialDayUnique_true.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/configs/modules/compliments/compliments_specialDayUnique_false.js b/tests/configs/modules/compliments/compliments_specialDayUnique_false.js index 5d7df231f1..e4cb64c6ab 100644 --- a/tests/configs/modules/compliments/compliments_specialDayUnique_false.js +++ b/tests/configs/modules/compliments/compliments_specialDayUnique_false.js @@ -11,7 +11,7 @@ let config = { "Typical message 2", "Typical message 3" ], - "....-05-06": ["Special day message"] + "....-..-..": ["Special day message"] } } } diff --git a/tests/configs/modules/compliments/compliments_specialDayUnique_true.js b/tests/configs/modules/compliments/compliments_specialDayUnique_true.js index cd148d6937..3d578611c8 100644 --- a/tests/configs/modules/compliments/compliments_specialDayUnique_true.js +++ b/tests/configs/modules/compliments/compliments_specialDayUnique_true.js @@ -11,7 +11,7 @@ let config = { "Typical message 2", "Typical message 3" ], - "....-05-06": ["Special day message"] + "....-..-..": ["Special day message"] } } } From dd1d4a8685803fa9faeea09c9a8b640b81931e52 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Thu, 27 Jun 2024 16:32:32 -0500 Subject: [PATCH 18/20] add anytime cron test case as server date doesn't work --- .../compliments/compliments_e2e_cron_entry.js | 18 ++++++++++++++++++ tests/e2e/modules/compliments_spec.js | 11 +++++++++++ 2 files changed, 29 insertions(+) create mode 100644 tests/configs/modules/compliments/compliments_e2e_cron_entry.js diff --git a/tests/configs/modules/compliments/compliments_e2e_cron_entry.js b/tests/configs/modules/compliments/compliments_e2e_cron_entry.js new file mode 100644 index 0000000000..a4d1c5d8f2 --- /dev/null +++ b/tests/configs/modules/compliments/compliments_e2e_cron_entry.js @@ -0,0 +1,18 @@ +let config = { + modules: [ + { + module: "compliments", + position: "middle_center", + config: { + specialDayUnique: true, + compliments: { + anytime: ["just a test"], + "* * * * *": ["anytime cron"] + } + } + } + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { module.exports = config; } diff --git a/tests/e2e/modules/compliments_spec.js b/tests/e2e/modules/compliments_spec.js index 95b342577e..7763dc689b 100644 --- a/tests/e2e/modules/compliments_spec.js +++ b/tests/e2e/modules/compliments_spec.js @@ -77,5 +77,16 @@ describe("Compliments module", () => { await expect(doTest(["Special day message"])).resolves.toBe(true); }); }); + + describe("cron type key", () => { + beforeAll(async () => { + await helpers.startApplication("tests/configs/modules/compliments/compliments_e2e_cron_entry.js"); + await helpers.getDocument(); + }); + + it("compliments array contains only special value", async () => { + await expect(doTest(["anytime cron"])).resolves.toBe(true); + }); + }); }); }); From b72feb3565c4e2878a649ef7f09396d110abca20 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Sat, 13 Jul 2024 12:51:26 -0500 Subject: [PATCH 19/20] fixes after review --- modules/default/compliments/compliments.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/default/compliments/compliments.js b/modules/default/compliments/compliments.js index 9d43246388..b683dba5d7 100644 --- a/modules/default/compliments/compliments.js +++ b/modules/default/compliments/compliments.js @@ -43,8 +43,8 @@ Module.register("compliments", { let minute_sync_delay = 1; // loop thru all the configured when events for (let m of Object.keys(this.config.compliments)) { - // if one contains a space, its a cron entry - if (m.includes(" ")) { + // if it is a cron entry + if (this.isCronEntry(m)) { // we need to synch our interval cycle to the minute minute_sync_delay = (60 - (moment().second())) * 1000; break; @@ -54,10 +54,16 @@ Module.register("compliments", { setTimeout(() => { setInterval(() => { this.updateDom(this.config.fadeSpeed); - }, this.config.updateInterval); }, + }, this.config.updateInterval); + }, minute_sync_delay); }, + // check to see if this entry could be a cron entry wich contains spaces + isCronEntry (entry) { + return entry.includes(" "); + }, + /** * @param {string} cronExpression The cron expression. See https://croner.56k.guru/usage/pattern/ * @param {Date} [timestamp] The timestamp to check. Defaults to the current time. @@ -72,7 +78,7 @@ Module.register("compliments", { const cronJob = new Cron(cronExpression); const nextRunTime = cronJob.nextRun(adjustedTimestamp); - let secondsDelta = (nextRunTime - adjustedTimestamp) / 1000; + const secondsDelta = (nextRunTime - adjustedTimestamp) / 1000; return secondsDelta; }, @@ -82,7 +88,7 @@ Module.register("compliments", { * @returns {number} a random index of given array */ randomIndex (compliments) { - if (compliments.length === 1) { + if (compliments.length <= 1) { return 0; } @@ -140,10 +146,10 @@ Module.register("compliments", { }); let date_compliments = []; - // Add compliments for special days + // Add compliments for special day/times for (let entry of temp_list) { // check if this could be a cron type entry - if (entry.includes(" ")) { + if (this.isCronEntry(entry)) { // make sure the regex is valid if (new RegExp(this.cron_regex).test(entry)) { // check if we are in the time range for the cron entry From 8ae865990be218dd43472acf4ec416f1e7eb8771 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Mon, 15 Jul 2024 09:50:21 -0500 Subject: [PATCH 20/20] add comment global Cron to remove eslint-disable --- modules/default/compliments/compliments.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/default/compliments/compliments.js b/modules/default/compliments/compliments.js index b683dba5d7..4c4bef471f 100644 --- a/modules/default/compliments/compliments.js +++ b/modules/default/compliments/compliments.js @@ -1,3 +1,5 @@ +/* global Cron */ + Module.register("compliments", { // Module config defaults. defaults: { @@ -74,7 +76,6 @@ Module.register("compliments", { const adjustedTimestamp = new Date(timestamp.getTime() - 1000); // https://www.npmjs.com/package/croner - /*eslint-disable-next-line*/ const cronJob = new Cron(cronExpression); const nextRunTime = cronJob.nextRun(adjustedTimestamp);