mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-08-21 04:45:17 +00:00
Release v2.25.0 (#3214)
## [2.25.0] - 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! ### Added - 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 - 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 - **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 - Update issue template - Update dev/dependencies incl. electron to v26 - Replace pretty-quick by lint-staged (<https://github.com/azz/pretty-quick/issues/164>) - 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` - Update the French translation according to the English file. - Update dependabot incl. vendor/fonts (monthly check) - Renew `package-lock.json` for release ### Fixed - Fix engine check on npm install (#3135) - Fix undefined formatTime method in clock module (#3143) - Fix clientonly startup fails after async added (#3151) - Fix electron width/heigth when using xrandr under bullseye - Fix 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 (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) - Fix issue in weathergov provider with probability of precipitation not showing up on hourly or daily forecast --------- Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Karsten Hassel <hassel@gmx.de> Co-authored-by: Malte Hallström <46646495+SkySails@users.noreply.github.com> Co-authored-by: Veeck <github@veeck.de> Co-authored-by: veeck <michael@veeck.de> Co-authored-by: dWoolridge <dwoolridge@charter.net> Co-authored-by: Johan <jojjepersson@yahoo.se> Co-authored-by: Dario Mratovich <dario_mratovich@hotmail.com> Co-authored-by: Dario Mratovich <dario.mratovich@outlook.com> Co-authored-by: Magnus <34011212+MagMar94@users.noreply.github.com> Co-authored-by: Naveen <172697+naveensrinivasan@users.noreply.github.com> Co-authored-by: buxxi <buxxi@omfilm.net> Co-authored-by: Thomas Hirschberger <47733292+Tom-Hirschberger@users.noreply.github.com> Co-authored-by: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com> Co-authored-by: Andrés Vanegas Jiménez <142350+angeldeejay@users.noreply.github.com> Co-authored-by: Dave Child <dave@addedbytes.com> Co-authored-by: grenagit <46225780+grenagit@users.noreply.github.com> Co-authored-by: Grena <grena@grenabox.fr> Co-authored-by: Magnus Marthinsen <magmar@online.no> Co-authored-by: Patrick <psieg@users.noreply.github.com> Co-authored-by: Piotr Rajnisz <56397164+rajniszp@users.noreply.github.com> Co-authored-by: Suthep Yonphimai <tomzt@users.noreply.github.com> Co-authored-by: CarJem Generations (Carter Wallace) <cwallacecs@gmail.com> Co-authored-by: Nicholas Fogal <nfogal.misc@gmail.com> Co-authored-by: JakeBinney <126349119+JakeBinney@users.noreply.github.com> Co-authored-by: OWL4C <124401812+OWL4C@users.noreply.github.com> Co-authored-by: Oscar Björkman <17575446+oscarb@users.noreply.github.com> Co-authored-by: Ismar Slomic <ismar@slomic.no> Co-authored-by: Jørgen Veum-Wahlberg <jorgen.wahlberg@amedia.no> Co-authored-by: Eddie Hung <6740044+eddiehung@users.noreply.github.com> Co-authored-by: Bugsounet - Cédric <github@bugsounet.fr> Co-authored-by: bugsounet <bugsounet@bugsounet.fr> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Knapoc <Knapoc@users.noreply.github.com> Co-authored-by: sam detweiler <sdetweil@gmail.com> Co-authored-by: veeck <michael.veeck@nebenan.de> Co-authored-by: Paranoid93 <6515818+Paranoid93@users.noreply.github.com> Co-authored-by: NolanKingdon <27908974+NolanKingdon@users.noreply.github.com> Co-authored-by: J. Kenzal Hunter <kenzal.hunter@gmail.com> Co-authored-by: Teddy <teddy.payet@gmail.com> Co-authored-by: TeddyStarinvest <teddy.payet@starinvest.com> Co-authored-by: martingron <61826403+martingron@users.noreply.github.com> Co-authored-by: dgoth <132394363+dgoth@users.noreply.github.com>
This commit is contained in:
@@ -4,7 +4,8 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = require(`${process.cwd()}/tests/configs/default.js`).configFactory({
|
||||
ipWhitelist: []
|
||||
ipWhitelist: [],
|
||||
port: 8282
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
|
@@ -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,
|
||||
|
@@ -0,0 +1,36 @@
|
||||
/* MagicMirror² Test config for multiple calendar events having the same name and start date/time
|
||||
*
|
||||
* By Paranoid93 https://github.com/Paranoid93/
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
timeFormat: 12,
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "calendar",
|
||||
position: "bottom_bar",
|
||||
config: {
|
||||
maximumEntries: 30,
|
||||
hideDuplicates: false,
|
||||
calendars: [
|
||||
{
|
||||
maximumEntries: 15,
|
||||
maximumNumberOfDays: 10000,
|
||||
url: "http://localhost:8080/tests/mocks/calendar_test.ics" // contains 11 events
|
||||
},
|
||||
{
|
||||
maximumEntries: 15,
|
||||
maximumNumberOfDays: 10000,
|
||||
url: "http://localhost:8080/tests/mocks/calendar_test_clone.ics" // clone of upper calendar
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
24
tests/configs/modules/clock/clock_showSunMoon.js
Normal file
24
tests/configs/modules/clock/clock_showSunMoon.js
Normal file
@@ -0,0 +1,24 @@
|
||||
/* MagicMirror² Test config for default clock module
|
||||
*
|
||||
* By Johan Hammar
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
timeFormat: 12,
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "clock",
|
||||
position: "middle_center",
|
||||
config: {
|
||||
showSunTimes: true,
|
||||
showMoonTimes: true
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
28
tests/configs/modules/compliments/compliments_animateCSS.js
Normal file
28
tests/configs/modules/compliments/compliments_animateCSS.js
Normal file
@@ -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;
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -4,7 +4,8 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = require(`${process.cwd()}/tests/configs/default.js`).configFactory({
|
||||
ipWhitelist: ["x.x.x.x"]
|
||||
ipWhitelist: ["x.x.x.x"],
|
||||
port: 8181
|
||||
});
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
|
78
tests/e2e/animateCSS_spec.js
Normal file
78
tests/e2e/animateCSS_spec.js
Normal file
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
@@ -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);
|
||||
});
|
||||
|
||||
|
@@ -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);
|
||||
});
|
||||
});
|
||||
|
@@ -1,5 +1,4 @@
|
||||
const jsdom = require("jsdom");
|
||||
const corefetch = require("fetch");
|
||||
|
||||
exports.startApplication = async (configFilename, exec) => {
|
||||
jest.resetModules();
|
||||
@@ -31,7 +30,8 @@ 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;
|
||||
global.window = dom.window;
|
||||
dom.window.fetch = fetch;
|
||||
dom.window.onload = () => {
|
||||
global.document = dom.window.document;
|
||||
resolve();
|
||||
@@ -80,14 +80,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);
|
||||
|
@@ -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();
|
||||
};
|
||||
|
@@ -10,7 +10,7 @@ describe("ipWhitelist directive configuration", () => {
|
||||
});
|
||||
|
||||
it("should return 403", async () => {
|
||||
const res = await helpers.fetch("http://localhost:8080");
|
||||
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:8080");
|
||||
const res = await fetch("http://localhost:8282");
|
||||
expect(res.status).toBe(200);
|
||||
});
|
||||
});
|
||||
|
@@ -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);
|
||||
});
|
||||
@@ -80,6 +84,17 @@ describe("Calendar module", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Events from multiple calendars", () => {
|
||||
beforeAll(async () => {
|
||||
await helpers.startApplication("tests/configs/modules/calendar/show-duplicates-in-calendar.js");
|
||||
await helpers.getDocument();
|
||||
});
|
||||
|
||||
it("should show multiple events with the same title and start time from different calendars", async () => {
|
||||
await testElementLength(".calendar .event", 22);
|
||||
});
|
||||
});
|
||||
|
||||
process.setMaxListeners(0);
|
||||
for (let i = -12; i < 12; i++) {
|
||||
describe("Recurring event per timezone", () => {
|
||||
|
@@ -71,11 +71,28 @@ describe("Clock module", () => {
|
||||
});
|
||||
|
||||
it("should not show the time when digital clock is shown", async () => {
|
||||
const elem = await document.querySelector(".clock .digital .time");
|
||||
const elem = document.querySelector(".clock .digital .time");
|
||||
expect(elem).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with showSun/MoonTime enabled", () => {
|
||||
beforeAll(async () => {
|
||||
await helpers.startApplication("tests/configs/modules/clock/clock_showSunMoon.js");
|
||||
await helpers.getDocument();
|
||||
});
|
||||
|
||||
it("should show the sun times", async () => {
|
||||
const elem = await helpers.waitForElement(".clock .digital .sun");
|
||||
expect(elem).not.toBe(null);
|
||||
});
|
||||
|
||||
it("should show the moon times", async () => {
|
||||
const elem = await helpers.waitForElement(".clock .digital .moon");
|
||||
expect(elem).not.toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with showWeek config enabled", () => {
|
||||
beforeAll(async () => {
|
||||
await helpers.startApplication("tests/configs/modules/clock/clock_showWeek.js");
|
||||
|
@@ -25,8 +25,8 @@ describe("Newsfeed module", () => {
|
||||
|
||||
it("should NOT show the newsfeed description", async () => {
|
||||
await helpers.waitForElement(".newsfeed");
|
||||
const element = document.querySelector(".newsfeed .newsfeed-desc");
|
||||
expect(element).toBe(null);
|
||||
const elem = document.querySelector(".newsfeed .newsfeed-desc");
|
||||
expect(elem).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -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", () => {
|
||||
@@ -48,7 +50,7 @@ describe("Weather module", () => {
|
||||
it("should render windDirection with an arrow", async () => {
|
||||
const elem = await helpers.waitForElement(".weather .normal.medium sup i.fa-long-arrow-alt-down");
|
||||
expect(elem).not.toBe(null);
|
||||
expect(elem.outerHTML).toContain("transform:rotate(250deg);");
|
||||
expect(elem.outerHTML).toContain("transform:rotate(250deg)");
|
||||
});
|
||||
|
||||
it("should render humidity", async () => {
|
||||
|
@@ -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", () => {
|
||||
|
@@ -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", () => {
|
||||
|
@@ -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);
|
||||
});
|
||||
});
|
||||
|
@@ -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);
|
||||
});
|
||||
});
|
||||
|
@@ -1,3 +1,4 @@
|
||||
const fs = require("fs");
|
||||
const helpers = require("./helpers/global-setup");
|
||||
|
||||
describe("templated config with port variable", () => {
|
||||
@@ -6,10 +7,15 @@ 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 () => {
|
||||
const res = await helpers.fetch("http://localhost:8090");
|
||||
const res = await fetch("http://localhost:8090");
|
||||
expect(res.status).toBe(200);
|
||||
});
|
||||
});
|
||||
|
@@ -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);
|
||||
});
|
||||
});
|
||||
|
@@ -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);
|
||||
};
|
||||
|
@@ -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", () => {
|
||||
|
190
tests/mocks/calendar_test_clone.ics
Normal file
190
tests/mocks/calendar_test_clone.ics
Normal file
@@ -0,0 +1,190 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Google Inc//Google Calendar 70.9054//EN
|
||||
VERSION:2.0
|
||||
CALSCALE:GREGORIAN
|
||||
METHOD:PUBLISH
|
||||
X-WR-CALNAME:MagicMirrorTest
|
||||
X-WR-TIMEZONE:America/Santiago
|
||||
X-WR-CALDESC:Testing propose MagicMirror
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:America/Santiago
|
||||
X-LIC-LOCATION:America/Santiago
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:-0300
|
||||
TZOFFSETTO:-0400
|
||||
TZNAME:-04
|
||||
DTSTART:19700510T000000
|
||||
RDATE:19700510T030000
|
||||
RDATE:19710509T030000
|
||||
RDATE:19720514T030000
|
||||
RDATE:19730513T030000
|
||||
RDATE:19740512T030000
|
||||
RDATE:19750511T030000
|
||||
RDATE:19760509T030000
|
||||
RDATE:19770515T030000
|
||||
RDATE:19780514T030000
|
||||
RDATE:19790513T030000
|
||||
RDATE:19800511T030000
|
||||
RDATE:19810510T030000
|
||||
RDATE:19820509T030000
|
||||
RDATE:19830515T030000
|
||||
RDATE:19840513T030000
|
||||
RDATE:19850512T030000
|
||||
RDATE:19860511T030000
|
||||
RDATE:19870510T030000
|
||||
RDATE:19880515T030000
|
||||
RDATE:19890514T030000
|
||||
RDATE:19900513T030000
|
||||
RDATE:19910512T030000
|
||||
RDATE:19920510T030000
|
||||
RDATE:19930509T030000
|
||||
RDATE:19940515T030000
|
||||
RDATE:19950514T030000
|
||||
RDATE:19960512T030000
|
||||
RDATE:19970511T030000
|
||||
RDATE:19980510T030000
|
||||
RDATE:19990509T030000
|
||||
RDATE:20000514T030000
|
||||
RDATE:20010513T030000
|
||||
RDATE:20020512T030000
|
||||
RDATE:20030511T030000
|
||||
RDATE:20040509T030000
|
||||
RDATE:20050515T030000
|
||||
RDATE:20060514T030000
|
||||
RDATE:20070513T030000
|
||||
RDATE:20080511T030000
|
||||
RDATE:20090510T030000
|
||||
RDATE:20100509T030000
|
||||
RDATE:20110515T030000
|
||||
RDATE:20120513T030000
|
||||
RDATE:20130512T030000
|
||||
RDATE:20140511T030000
|
||||
RDATE:20150510T030000
|
||||
RDATE:20160515T030000
|
||||
RDATE:20170514T030000
|
||||
RDATE:20180513T030000
|
||||
RDATE:20190512T030000
|
||||
RDATE:20200510T030000
|
||||
RDATE:20210509T030000
|
||||
RDATE:20220515T030000
|
||||
RDATE:20230514T030000
|
||||
RDATE:20240512T030000
|
||||
RDATE:20250511T030000
|
||||
RDATE:20260510T030000
|
||||
RDATE:20270509T030000
|
||||
RDATE:20280514T030000
|
||||
RDATE:20290513T030000
|
||||
RDATE:20300512T030000
|
||||
RDATE:20310511T030000
|
||||
RDATE:20320509T030000
|
||||
RDATE:20330515T030000
|
||||
RDATE:20340514T030000
|
||||
RDATE:20350513T030000
|
||||
RDATE:20360511T030000
|
||||
RDATE:20370510T030000
|
||||
END:STANDARD
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:-0300
|
||||
TZOFFSETTO:-0400
|
||||
TZNAME:-04
|
||||
DTSTART:20380509T000000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=5;BYDAY=2SU
|
||||
END:STANDARD
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:-0400
|
||||
TZOFFSETTO:-0300
|
||||
TZNAME:-03
|
||||
DTSTART:19700809T000000
|
||||
RDATE:19700809T040000
|
||||
RDATE:19710815T040000
|
||||
RDATE:19720813T040000
|
||||
RDATE:19730812T040000
|
||||
RDATE:19740811T040000
|
||||
RDATE:19750810T040000
|
||||
RDATE:19760815T040000
|
||||
RDATE:19770814T040000
|
||||
RDATE:19780813T040000
|
||||
RDATE:19790812T040000
|
||||
RDATE:19800810T040000
|
||||
RDATE:19810809T040000
|
||||
RDATE:19820815T040000
|
||||
RDATE:19830814T040000
|
||||
RDATE:19840812T040000
|
||||
RDATE:19850811T040000
|
||||
RDATE:19860810T040000
|
||||
RDATE:19870809T040000
|
||||
RDATE:19880814T040000
|
||||
RDATE:19890813T040000
|
||||
RDATE:19900812T040000
|
||||
RDATE:19910811T040000
|
||||
RDATE:19920809T040000
|
||||
RDATE:19930815T040000
|
||||
RDATE:19940814T040000
|
||||
RDATE:19950813T040000
|
||||
RDATE:19960811T040000
|
||||
RDATE:19970810T040000
|
||||
RDATE:19980809T040000
|
||||
RDATE:19990815T040000
|
||||
RDATE:20000813T040000
|
||||
RDATE:20010812T040000
|
||||
RDATE:20020811T040000
|
||||
RDATE:20030810T040000
|
||||
RDATE:20040815T040000
|
||||
RDATE:20050814T040000
|
||||
RDATE:20060813T040000
|
||||
RDATE:20070812T040000
|
||||
RDATE:20080810T040000
|
||||
RDATE:20090809T040000
|
||||
RDATE:20100815T040000
|
||||
RDATE:20110814T040000
|
||||
RDATE:20120812T040000
|
||||
RDATE:20130811T040000
|
||||
RDATE:20140810T040000
|
||||
RDATE:20150809T040000
|
||||
RDATE:20160814T040000
|
||||
RDATE:20170813T040000
|
||||
RDATE:20180812T040000
|
||||
RDATE:20190811T040000
|
||||
RDATE:20200809T040000
|
||||
RDATE:20210815T040000
|
||||
RDATE:20220814T040000
|
||||
RDATE:20230813T040000
|
||||
RDATE:20240811T040000
|
||||
RDATE:20250810T040000
|
||||
RDATE:20260809T040000
|
||||
RDATE:20270815T040000
|
||||
RDATE:20280813T040000
|
||||
RDATE:20290812T040000
|
||||
RDATE:20300811T040000
|
||||
RDATE:20310810T040000
|
||||
RDATE:20320815T040000
|
||||
RDATE:20330814T040000
|
||||
RDATE:20340813T040000
|
||||
RDATE:20350812T040000
|
||||
RDATE:20360810T040000
|
||||
RDATE:20370809T040000
|
||||
END:DAYLIGHT
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:-0400
|
||||
TZOFFSETTO:-0300
|
||||
TZNAME:-03
|
||||
DTSTART:20380815T000000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=8;BYDAY=2SU
|
||||
END:DAYLIGHT
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
DTSTART;TZID=America/Santiago:20170309T100000
|
||||
DTEND;TZID=America/Santiago:20170309T110000
|
||||
RRULE:FREQ=MONTHLY;INTERVAL=30;BYMONTHDAY=9
|
||||
DTSTAMP:20170310T172720Z
|
||||
UID:80rl9kuu5bq49gme99eklov27k@google.com
|
||||
CREATED:20170310T172400Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20170310T172400Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:TestEvent
|
||||
TRANSP:OPAQUE
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
@@ -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(() => {}),
|
||||
|
@@ -1,113 +1,107 @@
|
||||
global.moment = require("moment-timezone");
|
||||
const { performWebRequest, formatTime } = require("../../../../modules/default/utils");
|
||||
|
||||
const nodeVersion = process.version.match(/^v(\d+)\.*/)[1];
|
||||
|
||||
describe("Default modules utils tests", () => {
|
||||
describe("performWebRequest", () => {
|
||||
if (nodeVersion > 18) {
|
||||
const locationHost = "localhost:8080";
|
||||
const locationProtocol = "http";
|
||||
const locationHost = "localhost:8080";
|
||||
const locationProtocol = "http";
|
||||
|
||||
let fetchResponse;
|
||||
let fetchMock;
|
||||
let urlToCall;
|
||||
let fetchResponse;
|
||||
let fetchMock;
|
||||
let urlToCall;
|
||||
|
||||
beforeEach(() => {
|
||||
fetchResponse = new Response();
|
||||
global.fetch = jest.fn(() => Promise.resolve(fetchResponse));
|
||||
fetchMock = global.fetch;
|
||||
beforeEach(() => {
|
||||
fetchResponse = new Response();
|
||||
global.fetch = jest.fn(() => Promise.resolve(fetchResponse));
|
||||
fetchMock = global.fetch;
|
||||
});
|
||||
|
||||
describe("When using cors proxy", () => {
|
||||
Object.defineProperty(global, "location", {
|
||||
value: {
|
||||
host: locationHost,
|
||||
protocol: locationProtocol
|
||||
}
|
||||
});
|
||||
|
||||
describe("When using cors proxy", () => {
|
||||
Object.defineProperty(global, "location", {
|
||||
value: {
|
||||
host: locationHost,
|
||||
protocol: locationProtocol
|
||||
}
|
||||
});
|
||||
test("Calls correct URL once", async () => {
|
||||
urlToCall = "http://www.test.com/path?param1=value1";
|
||||
|
||||
test("Calls correct URL once", async () => {
|
||||
urlToCall = "http://www.test.com/path?param1=value1";
|
||||
await performWebRequest(urlToCall, "json", true);
|
||||
|
||||
await performWebRequest(urlToCall, "json", true);
|
||||
|
||||
expect(fetchMock.mock.calls.length).toBe(1);
|
||||
expect(fetchMock.mock.calls[0][0]).toBe(`${locationProtocol}//${locationHost}/cors?url=${urlToCall}`);
|
||||
});
|
||||
|
||||
test("Sends correct headers", async () => {
|
||||
urlToCall = "http://www.test.com/path?param1=value1";
|
||||
|
||||
const headers = [
|
||||
{ name: "header1", value: "value1" },
|
||||
{ name: "header2", value: "value2" }
|
||||
];
|
||||
|
||||
await performWebRequest(urlToCall, "json", true, headers);
|
||||
|
||||
expect(fetchMock.mock.calls.length).toBe(1);
|
||||
expect(fetchMock.mock.calls[0][0]).toBe(`${locationProtocol}//${locationHost}/cors?sendheaders=header1:value1,header2:value2&url=${urlToCall}`);
|
||||
});
|
||||
expect(fetchMock.mock.calls.length).toBe(1);
|
||||
expect(fetchMock.mock.calls[0][0]).toBe(`${locationProtocol}//${locationHost}/cors?url=${urlToCall}`);
|
||||
});
|
||||
|
||||
describe("When not using cors proxy", () => {
|
||||
test("Calls correct URL once", async () => {
|
||||
urlToCall = "http://www.test.com/path?param1=value1";
|
||||
test("Sends correct headers", async () => {
|
||||
urlToCall = "http://www.test.com/path?param1=value1";
|
||||
|
||||
await performWebRequest(urlToCall);
|
||||
const headers = [
|
||||
{ name: "header1", value: "value1" },
|
||||
{ name: "header2", value: "value2" }
|
||||
];
|
||||
|
||||
expect(fetchMock.mock.calls.length).toBe(1);
|
||||
expect(fetchMock.mock.calls[0][0]).toBe(urlToCall);
|
||||
});
|
||||
await performWebRequest(urlToCall, "json", true, headers);
|
||||
|
||||
test("Sends correct headers", async () => {
|
||||
urlToCall = "http://www.test.com/path?param1=value1";
|
||||
const headers = [
|
||||
{ name: "header1", value: "value1" },
|
||||
{ name: "header2", value: "value2" }
|
||||
];
|
||||
expect(fetchMock.mock.calls.length).toBe(1);
|
||||
expect(fetchMock.mock.calls[0][0]).toBe(`${locationProtocol}//${locationHost}/cors?sendheaders=header1:value1,header2:value2&url=${urlToCall}`);
|
||||
});
|
||||
});
|
||||
|
||||
await performWebRequest(urlToCall, "json", false, headers);
|
||||
describe("When not using cors proxy", () => {
|
||||
test("Calls correct URL once", async () => {
|
||||
urlToCall = "http://www.test.com/path?param1=value1";
|
||||
|
||||
const expectedHeaders = { headers: { header1: "value1", header2: "value2" } };
|
||||
expect(fetchMock.mock.calls.length).toBe(1);
|
||||
expect(fetchMock.mock.calls[0][1]).toStrictEqual(expectedHeaders);
|
||||
});
|
||||
await performWebRequest(urlToCall);
|
||||
|
||||
expect(fetchMock.mock.calls.length).toBe(1);
|
||||
expect(fetchMock.mock.calls[0][0]).toBe(urlToCall);
|
||||
});
|
||||
|
||||
describe("When receiving json format", () => {
|
||||
test("Returns undefined when no data is received", async () => {
|
||||
urlToCall = "www.test.com";
|
||||
test("Sends correct headers", async () => {
|
||||
urlToCall = "http://www.test.com/path?param1=value1";
|
||||
const headers = [
|
||||
{ name: "header1", value: "value1" },
|
||||
{ name: "header2", value: "value2" }
|
||||
];
|
||||
|
||||
const response = await performWebRequest(urlToCall);
|
||||
await performWebRequest(urlToCall, "json", false, headers);
|
||||
|
||||
expect(response).toBe(undefined);
|
||||
});
|
||||
|
||||
test("Returns object when data is received", async () => {
|
||||
urlToCall = "www.test.com";
|
||||
fetchResponse = new Response('{"body": "some content"}');
|
||||
|
||||
const response = await performWebRequest(urlToCall);
|
||||
|
||||
expect(response.body).toBe("some content");
|
||||
});
|
||||
|
||||
test("Returns expected headers when data is received", async () => {
|
||||
urlToCall = "www.test.com";
|
||||
fetchResponse = new Response('{"body": "some content"}', { headers: { header1: "value1", header2: "value2" } });
|
||||
|
||||
const response = await performWebRequest(urlToCall, "json", false, undefined, ["header1"]);
|
||||
|
||||
expect(response.headers.length).toBe(1);
|
||||
expect(response.headers[0].name).toBe("header1");
|
||||
expect(response.headers[0].value).toBe("value1");
|
||||
});
|
||||
const expectedHeaders = { headers: { header1: "value1", header2: "value2" } };
|
||||
expect(fetchMock.mock.calls.length).toBe(1);
|
||||
expect(fetchMock.mock.calls[0][1]).toStrictEqual(expectedHeaders);
|
||||
});
|
||||
} else {
|
||||
test("Always ok, need one test", () => {});
|
||||
}
|
||||
});
|
||||
|
||||
describe("When receiving json format", () => {
|
||||
test("Returns undefined when no data is received", async () => {
|
||||
urlToCall = "www.test.com";
|
||||
|
||||
const response = await performWebRequest(urlToCall);
|
||||
|
||||
expect(response).toBe(undefined);
|
||||
});
|
||||
|
||||
test("Returns object when data is received", async () => {
|
||||
urlToCall = "www.test.com";
|
||||
fetchResponse = new Response('{"body": "some content"}');
|
||||
|
||||
const response = await performWebRequest(urlToCall);
|
||||
|
||||
expect(response.body).toBe("some content");
|
||||
});
|
||||
|
||||
test("Returns expected headers when data is received", async () => {
|
||||
urlToCall = "www.test.com";
|
||||
fetchResponse = new Response('{"body": "some content"}', { headers: { header1: "value1", header2: "value2" } });
|
||||
|
||||
const response = await performWebRequest(urlToCall, "json", false, undefined, ["header1"]);
|
||||
|
||||
expect(response.headers.length).toBe(1);
|
||||
expect(response.headers[0].name).toBe("header1");
|
||||
expect(response.headers[0].value).toBe("value1");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatTime", () => {
|
||||
|
@@ -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 };
|
||||
|
Reference in New Issue
Block a user