Release 2.26.0 (#3319)

## [2.26.0] - 01-01-2024

Thanks to: @bnitkin, @bugsounet, @dependabot, @jkriegshauser,
@kaennchenstruggle, @KristjanESPERANTO 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 also marks the latest release by Michael Teeuw. For more
info, please read the following post: [A New Chapter for MagicMirror:
The Community Takes the
Lead](https://forum.magicmirror.builders/topic/18329/a-new-chapter-for-magicmirror-the-community-takes-the-lead).

### Added

- Added update notification updater (for 3rd party modules)
- Added node 21 to the test matrix
- Added transform object to calendar:customEvents
- Added ESLint rules for jest (including jest/expect-expect and
jest/no-done-callback)

### Removed

- Removed Codecov workflow (not working anymore, other workflow
required) (#3107)
- Removed titleReplace from calendar, replaced + extended by
customEvents (backward compatibility included) (#3249)
- Removed failing unit test (#3254)
- Removed some unused variables

### Updated

- Update electron to v27 and update other dependencies as well as github
actions
- Update newsfeed: Use `html-to-text` instead of regex for transform
description
- Review ESLint config (#3269)
- Updated dependencies
- Clock module: optionally display current moon phase in addition to
rise/set times
- electron is now per default started without gpu, if needed it must be
enabled with new env var `ELECTRON_ENABLE_GPU=1` on startup (#3226)
- Replace prettier by stylistic in ESLint config to lint JavaScript (and
disable some rules for `config/config.js*` files)
- Update node-ical to v0.17.1 and fix tests

### Fixed

- Avoid fade out/in on updateDom when many calendars are used
- Fix the option eventClass on customEvents.
- Fix yr API version in locationforecast and sunrise call (#3227)
- Fix cloneObject() function to respect RegExp (#3237)
- Fix newsfeed module for feeds using "a10:updated" tag (#3238)
- Fix issue template (#3167)
- Fix #3256 filter out bad results from rrule.between
- Fix calendar events sometimes not respecting deleted events (#3250)
- Fix electron loadurl locally on Windows when address "0.0.0.0" (#2550)
- Fix updatanotification (update_helper.js): catch error if reponse is
not an JSON format (check PM2)
- Fix missing typeof in calendar module
- Fix style issues after prettier update
- Fix calendar test (#3291) by moving "Exdate check" from e2e to
electron to run on a Thursday
- Fix calendar config params `fetchInterval` and `excludedEvents` were
never used from single calendar config (#3297)
- Fix MM_PORT variable not used in electron and allow full path for
MM_CONFIG_FILE variable (#3302)

---------

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>
Co-authored-by: kaennchenstruggle <54073894+kaennchenstruggle@users.noreply.github.com>
Co-authored-by: jkriegshauser <jkriegshauser@gmail.com>
Co-authored-by: Ben Nitkin <ben@nitkin.net>
This commit is contained in:
Michael Teeuw
2024-01-01 15:38:08 +01:00
committed by GitHub
parent 343e7de7bd
commit 8c0e7db494
110 changed files with 3433 additions and 2915 deletions

View File

@@ -0,0 +1,27 @@
/* MagicMirror² Test ics with out of date event causing bad return from rrule.between
*
* By Sam Detweiler
* MIT Licensed.
*/
let config = {
timeFormat: 12,
logLevel: ["INFO", "LOG", "WARN", "ERROR", "DEBUG"],
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
calendars: [
{
url: "http://localhost:8080/tests/mocks/bad_rrule.ics"
}
]
}
}
]
};
/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {
module.exports = config;
}

View File

@@ -0,0 +1,36 @@
/* MagicMirror² Test calendar exdate
*
* By jkriegshauser
* MIT Licensed.
*
* NOTE: calendar_test_exdate.ics has exdate entries for the next 20 years, but without some
* way to set a debug date for tests, this test may become flaky on specific days (i.e. could
* not test easily on leap-years, the BYDAY specified in exdate, etc.) or when the 20 years
* elapses if this project is still in active development ;)
* See issue #3250
*/
let config = {
timeFormat: 12,
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
maximumEntries: 100,
calendars: [
{
maximumEntries: 100,
maximumNumberOfDays: 364,
url: "http://localhost:8080/tests/mocks/calendar_test_exdate.ics"
}
]
}
}
]
};
/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {
module.exports = config;
}

View File

@@ -5,7 +5,7 @@
* 09/2023
* MIT Licensed.
*/
const helpers = require("./helpers/global-setup.js");
const helpers = require("./helpers/global-setup");
describe("AnimateCSS integration Test", () => {
// define config file for testing
@@ -21,27 +21,29 @@ describe("AnimateCSS integration Test", () => {
* move similar tests in function doTest
* @param {string} [animationIn] animation in name of AnimateCSS to test.
* @param {string} [animationOut] animation out name of AnimateCSS to test.
* @returns {boolean} result
*/
const doTest = async (animationIn, animationOut) => {
await helpers.getDocument();
let elem = await helpers.waitForElement(`.compliments`);
expect(elem).not.toBe(null);
let elem = await helpers.waitForElement(".compliments");
expect(elem).not.toBeNull();
let styles = window.getComputedStyle(elem);
if (animationIn && animationIn !== "") {
expect(styles._values["animation-name"]).toBe(animationIn);
} else {
expect(styles._values["animation-name"]).toBe(undefined);
expect(styles._values["animation-name"]).toBeUndefined();
}
if (animationOut && animationOut !== "") {
elem = await helpers.waitForElement(`.compliments.animate__animated.animate__${animationOut}`);
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
styles = window.getComputedStyle(elem);
expect(styles._values["animation-name"]).toBe(animationOut);
} else {
expect(styles._values["animation-name"]).toBe(undefined);
expect(styles._values["animation-name"]).toBeUndefined();
}
return true;
};
afterEach(async () => {
@@ -51,28 +53,28 @@ describe("AnimateCSS integration Test", () => {
describe("animateIn and animateOut Test", () => {
it("with flipInX and flipOutX animation", async () => {
await helpers.startApplication(testConfigFile);
await doTest("flipInX", "flipOutX");
await expect(doTest("flipInX", "flipOutX")).resolves.toBe(true);
});
});
describe("use animateOut name for animateIn (vice versa) Test", () => {
it("without animation", async () => {
await helpers.startApplication(testConfigFileInvertedAnimationName);
await doTest();
await expect(doTest()).resolves.toBe(true);
});
});
describe("false Animation name test", () => {
it("without animation", async () => {
await helpers.startApplication(testConfigFileFallbackToDefault);
await doTest();
await expect(doTest()).resolves.toBe(true);
});
});
describe("no Animation defined test", () => {
it("without animation", async () => {
await helpers.startApplication(testConfigByDefault);
await doTest();
await expect(doTest()).resolves.toBe(true);
});
});
});

View File

@@ -21,7 +21,7 @@ describe("App environment", () => {
it("should show the title MagicMirror²", async () => {
const elem = await helpers.waitForElement("title");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toBe("MagicMirror²");
});
});

View File

@@ -20,7 +20,7 @@ describe("All font files from roboto.css should be downloadable", () => {
await helpers.stopApplication();
});
test.each(fontFiles)("should return 200 HTTP code for file '%s'", async (fontFile) => {
it.each(fontFiles)("should return 200 HTTP code for file '%s'", async (fontFile) => {
const fontUrl = `http://localhost:8080/fonts/${fontFile}`;
const res = await fetch(fontUrl);
expect(res.status).toBe(200);

View File

@@ -1,6 +1,7 @@
const path = require("path");
const auth = require("express-basic-auth");
const express = require("express");
const app = express();
const basicAuth = auth({

View File

@@ -31,6 +31,11 @@ exports.getDocument = () => {
jsdom.JSDOM.fromURL(url, { resources: "usable", runScripts: "dangerously" }).then((dom) => {
dom.window.name = "jsdom";
global.window = dom.window;
// Following fixes `navigator is not defined` errors in e2e tests, found here
// https://www.appsloveworld.com/reactjs/100/37/mocha-react-navigator-is-not-defined
global.navigator = {
useragent: "node.js"
};
dom.window.fetch = fetch;
dom.window.onload = () => {
global.document = dom.window.document;
@@ -82,6 +87,7 @@ exports.waitForAllElements = (selector) => {
exports.testMatch = async (element, regex) => {
const elem = await this.waitForElement(element);
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toMatch(regex);
return true;
};

View File

@@ -4,13 +4,13 @@
*/
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") ||
err.includes("AggregateError") ||
err.includes("ERR_SOCKET_CONNECTION_TIMEOUT")
err.includes("ECONNREFUSED")
|| err.includes("ECONNRESET")
|| err.includes("socket hang up")
|| err.includes("exports is not defined")
|| err.includes("write EPIPE")
|| err.includes("AggregateError")
|| err.includes("ERR_SOCKET_CONNECTION_TIMEOUT")
) {
jest.fn();
} else {

View File

@@ -3,13 +3,14 @@ const helpers = require("./global-setup");
exports.getText = async (element, result) => {
const elem = await helpers.waitForElement(element);
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(
elem.textContent
.trim()
.replace(/(\r\n|\n|\r)/gm, "")
.replace(/[ ]+/g, " ")
).toBe(result);
return true;
};
exports.startApp = async (configFileName, additionalMockData) => {

View File

@@ -11,7 +11,7 @@ describe("Alert module", () => {
it("should show the welcome message", async () => {
const elem = await helpers.waitForElement(".ns-box .ns-box-inner .light.bright.small");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toContain("Welcome, start was successful!");
});
});

View File

@@ -2,25 +2,29 @@ const helpers = require("../helpers/global-setup");
const serverBasicAuth = require("../helpers/basic-auth");
describe("Calendar module", () => {
/**
* @param {string} element css selector
* @param {string} result expected number
* @param {string} not reverse result
* @returns {boolean} result
*/
const testElementLength = async (element, result, not) => {
const elem = await helpers.waitForAllElements(element);
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
if (not === "not") {
expect(elem.length).not.toBe(result);
expect(elem).not.toHaveLength(result);
} else {
expect(elem.length).toBe(result);
expect(elem).toHaveLength(result);
}
return true;
};
const testTextContain = async (element, text) => {
const elem = await helpers.waitForElement(element, "undefinedLoading");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toContain(text);
return true;
};
afterAll(async () => {
@@ -34,11 +38,11 @@ describe("Calendar module", () => {
});
it("should show the default maximumEntries of 10", async () => {
await testElementLength(".calendar .event", 10);
await expect(testElementLength(".calendar .event", 10)).resolves.toBe(true);
});
it("should show the default calendar symbol in each event", async () => {
await testElementLength(".calendar .event .fa-calendar-alt", 0, "not");
await expect(testElementLength(".calendar .event .fa-calendar-alt", 0, "not")).resolves.toBe(true);
});
});
@@ -49,27 +53,27 @@ describe("Calendar module", () => {
});
it("should show the custom maximumEntries of 5", async () => {
await testElementLength(".calendar .event", 5);
await expect(testElementLength(".calendar .event", 5)).resolves.toBe(true);
});
it("should show the custom calendar symbol in four events", async () => {
await testElementLength(".calendar .event .fa-birthday-cake", 4);
await expect(testElementLength(".calendar .event .fa-birthday-cake", 4)).resolves.toBe(true);
});
it("should show a customEvent calendar symbol in one event", async () => {
await testElementLength(".calendar .event .fa-dice", 1);
await expect(testElementLength(".calendar .event .fa-dice", 1)).resolves.toBe(true);
});
it("should show a customEvent calendar eventClass in one event", async () => {
await testElementLength(".calendar .event.undo", 1);
await expect(testElementLength(".calendar .event.undo", 1)).resolves.toBe(true);
});
it("should show two custom icons for repeating events", async () => {
await testElementLength(".calendar .event .fa-undo", 2);
await expect(testElementLength(".calendar .event .fa-undo", 2)).resolves.toBe(true);
});
it("should show two custom icons for day events", async () => {
await testElementLength(".calendar .event .fa-calendar-day", 2);
await expect(testElementLength(".calendar .event .fa-calendar-day", 2)).resolves.toBe(true);
});
});
@@ -80,7 +84,7 @@ describe("Calendar module", () => {
});
it("should show the recurring birthday event 6 times", async () => {
await testElementLength(".calendar .event", 6);
await expect(testElementLength(".calendar .event", 6)).resolves.toBe(true);
});
});
@@ -91,7 +95,7 @@ describe("Calendar module", () => {
});
it("should show multiple events with the same title and start time from different calendars", async () => {
await testElementLength(".calendar .event", 22);
await expect(testElementLength(".calendar .event", 22)).resolves.toBe(true);
});
});
@@ -107,7 +111,7 @@ describe("Calendar module", () => {
});
it(`should contain text "Mar 25th" in timezone UTC ${-i}`, async () => {
await testTextContain(".calendar", "Mar 25th");
await expect(testTextContain(".calendar", "Mar 25th")).resolves.toBe(true);
});
});
}
@@ -124,7 +128,7 @@ describe("Calendar module", () => {
});
it("should return TestEvents", async () => {
await testElementLength(".calendar .event", 0, "not");
await expect(testElementLength(".calendar .event", 0, "not")).resolves.toBe(true);
});
});
@@ -135,7 +139,7 @@ describe("Calendar module", () => {
});
it("should return TestEvents", async () => {
await testElementLength(".calendar .event", 0, "not");
await expect(testElementLength(".calendar .event", 0, "not")).resolves.toBe(true);
});
});
@@ -146,7 +150,7 @@ describe("Calendar module", () => {
});
it("should return TestEvents", async () => {
await testElementLength(".calendar .event", 0, "not");
await expect(testElementLength(".calendar .event", 0, "not")).resolves.toBe(true);
});
});
@@ -157,7 +161,7 @@ describe("Calendar module", () => {
});
it("should return TestEvents", async () => {
await testElementLength(".calendar .event", 0, "not");
await expect(testElementLength(".calendar .event", 0, "not")).resolves.toBe(true);
});
});
@@ -173,7 +177,7 @@ describe("Calendar module", () => {
});
it("should show Unauthorized error", async () => {
await testTextContain(".calendar", "Error in the calendar module. Authorization failed");
await expect(testTextContain(".calendar", "Error in the calendar module. Authorization failed")).resolves.toBe(true);
});
});
});

View File

@@ -13,12 +13,12 @@ describe("Clock set to spanish language module", () => {
it("shows date with correct format", async () => {
const dateRegex = /^(?:lunes|martes|miércoles|jueves|viernes|sábado|domingo), \d{1,2} de (?:enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre) de \d{4}$/;
await helpers.testMatch(".clock .date", dateRegex);
await expect(helpers.testMatch(".clock .date", dateRegex)).resolves.toBe(true);
});
it("shows time in 24hr format", async () => {
const timeRegex = /^(?:2[0-3]|[01]\d):[0-5]\d[0-5]\d$/;
await helpers.testMatch(".clock .time", timeRegex);
await expect(helpers.testMatch(".clock .time", timeRegex)).resolves.toBe(true);
});
});
@@ -30,12 +30,12 @@ describe("Clock set to spanish language module", () => {
it("shows date with correct format", async () => {
const dateRegex = /^(?:lunes|martes|miércoles|jueves|viernes|sábado|domingo), \d{1,2} de (?:enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre) de \d{4}$/;
await helpers.testMatch(".clock .date", dateRegex);
await expect(helpers.testMatch(".clock .date", dateRegex)).resolves.toBe(true);
});
it("shows time in 12hr format", async () => {
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[ap]m$/;
await helpers.testMatch(".clock .time", timeRegex);
await expect(helpers.testMatch(".clock .time", timeRegex)).resolves.toBe(true);
});
});
@@ -47,7 +47,7 @@ describe("Clock set to spanish language module", () => {
it("shows 12hr time with upper case AM/PM", async () => {
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[AP]M$/;
await helpers.testMatch(".clock .time", timeRegex);
await expect(helpers.testMatch(".clock .time", timeRegex)).resolves.toBe(true);
});
});
@@ -59,7 +59,7 @@ describe("Clock set to spanish language module", () => {
it("shows week with correct format", async () => {
const weekRegex = /^Semana [0-9]{1,2}$/;
await helpers.testMatch(".clock .week", weekRegex);
await expect(helpers.testMatch(".clock .week", weekRegex)).resolves.toBe(true);
});
});
});

View File

@@ -14,12 +14,12 @@ describe("Clock module", () => {
it("should show the date in the correct format", async () => {
const dateRegex = /^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}, \d{4}$/;
await helpers.testMatch(".clock .date", dateRegex);
await expect(helpers.testMatch(".clock .date", dateRegex)).resolves.toBe(true);
});
it("should show the time in 24hr format", async () => {
const timeRegex = /^(?:2[0-3]|[01]\d):[0-5]\d[0-5]\d$/;
await helpers.testMatch(".clock .time", timeRegex);
await expect(helpers.testMatch(".clock .time", timeRegex)).resolves.toBe(true);
});
});
@@ -31,12 +31,12 @@ describe("Clock module", () => {
it("should show the date in the correct format", async () => {
const dateRegex = /^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}, \d{4}$/;
await helpers.testMatch(".clock .date", dateRegex);
await expect(helpers.testMatch(".clock .date", dateRegex)).resolves.toBe(true);
});
it("should show the time in 12hr format", async () => {
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[ap]m$/;
await helpers.testMatch(".clock .time", timeRegex);
await expect(helpers.testMatch(".clock .time", timeRegex)).resolves.toBe(true);
});
});
@@ -48,7 +48,7 @@ describe("Clock module", () => {
it("should show 12hr time with upper case AM/PM", async () => {
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[0-5]\d[AP]M$/;
await helpers.testMatch(".clock .time", timeRegex);
await expect(helpers.testMatch(".clock .time", timeRegex)).resolves.toBe(true);
});
});
@@ -60,7 +60,7 @@ describe("Clock module", () => {
it("should show 12hr time without seconds am/pm", async () => {
const timeRegex = /^(?:1[0-2]|[1-9]):[0-5]\d[ap]m$/;
await helpers.testMatch(".clock .time", timeRegex);
await expect(helpers.testMatch(".clock .time", timeRegex)).resolves.toBe(true);
});
});
@@ -72,7 +72,7 @@ describe("Clock module", () => {
it("should not show the time when digital clock is shown", async () => {
const elem = document.querySelector(".clock .digital .time");
expect(elem).toBe(null);
expect(elem).toBeNull();
});
});
@@ -84,12 +84,12 @@ describe("Clock module", () => {
it("should show the sun times", async () => {
const elem = await helpers.waitForElement(".clock .digital .sun");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
});
it("should show the moon times", async () => {
const elem = await helpers.waitForElement(".clock .digital .moon");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
});
});
@@ -101,14 +101,14 @@ describe("Clock module", () => {
it("should show the week in the correct format", async () => {
const weekRegex = /^Week [0-9]{1,2}$/;
await helpers.testMatch(".clock .week", weekRegex);
await expect(helpers.testMatch(".clock .week", weekRegex)).resolves.toBe(true);
});
it("should show the week with the correct number of week of year", async () => {
const currentWeekNumber = moment().week();
const weekToShow = `Week ${currentWeekNumber}`;
const elem = await helpers.waitForElement(".clock .week");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toBe(weekToShow);
});
});
@@ -121,7 +121,7 @@ describe("Clock module", () => {
it("should show the analog clock face", async () => {
const elem = helpers.waitForElement(".clock-circle");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
});
});
@@ -133,9 +133,9 @@ describe("Clock module", () => {
it("should show the analog clock face and the date", async () => {
const elemClock = helpers.waitForElement(".clock-circle");
await expect(elemClock).not.toBe(null);
await expect(elemClock).not.toBeNull();
const elemDate = helpers.waitForElement(".clock .date");
await expect(elemDate).not.toBe(null);
await expect(elemDate).not.toBeNull();
});
});
});

View File

@@ -1,16 +1,19 @@
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.toBe(null);
expect(elem).not.toBeNull();
elem = await helpers.waitForElement(".module-content");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(complimentsArray).toContain(elem.textContent);
return true;
};
afterAll(async () => {
@@ -18,14 +21,14 @@ describe("Compliments module", () => {
});
describe("Feature anytime in compliments module", () => {
describe("Set anytime and empty compliments for morning, evening and afternoon ", () => {
describe("Set anytime and empty compliments for morning, evening and afternoon", () => {
beforeAll(async () => {
await helpers.startApplication("tests/configs/modules/compliments/compliments_anytime.js");
await helpers.getDocument();
});
it("shows anytime because if configure empty parts of day compliments and set anytime compliments", async () => {
await doTest(["Anytime here"]);
await expect(doTest(["Anytime here"])).resolves.toBe(true);
});
});
@@ -36,7 +39,7 @@ describe("Compliments module", () => {
});
it("shows anytime compliments", async () => {
await doTest(["Anytime here"]);
await expect(doTest(["Anytime here"])).resolves.toBe(true);
});
});
});
@@ -48,7 +51,7 @@ describe("Compliments module", () => {
});
it("should show compliments from a remote file", async () => {
await doTest(["Remote compliment file works!"]);
await expect(doTest(["Remote compliment file works!"])).resolves.toBe(true);
});
});
});

View File

@@ -13,7 +13,7 @@ describe("Test helloworld module", () => {
it("Test message helloworld module", async () => {
const elem = await helpers.waitForElement(".helloworld");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toContain("Test HelloWorld Module");
});
});
@@ -26,7 +26,7 @@ describe("Test helloworld module", () => {
it("Test message helloworld module", async () => {
const elem = await helpers.waitForElement(".helloworld");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toContain("Hello World!");
});
});

View File

@@ -13,20 +13,20 @@ describe("Newsfeed module", () => {
it("should show the newsfeed title", async () => {
const elem = await helpers.waitForElement(".newsfeed .newsfeed-source");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toContain("Rodrigo Ramirez Blog");
});
it("should show the newsfeed article", async () => {
const elem = await helpers.waitForElement(".newsfeed .newsfeed-title");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toContain("QPanel");
});
it("should NOT show the newsfeed description", async () => {
await helpers.waitForElement(".newsfeed");
const elem = document.querySelector(".newsfeed .newsfeed-desc");
expect(elem).toBe(null);
expect(elem).toBeNull();
});
});
@@ -38,14 +38,14 @@ describe("Newsfeed module", () => {
it("should not show articles with prohibited words", async () => {
const elem = await helpers.waitForElement(".newsfeed .newsfeed-title");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toContain("Problema VirtualBox");
});
it("should show the newsfeed description", async () => {
const elem = await helpers.waitForElement(".newsfeed .newsfeed-desc");
expect(elem).not.toBe(null);
expect(elem.textContent.length).not.toBe(0);
expect(elem).not.toBeNull();
expect(elem.textContent).not.toHaveLength(0);
});
});
@@ -57,7 +57,7 @@ describe("Newsfeed module", () => {
it("should show malformed url warning", async () => {
const elem = await helpers.waitForElement(".newsfeed .small", "No news at the moment.");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toContain("Error in the Newsfeed module. Malformed url.");
});
});
@@ -70,7 +70,7 @@ describe("Newsfeed module", () => {
it("should show empty items info message", async () => {
const elem = await helpers.waitForElement(".newsfeed .small");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toContain("No news at the moment.");
});
});

View File

@@ -15,15 +15,15 @@ describe("Weather module", () => {
});
it("should render wind speed and wind direction", async () => {
await weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "12 WSW");
await expect(weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "12 WSW")).resolves.toBe(true);
});
it("should render temperature with icon", async () => {
await weatherFunc.getText(".weather .large.light span.bright", "1.5°");
await expect(weatherFunc.getText(".weather .large.light span.bright", "1.5°")).resolves.toBe(true);
});
it("should render feels like temperature", async () => {
await weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like -5.6°");
await expect(weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like -5.6°")).resolves.toBe(true);
});
});
});
@@ -34,7 +34,7 @@ describe("Weather module", () => {
});
it("should render a compliment based on the current weather", async () => {
await weatherFunc.getText(".compliments .module-content span", "snow");
await expect(weatherFunc.getText(".compliments .module-content span", "snow")).resolves.toBe(true);
});
});
@@ -44,25 +44,25 @@ describe("Weather module", () => {
});
it("should render windUnits in beaufort", async () => {
await weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "6");
await expect(weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "6")).resolves.toBe(true);
});
it("should render windDirection with an arrow", async () => {
const elem = await helpers.waitForElement(".weather .normal.medium sup i.fa-long-arrow-alt-down");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.outerHTML).toContain("transform:rotate(250deg)");
});
it("should render humidity", async () => {
await weatherFunc.getText(".weather .normal.medium span:nth-child(3)", "93.7");
await expect(weatherFunc.getText(".weather .normal.medium span:nth-child(3)", "93.7")).resolves.toBe(true);
});
it("should render degreeLabel for temp", async () => {
await weatherFunc.getText(".weather .large.light span.bright", "1°C");
await expect(weatherFunc.getText(".weather .large.light span.bright", "1°C")).resolves.toBe(true);
});
it("should render degreeLabel for feels like", async () => {
await weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like -6°C");
await expect(weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like -6°C")).resolves.toBe(true);
});
});
@@ -72,15 +72,15 @@ describe("Weather module", () => {
});
it("should render wind in imperial units", async () => {
await weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "26 WSW");
await expect(weatherFunc.getText(".weather .normal.medium span:nth-child(2)", "26 WSW")).resolves.toBe(true);
});
it("should render temperatures in fahrenheit", async () => {
await weatherFunc.getText(".weather .large.light span.bright", "34,7°");
await expect(weatherFunc.getText(".weather .large.light span.bright", "34,7°")).resolves.toBe(true);
});
it("should render 'feels like' in fahrenheit", async () => {
await weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like 21,9°");
await expect(weatherFunc.getText(".weather .normal.medium.feelslike span.dimmed", "Feels like 21,9°")).resolves.toBe(true);
});
});
});

View File

@@ -16,7 +16,7 @@ describe("Weather module: Weather Forecast", () => {
const days = ["Today", "Tomorrow", "Sun", "Mon", "Tue"];
for (const [index, day] of days.entries()) {
it(`should render day ${day}`, async () => {
await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day);
await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day)).resolves.toBe(true);
});
}
@@ -24,21 +24,21 @@ describe("Weather module: Weather Forecast", () => {
for (const [index, icon] of icons.entries()) {
it(`should render icon ${icon}`, async () => {
const elem = await helpers.waitForElement(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(2) span.wi-${icon}`);
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
});
}
const maxTemps = ["24.4°", "21.0°", "22.9°", "23.4°", "20.6°"];
for (const [index, temp] of maxTemps.entries()) {
it(`should render max temperature ${temp}`, async () => {
await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(3)`, temp);
await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(3)`, temp)).resolves.toBe(true);
});
}
const minTemps = ["15.3°", "13.6°", "13.8°", "13.9°", "10.9°"];
for (const [index, temp] of minTemps.entries()) {
it(`should render min temperature ${temp}`, async () => {
await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(4)`, temp);
await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(4)`, temp)).resolves.toBe(true);
});
}
@@ -46,7 +46,7 @@ describe("Weather module: Weather Forecast", () => {
for (const [index, opacity] of opacities.entries()) {
it(`should render fading of rows with opacity=${opacity}`, async () => {
const elem = await helpers.waitForElement(`.weather table.small tr:nth-child(${index + 1})`);
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.outerHTML).toContain(`<tr style="opacity: ${opacity};">`);
});
}
@@ -60,7 +60,7 @@ describe("Weather module: Weather Forecast", () => {
const days = ["Fri", "Sat", "Sun", "Mon", "Tue"];
for (const [index, day] of days.entries()) {
it(`should render day ${day}`, async () => {
await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day);
await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day)).resolves.toBe(true);
});
}
});
@@ -72,21 +72,21 @@ describe("Weather module: Weather Forecast", () => {
it("should render custom table class", async () => {
const elem = await helpers.waitForElement(".weather table.myTableClass");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
});
it("should render colored rows", async () => {
const table = await helpers.waitForElement(".weather table.myTableClass");
expect(table).not.toBe(null);
expect(table.rows).not.toBe(null);
expect(table.rows.length).toBe(5);
expect(table).not.toBeNull();
expect(table.rows).not.toBeNull();
expect(table.rows).toHaveLength(5);
});
const precipitations = [undefined, "2.51 mm"];
for (const [index, precipitation] of precipitations.entries()) {
if (precipitation) {
it(`should render precipitation amount ${precipitation}`, async () => {
await weatherFunc.getText(`.weather table tr:nth-child(${index + 1}) td.precipitation-amount`, precipitation);
await expect(weatherFunc.getText(`.weather table tr:nth-child(${index + 1}) td.precipitation-amount`, precipitation)).resolves.toBe(true);
});
}
}
@@ -101,7 +101,7 @@ describe("Weather module: Weather Forecast", () => {
const temperatures = ["75_9°", "69_8°", "73_2°", "74_1°", "69_1°"];
for (const [index, temp] of temperatures.entries()) {
it(`should render custom decimalSymbol = '_' for temp ${temp}`, async () => {
await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(3)`, temp);
await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(3)`, temp)).resolves.toBe(true);
});
}
});
@@ -111,7 +111,7 @@ describe("Weather module: Weather Forecast", () => {
for (const [index, precipitation] of precipitations.entries()) {
if (precipitation) {
it(`should render precipitation amount ${precipitation}`, async () => {
await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.precipitation-amount`, precipitation);
await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.precipitation-amount`, precipitation)).resolves.toBe(true);
});
}
}

View File

@@ -16,7 +16,7 @@ describe("Weather module: Weather Hourly Forecast", () => {
const minTemps = ["7:00 pm", "8:00 pm", "9:00 pm", "10:00 pm", "11:00 pm"];
for (const [index, hour] of minTemps.entries()) {
it(`should render forecast for hour ${hour}`, async () => {
await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.day`, hour);
await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.day`, hour)).resolves.toBe(true);
});
}
});
@@ -30,7 +30,7 @@ describe("Weather module: Weather Hourly Forecast", () => {
const minTemps = ["7:00 pm", "9:00 pm", "11:00 pm", "1:00 am", "3:00 am"];
for (const [index, hour] of minTemps.entries()) {
it(`should render forecast for hour ${hour}`, async () => {
await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.day`, hour);
await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.day`, hour)).resolves.toBe(true);
});
}
});
@@ -46,7 +46,7 @@ describe("Weather module: Weather Hourly Forecast", () => {
for (const [index, amount] of amounts.entries()) {
if (amount) {
it(`should render precipitation amount ${amount}`, async () => {
await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.precipitation-amount`, amount);
await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.precipitation-amount`, amount)).resolves.toBe(true);
});
}
}
@@ -57,7 +57,7 @@ describe("Weather module: Weather Hourly Forecast", () => {
for (const [index, pop] of propabilities.entries()) {
if (pop) {
it(`should render probability ${pop}`, async () => {
await weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.precipitation-prob`, pop);
await expect(weatherFunc.getText(`.weather table.small tr:nth-child(${index + 1}) td.precipitation-prob`, pop)).resolves.toBe(true);
});
}
}

View File

@@ -11,14 +11,14 @@ describe("Display of modules", () => {
it("should show the test header", async () => {
const elem = await helpers.waitForElement("#module_0_helloworld .module-header");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
// textContent gibt hier lowercase zurück, das uppercase wird durch css realisiert, was daher nicht in textContent landet
expect(elem.textContent).toBe("test_header");
});
it("should show no header if no header text is specified", async () => {
const elem = await helpers.waitForElement("#module_1_helloworld .module-header");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toBe("undefined");
});
});

View File

@@ -11,13 +11,13 @@ describe("Check configuration without modules", () => {
it("shows the message MagicMirror² title", async () => {
const elem = await helpers.waitForElement("#module_1_helloworld .module-content");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toContain("MagicMirror²");
});
it("shows the url of michael's website", async () => {
const elem = await helpers.waitForElement("#module_5_helloworld .module-content");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toContain("www.michaelteeuw.nl");
});
});

View File

@@ -15,7 +15,7 @@ describe("Position of modules", () => {
const className = position.replace("_", ".");
it(`should show text in ${position}`, async () => {
const elem = await helpers.waitForElement(`.${className}`);
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(elem.textContent).toContain(`Text in ${position}`);
});
}

View File

@@ -1,5 +1,3 @@
const helpers = require("./helpers/global-setup");
const delay = (time) => {
return new Promise((resolve) => setTimeout(resolve, time));
};

View File

@@ -44,100 +44,110 @@ describe("Translations", () => {
);
});
it("should load translation file", (done) => {
dom.window.onload = async () => {
const { Translator, Module, config } = dom.window;
config.language = "en";
Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null);
it("should load translation file", () => {
return new Promise((done) => {
dom.window.onload = async () => {
const { Translator, Module, config } = dom.window;
config.language = "en";
Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null);
Module.register("name", { getTranslations: () => translations });
const MMM = Module.create("name");
Module.register("name", { getTranslations: () => translations });
const MMM = Module.create("name");
await MMM.loadTranslations();
await MMM.loadTranslations();
expect(Translator.load.args.length).toBe(1);
expect(Translator.load.calledWith(MMM, "translations/en.json", false)).toBe(true);
expect(Translator.load.args).toHaveLength(1);
expect(Translator.load.calledWith(MMM, "translations/en.json", false)).toBe(true);
done();
};
done();
};
});
});
it("should load translation + fallback file", (done) => {
dom.window.onload = async () => {
const { Translator, Module } = dom.window;
Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null);
it("should load translation + fallback file", () => {
return new Promise((done) => {
dom.window.onload = async () => {
const { Translator, Module } = dom.window;
Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null);
Module.register("name", { getTranslations: () => translations });
const MMM = Module.create("name");
Module.register("name", { getTranslations: () => translations });
const MMM = Module.create("name");
await MMM.loadTranslations();
await MMM.loadTranslations();
expect(Translator.load.args.length).toBe(2);
expect(Translator.load.calledWith(MMM, "translations/de.json", false)).toBe(true);
expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true);
expect(Translator.load.args).toHaveLength(2);
expect(Translator.load.calledWith(MMM, "translations/de.json", false)).toBe(true);
expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true);
done();
};
done();
};
});
});
it("should load translation fallback file", (done) => {
dom.window.onload = async () => {
const { Translator, Module, config } = dom.window;
config.language = "--";
Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null);
it("should load translation fallback file", () => {
return new Promise((done) => {
dom.window.onload = async () => {
const { Translator, Module, config } = dom.window;
config.language = "--";
Translator.load = sinon.stub().callsFake((_m, _f, _fb) => null);
Module.register("name", { getTranslations: () => translations });
const MMM = Module.create("name");
Module.register("name", { getTranslations: () => translations });
const MMM = Module.create("name");
await MMM.loadTranslations();
await MMM.loadTranslations();
expect(Translator.load.args.length).toBe(1);
expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true);
expect(Translator.load.args).toHaveLength(1);
expect(Translator.load.calledWith(MMM, "translations/en.json", true)).toBe(true);
done();
};
done();
};
});
});
it("should load no file", (done) => {
dom.window.onload = async () => {
const { Translator, Module } = dom.window;
Translator.load = sinon.stub();
it("should load no file", () => {
return new Promise((done) => {
dom.window.onload = async () => {
const { Translator, Module } = dom.window;
Translator.load = sinon.stub();
Module.register("name", {});
const MMM = Module.create("name");
Module.register("name", {});
const MMM = Module.create("name");
await MMM.loadTranslations();
await MMM.loadTranslations();
expect(Translator.load.callCount).toBe(0);
expect(Translator.load.callCount).toBe(0);
done();
};
done();
};
});
});
});
const mmm = {
name: "TranslationTest",
file(file) {
file (file) {
return `http://localhost:3000/${file}`;
}
};
describe("Parsing language files through the Translator class", () => {
for (let language in translations) {
it(`should parse ${language}`, (done) => {
const dom = new JSDOM(
`<script>var translations = ${JSON.stringify(translations)}; var Log = {log: () => {}};</script>\
it(`should parse ${language}`, () => {
return new Promise((done) => {
const dom = new JSDOM(
`<script>var translations = ${JSON.stringify(translations)}; var Log = {log: () => {}};</script>\
<script src="file://${path.join(__dirname, "..", "..", "js", "translator.js")}">`,
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;
await Translator.load(mmm, translations[language], false);
expect(typeof Translator.translations[mmm.name]).toBe("object");
expect(Object.keys(Translator.translations[mmm.name]).length).toBeGreaterThanOrEqual(1);
done();
};
await Translator.load(mmm, translations[language], false);
expect(typeof Translator.translations[mmm.name]).toBe("object");
expect(Object.keys(Translator.translations[mmm.name]).length).toBeGreaterThanOrEqual(1);
done();
};
});
});
}
});
@@ -146,19 +156,21 @@ describe("Translations", () => {
let base;
let missing = [];
beforeAll((done) => {
const dom = new JSDOM(
`<script>var translations = ${JSON.stringify(translations)}; var Log = {log: () => {}};</script>\
beforeAll(() => {
return new Promise((done) => {
const dom = new JSDOM(
`<script>var translations = ${JSON.stringify(translations)}; var Log = {log: () => {}};</script>\
<script src="file://${path.join(__dirname, "..", "..", "js", "translator.js")}">`,
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;
await Translator.load(mmm, translations.de, false);
base = Object.keys(Translator.translations[mmm.name]).sort();
done();
};
await Translator.load(mmm, translations.de, false);
base = Object.keys(Translator.translations[mmm.name]).sort();
done();
};
});
});
afterAll(() => {
@@ -175,19 +187,21 @@ describe("Translations", () => {
describe(`Translation keys of ${language}`, () => {
let keys;
beforeAll((done) => {
const dom = new JSDOM(
`<script>var translations = ${JSON.stringify(translations)}; var Log = {log: () => {}};</script>\
beforeAll(() => {
return new Promise((done) => {
const dom = new JSDOM(
`<script>var translations = ${JSON.stringify(translations)}; var Log = {log: () => {}};</script>\
<script src="file://${path.join(__dirname, "..", "..", "js", "translator.js")}">`,
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;
await Translator.load(mmm, translations[language], false);
keys = Object.keys(Translator.translations[mmm.name]).sort();
done();
};
await Translator.load(mmm, translations[language], false);
keys = Object.keys(Translator.translations[mmm.name]).sort();
done();
};
});
});
it(`${language} keys should be in base`, () => {

View File

@@ -12,8 +12,8 @@ describe("Electron app environment", () => {
it("should open browserwindow", async () => {
const module = await helpers.getElement("#module_0_helloworld");
expect(await module.textContent()).toContain("Test Display Header");
expect(global.electronApp.windows().length).toBe(1);
await expect(module.textContent()).resolves.toContain("Test Display Header");
expect(global.electronApp.windows()).toHaveLength(1);
});
});
@@ -29,7 +29,7 @@ describe("Development console tests", () => {
it("should open browserwindow and dev console", async () => {
while (global.electronApp.windows().length < 2) await events.once(global.electronApp, "window");
const pageArray = await global.electronApp.windows();
expect(pageArray.length).toBe(2);
expect(pageArray).toHaveLength(2);
for (const page of pageArray) {
expect(["MagicMirror²", "DevTools"]).toContain(await page.title());
}

View File

@@ -37,9 +37,9 @@ exports.stopApplication = async () => {
};
exports.getElement = async (selector) => {
expect(global.page);
expect(global.page).not.toBeNull();
let elem = global.page.locator(selector);
await elem.waitFor();
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
return elem;
};

View File

@@ -3,7 +3,7 @@ const helpers = require("./global-setup");
exports.getText = async (element, result) => {
const elem = await helpers.getElement(element);
await expect(elem).not.toBe(null);
await expect(elem).not.toBeNull();
const text = await elem.textContent();
await expect(
text
@@ -11,6 +11,7 @@ exports.getText = async (element, result) => {
.replace(/(\r\n|\n|\r)/gm, "")
.replace(/[ ]+/g, " ")
).toBe(result);
return true;
};
exports.startApp = async (configFileName, systemDate) => {

View File

@@ -1,13 +1,16 @@
const helpers = require("../helpers/global-setup");
describe("Calendar module", () => {
/**
* move similar tests in function doTest
* @param {string} cssClass css selector
* @returns {boolean} result
*/
const doTest = async (cssClass) => {
let elem = await helpers.getElement(`.calendar .module-content .event${cssClass}`);
expect(await elem.isVisible()).toBe(true);
const elem = await helpers.getElement(`.calendar .module-content .event${cssClass}`);
await expect(elem.isVisible()).resolves.toBe(true);
return true;
};
afterEach(async () => {
@@ -17,27 +20,41 @@ describe("Calendar module", () => {
describe("Test css classes", () => {
it("has css class dayBeforeYesterday", async () => {
await helpers.startApplication("tests/configs/modules/calendar/custom.js", "03 Jan 2030 12:30:00 GMT");
await doTest(".dayBeforeYesterday");
await expect(doTest(".dayBeforeYesterday")).resolves.toBe(true);
});
it("has css class yesterday", async () => {
await helpers.startApplication("tests/configs/modules/calendar/custom.js", "02 Jan 2030 12:30:00 GMT");
await doTest(".yesterday");
await expect(doTest(".yesterday")).resolves.toBe(true);
});
it("has css class today", async () => {
await helpers.startApplication("tests/configs/modules/calendar/custom.js", "01 Jan 2030 12:30:00 GMT");
await doTest(".today");
await expect(doTest(".today")).resolves.toBe(true);
});
it("has css class tomorrow", async () => {
await helpers.startApplication("tests/configs/modules/calendar/custom.js", "31 Dec 2029 12:30:00 GMT");
await doTest(".tomorrow");
await expect(doTest(".tomorrow")).resolves.toBe(true);
});
it("has css class dayAfterTomorrow", async () => {
await helpers.startApplication("tests/configs/modules/calendar/custom.js", "30 Dec 2029 12:30:00 GMT");
await doTest(".dayAfterTomorrow");
await expect(doTest(".dayAfterTomorrow")).resolves.toBe(true);
});
});
describe("Exdate check", () => {
it("should show the recurring event 51 times (excluded once) in a 364-day (inclusive) period", async () => {
// test must run on a Thursday
await helpers.startApplication("tests/configs/modules/calendar/exdate.js", "14 Dec 2023 12:30:00 GMT");
expect(global.page).not.toBeNull();
const loc = await global.page.locator(".calendar .event");
const elem = loc.first();
await elem.waitFor();
expect(elem).not.toBeNull();
const cnt = await loc.count();
expect(cnt).toBe(51);
});
});
});

View File

@@ -1,15 +1,18 @@
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) => {
await helpers.getElement(".compliments");
const elem = await helpers.getElement(".module-content");
expect(elem).not.toBe(null);
expect(elem).not.toBeNull();
expect(complimentsArray).toContain(await elem.textContent());
return true;
};
afterEach(async () => {
@@ -19,17 +22,17 @@ describe("Compliments module", () => {
describe("parts of days", () => {
it("Morning compliments for that part of day", async () => {
await helpers.startApplication("tests/configs/modules/compliments/compliments_parts_day.js", "01 Oct 2022 10:00:00 GMT");
await doTest(["Hi", "Good Morning", "Morning test"]);
await expect(doTest(["Hi", "Good Morning", "Morning test"])).resolves.toBe(true);
});
it("Afternoon show Compliments for that part of day", async () => {
await helpers.startApplication("tests/configs/modules/compliments/compliments_parts_day.js", "01 Oct 2022 15:00:00 GMT");
await doTest(["Hello", "Good Afternoon", "Afternoon test"]);
await expect(doTest(["Hello", "Good Afternoon", "Afternoon test"])).resolves.toBe(true);
});
it("Evening show Compliments for that part of day", async () => {
await helpers.startApplication("tests/configs/modules/compliments/compliments_parts_day.js", "01 Oct 2022 20:00:00 GMT");
await doTest(["Hello There", "Good Evening", "Evening test"]);
await expect(doTest(["Hello There", "Good Evening", "Evening test"])).resolves.toBe(true);
});
});
@@ -37,7 +40,7 @@ describe("Compliments module", () => {
describe("Set date and empty compliments for anytime, morning, evening and afternoon", () => {
it("shows happy new year compliment on new years day", async () => {
await helpers.startApplication("tests/configs/modules/compliments/compliments_date.js", "01 Jan 2022 10:00:00 GMT");
await doTest(["Happy new year!"]);
await expect(doTest(["Happy new year!"])).resolves.toBe(true);
});
});
});

View File

@@ -14,7 +14,7 @@ describe("Weather module", () => {
});
it("should render sunrise", async () => {
await weatherHelper.getText(".weather .normal.medium span:nth-child(4)", "7:00 am");
await expect(weatherHelper.getText(".weather .normal.medium span:nth-child(4)", "7:00 am")).resolves.toBe(true);
});
});
@@ -24,7 +24,7 @@ describe("Weather module", () => {
});
it("should render sunset", async () => {
await weatherHelper.getText(".weather .normal.medium span:nth-child(4)", "3:45 pm");
await expect(weatherHelper.getText(".weather .normal.medium span:nth-child(4)", "3:45 pm")).resolves.toBe(true);
});
});
});

20
tests/mocks/bad_rrule.ics Normal file
View File

@@ -0,0 +1,20 @@
BEGIN:VCALENDAR
BEGIN:VEVENT
DTSTAMP:20210413T203456Z
UID:E689AEB8C02C4E2CADD8C7D3D303CEAD0
DTSTART;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210415T190000
DTEND;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210415T210000
CLASS:PUBLIC
LOCATION:albert heijn
SUMMARY:xxx xxxx
SEQUENCE:10
RRULE:FREQ=DAILY;UNTIL=20210418T170000Z
EXDATE;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210417T190000
EXDATE;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210416T190000
EXDATE;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210415T190000
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER;RELATED=START:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR

View File

@@ -0,0 +1,34 @@
BEGIN:VEVENT
DTSTART;TZID=UTC:20231025T181000
DTEND;TZID=UTC:20231025T195000
RRULE:FREQ=WEEKLY;BYDAY=WE
EXDATE;TZID=UTC:20231101T181000
EXDATE;TZID=UTC:20241030T181000
EXDATE;TZID=UTC:20251029T181000
EXDATE;TZID=UTC:20261028T181000
EXDATE;TZID=UTC:20271027T181000
EXDATE;TZID=UTC:20281025T181000
EXDATE;TZID=UTC:20291024T181000
EXDATE;TZID=UTC:20301023T181000
EXDATE;TZID=UTC:20311022T181000
EXDATE;TZID=UTC:20321020T181000
EXDATE;TZID=UTC:20331019T181000
EXDATE;TZID=UTC:20341018T181000
EXDATE;TZID=UTC:20351017T181000
EXDATE;TZID=UTC:20361015T181000
EXDATE;TZID=UTC:20371014T181000
EXDATE;TZID=UTC:20381013T181000
EXDATE;TZID=UTC:20391012T181000
EXDATE;TZID=UTC:20401010T181000
EXDATE;TZID=UTC:20411009T181000
EXDATE;TZID=UTC:20421008T181000
EXDATE;TZID=UTC:20431007T181000
DTSTAMP:20231025T233434Z
UID:sdflbkasuhdb5fkauglkb@google.com
CREATED:20230306T193128Z
LAST-MODIFIED:20231024T222515Z
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:My Event
TRANSP:OPAQUE
END:VEVENT

View File

@@ -6,17 +6,19 @@ describe("File js/class", () => {
let clone;
let dom;
beforeAll((done) => {
dom = new JSDOM(
`<script>var Log = {log: () => {}};</script>\
beforeAll(() => {
return new Promise((done) => {
dom = new JSDOM(
`<script>var Log = {log: () => {}};</script>\
<script src="file://${path.join(__dirname, "..", "..", "..", "js", "class.js")}">`,
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = () => {
const { cloneObject } = dom.window;
clone = cloneObject;
done();
};
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = () => {
const { cloneObject } = dom.window;
clone = cloneObject;
done();
};
});
});
it("should clone object", () => {
@@ -49,6 +51,13 @@ describe("File js/class", () => {
expect(obj).toBe(expected);
});
it("should clone regex", () => {
const expected = /.*Magic/;
const obj = clone(expected);
expect(obj).toEqual(expected);
expect(expected === obj).toBe(false);
});
it("should clone undefined", () => {
const expected = undefined;
const obj = clone(expected);

View File

@@ -77,209 +77,235 @@ describe("Translator", () => {
Translator.coreTranslationsFallback = coreTranslationsFallback;
};
it("should return custom module translation", (done) => {
const dom = new JSDOM(`<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = () => {
const { Translator } = dom.window;
setTranslations(Translator);
let translation = Translator.translate({ name: "MMM-Module" }, "Hello");
expect(translation).toBe("Hallo");
translation = Translator.translate({ name: "MMM-Module" }, "Hello {username}", { username: "fewieden" });
expect(translation).toBe("Hallo fewieden");
done();
};
it("should return custom module translation", () => {
return new Promise((done) => {
const dom = new JSDOM(`<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = () => {
const { Translator } = dom.window;
setTranslations(Translator);
let translation = Translator.translate({ name: "MMM-Module" }, "Hello");
expect(translation).toBe("Hallo");
translation = Translator.translate({ name: "MMM-Module" }, "Hello {username}", { username: "fewieden" });
expect(translation).toBe("Hallo fewieden");
done();
};
});
});
it("should return core translation", (done) => {
const dom = new JSDOM(`<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = () => {
const { Translator } = dom.window;
setTranslations(Translator);
let translation = Translator.translate({ name: "MMM-Module" }, "FOO");
expect(translation).toBe("Foo");
translation = Translator.translate({ name: "MMM-Module" }, "BAR {something}", { something: "Lorem Ipsum" });
expect(translation).toBe("Bar Lorem Ipsum");
done();
};
it("should return core translation", () => {
return new Promise((done) => {
const dom = new JSDOM(`<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = () => {
const { Translator } = dom.window;
setTranslations(Translator);
let translation = Translator.translate({ name: "MMM-Module" }, "FOO");
expect(translation).toBe("Foo");
translation = Translator.translate({ name: "MMM-Module" }, "BAR {something}", { something: "Lorem Ipsum" });
expect(translation).toBe("Bar Lorem Ipsum");
done();
};
});
});
it("should return custom module translation fallback", (done) => {
const dom = new JSDOM(`<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = () => {
const { Translator } = dom.window;
setTranslations(Translator);
const translation = Translator.translate({ name: "MMM-Module" }, "A key");
expect(translation).toBe("A translation");
done();
};
it("should return custom module translation fallback", () => {
return new Promise((done) => {
const dom = new JSDOM(`<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = () => {
const { Translator } = dom.window;
setTranslations(Translator);
const translation = Translator.translate({ name: "MMM-Module" }, "A key");
expect(translation).toBe("A translation");
done();
};
});
});
it("should return core translation fallback", (done) => {
const dom = new JSDOM(`<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = () => {
const { Translator } = dom.window;
setTranslations(Translator);
const translation = Translator.translate({ name: "MMM-Module" }, "Fallback");
expect(translation).toBe("core fallback");
done();
};
it("should return core translation fallback", () => {
return new Promise((done) => {
const dom = new JSDOM(`<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = () => {
const { Translator } = dom.window;
setTranslations(Translator);
const translation = Translator.translate({ name: "MMM-Module" }, "Fallback");
expect(translation).toBe("core fallback");
done();
};
});
});
it("should return translation with placeholder for missing variables", (done) => {
const dom = new JSDOM(`<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = () => {
const { Translator } = dom.window;
setTranslations(Translator);
const translation = Translator.translate({ name: "MMM-Module" }, "Hello {username}");
expect(translation).toBe("Hallo {username}");
done();
};
it("should return translation with placeholder for missing variables", () => {
return new Promise((done) => {
const dom = new JSDOM(`<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = () => {
const { Translator } = dom.window;
setTranslations(Translator);
const translation = Translator.translate({ name: "MMM-Module" }, "Hello {username}");
expect(translation).toBe("Hallo {username}");
done();
};
});
});
it("should return key if no translation was found", (done) => {
const dom = new JSDOM(`<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = () => {
const { Translator } = dom.window;
setTranslations(Translator);
const translation = Translator.translate({ name: "MMM-Module" }, "MISSING");
expect(translation).toBe("MISSING");
done();
};
it("should return key if no translation was found", () => {
return new Promise((done) => {
const dom = new JSDOM(`<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = () => {
const { Translator } = dom.window;
setTranslations(Translator);
const translation = Translator.translate({ name: "MMM-Module" }, "MISSING");
expect(translation).toBe("MISSING");
done();
};
});
});
});
describe("load", () => {
const mmm = {
name: "TranslationTest",
file(file) {
file (file) {
return `http://localhost:3000/translations/${file}`;
}
};
it("should load translations", (done) => {
const dom = new JSDOM(`<script>var Log = {log: () => {}};</script><script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = async () => {
const { Translator } = dom.window;
const file = "translation_test.json";
it("should load translations", () => {
return new Promise((done) => {
const dom = new JSDOM(`<script>var Log = {log: () => {}};</script><script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = async () => {
const { Translator } = dom.window;
const file = "translation_test.json";
await Translator.load(mmm, file, false);
const json = require(path.join(__dirname, "..", "..", "..", "tests", "mocks", file));
expect(Translator.translations[mmm.name]).toEqual(json);
done();
};
await Translator.load(mmm, file, false);
const json = require(path.join(__dirname, "..", "..", "..", "tests", "mocks", file));
expect(Translator.translations[mmm.name]).toEqual(json);
done();
};
});
});
it("should load translation fallbacks", (done) => {
const dom = new JSDOM(`<script>var Log = {log: () => {}};</script><script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = async () => {
const { Translator } = dom.window;
const file = "translation_test.json";
it("should load translation fallbacks", () => {
return new Promise((done) => {
const dom = new JSDOM(`<script>var Log = {log: () => {}};</script><script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = async () => {
const { Translator } = dom.window;
const file = "translation_test.json";
await Translator.load(mmm, file, true);
const json = require(path.join(__dirname, "..", "..", "..", "tests", "mocks", file));
expect(Translator.translationsFallback[mmm.name]).toEqual(json);
done();
};
await Translator.load(mmm, file, true);
const json = require(path.join(__dirname, "..", "..", "..", "tests", "mocks", file));
expect(Translator.translationsFallback[mmm.name]).toEqual(json);
done();
};
});
});
it("should not load translations, if module fallback exists", (done) => {
const dom = new JSDOM(`<script>var Log = {log: () => {}};</script><script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = async () => {
const { Translator, XMLHttpRequest } = dom.window;
const file = "translation_test.json";
it("should not load translations, if module fallback exists", () => {
return new Promise((done) => {
const dom = new JSDOM(`<script>var Log = {log: () => {}};</script><script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`, { runScripts: "dangerously", resources: "usable" });
dom.window.onload = async () => {
const { Translator, XMLHttpRequest } = dom.window;
const file = "translation_test.json";
XMLHttpRequest.prototype.send = () => {
throw new Error("Shouldn't load files");
XMLHttpRequest.prototype.send = () => {
throw new Error("Shouldn't load files");
};
Translator.translationsFallback[mmm.name] = {
Hello: "Hallo"
};
await Translator.load(mmm, file, false);
expect(Translator.translations[mmm.name]).toBeUndefined();
expect(Translator.translationsFallback[mmm.name]).toEqual({
Hello: "Hallo"
});
done();
};
Translator.translationsFallback[mmm.name] = {
Hello: "Hallo"
};
await Translator.load(mmm, file, false);
expect(Translator.translations[mmm.name]).toBe(undefined);
expect(Translator.translationsFallback[mmm.name]).toEqual({
Hello: "Hallo"
});
done();
};
});
});
});
describe("loadCoreTranslations", () => {
it("should load core translations and fallback", (done) => {
const dom = new JSDOM(
`<script>var translations = {en: "http://localhost:3000/translations/translation_test.json"}; var Log = {log: () => {}};</script>\
it("should load core translations and fallback", () => {
return new Promise((done) => {
const dom = new JSDOM(
`<script>var translations = {en: "http://localhost:3000/translations/translation_test.json"}; var Log = {log: () => {}};</script>\
<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`,
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;
await Translator.loadCoreTranslations("en");
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;
await Translator.loadCoreTranslations("en");
const en = require(path.join(__dirname, "..", "..", "..", "tests", "mocks", "translation_test.json"));
setTimeout(() => {
expect(Translator.coreTranslations).toEqual(en);
expect(Translator.coreTranslationsFallback).toEqual(en);
done();
}, 500);
};
const en = require(path.join(__dirname, "..", "..", "..", "tests", "mocks", "translation_test.json"));
setTimeout(() => {
expect(Translator.coreTranslations).toEqual(en);
expect(Translator.coreTranslationsFallback).toEqual(en);
done();
}, 500);
};
});
});
it("should load core fallback if language cannot be found", (done) => {
const dom = new JSDOM(
`<script>var translations = {en: "http://localhost:3000/translations/translation_test.json"}; var Log = {log: () => {}};</script>\
it("should load core fallback if language cannot be found", () => {
return new Promise((done) => {
const dom = new JSDOM(
`<script>var translations = {en: "http://localhost:3000/translations/translation_test.json"}; var Log = {log: () => {}};</script>\
<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`,
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;
await Translator.loadCoreTranslations("MISSINGLANG");
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;
await Translator.loadCoreTranslations("MISSINGLANG");
const en = require(path.join(__dirname, "..", "..", "..", "tests", "mocks", "translation_test.json"));
setTimeout(() => {
expect(Translator.coreTranslations).toEqual({});
expect(Translator.coreTranslationsFallback).toEqual(en);
done();
}, 500);
};
const en = require(path.join(__dirname, "..", "..", "..", "tests", "mocks", "translation_test.json"));
setTimeout(() => {
expect(Translator.coreTranslations).toEqual({});
expect(Translator.coreTranslationsFallback).toEqual(en);
done();
}, 500);
};
});
});
});
describe("loadCoreTranslationsFallback", () => {
it("should load core translations fallback", (done) => {
const dom = new JSDOM(
`<script>var translations = {en: "http://localhost:3000/translations/translation_test.json"}; var Log = {log: () => {}};</script>\
it("should load core translations fallback", () => {
return new Promise((done) => {
const dom = new JSDOM(
`<script>var translations = {en: "http://localhost:3000/translations/translation_test.json"}; var Log = {log: () => {}};</script>\
<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`,
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;
await Translator.loadCoreTranslationsFallback();
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;
await Translator.loadCoreTranslationsFallback();
const en = require(path.join(__dirname, "..", "..", "..", "tests", "mocks", "translation_test.json"));
setTimeout(() => {
expect(Translator.coreTranslationsFallback).toEqual(en);
done();
}, 500);
};
const en = require(path.join(__dirname, "..", "..", "..", "tests", "mocks", "translation_test.json"));
setTimeout(() => {
expect(Translator.coreTranslationsFallback).toEqual(en);
done();
}, 500);
};
});
});
it("should load core fallback if language cannot be found", (done) => {
const dom = new JSDOM(
`<script>var translations = {}; var Log = {log: () => {}};</script>\
it("should load core fallback if language cannot be found", () => {
return new Promise((done) => {
const dom = new JSDOM(
`<script>var translations = {}; var Log = {log: () => {}};</script>\
<script src="file://${path.join(__dirname, "..", "..", "..", "js", "translator.js")}">`,
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;
await Translator.loadCoreTranslations();
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = async () => {
const { Translator } = dom.window;
await Translator.loadCoreTranslations();
setTimeout(() => {
expect(Translator.coreTranslationsFallback).toEqual({});
done();
}, 500);
};
setTimeout(() => {
expect(Translator.coreTranslationsFallback).toEqual({});
done();
}, 500);
};
});
});
});
});

View File

@@ -4,17 +4,19 @@ const { JSDOM } = require("jsdom");
describe("Test function cmpVersions in js/module.js", () => {
let cmp;
beforeAll((done) => {
const dom = new JSDOM(
`<script>var Class = {extend: () => { return {}; }};</script>\
beforeAll(() => {
return new Promise((done) => {
const dom = new JSDOM(
`<script>var Class = {extend: () => { return {}; }};</script>\
<script src="file://${path.join(__dirname, "..", "..", "..", "js", "module.js")}">`,
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = () => {
const { cmpVersions } = dom.window;
cmp = cmpVersions;
done();
};
{ runScripts: "dangerously", resources: "usable" }
);
dom.window.onload = () => {
const { cmpVersions } = dom.window;
cmp = cmpVersions;
done();
};
});
});
it("should return -1 when comparing 2.1 to 2.2", () => {

View File

@@ -31,47 +31,47 @@ describe("server_functions tests", () => {
};
request = {
url: `/cors?url=www.test.com`
url: "/cors?url=www.test.com"
};
});
test("Calls correct URL once", async () => {
it("Calls correct URL once", async () => {
const urlToCall = "http://www.test.com/path?param1=value1";
request.url = `/cors?url=${urlToCall}`;
await cors(request, corsResponse);
expect(fetchMock.mock.calls.length).toBe(1);
expect(fetchMock.mock.calls).toHaveLength(1);
expect(fetchMock.mock.calls[0][0]).toBe(urlToCall);
});
test("Forewards Content-Type if json", async () => {
it("Forewards Content-Type if json", async () => {
fetchResponseHeadersGet.mockImplementation(() => "json");
await cors(request, corsResponse);
expect(fetchResponseHeadersGet.mock.calls.length).toBe(1);
expect(fetchResponseHeadersGet.mock.calls).toHaveLength(1);
expect(fetchResponseHeadersGet.mock.calls[0][0]).toBe("Content-Type");
expect(corsResponse.set.mock.calls.length).toBe(1);
expect(corsResponse.set.mock.calls).toHaveLength(1);
expect(corsResponse.set.mock.calls[0][0]).toBe("Content-Type");
expect(corsResponse.set.mock.calls[0][1]).toBe("json");
});
test("Forewards Content-Type if xml", async () => {
it("Forewards Content-Type if xml", async () => {
fetchResponseHeadersGet.mockImplementation(() => "xml");
await cors(request, corsResponse);
expect(fetchResponseHeadersGet.mock.calls.length).toBe(1);
expect(fetchResponseHeadersGet.mock.calls).toHaveLength(1);
expect(fetchResponseHeadersGet.mock.calls[0][0]).toBe("Content-Type");
expect(corsResponse.set.mock.calls.length).toBe(1);
expect(corsResponse.set.mock.calls).toHaveLength(1);
expect(corsResponse.set.mock.calls[0][0]).toBe("Content-Type");
expect(corsResponse.set.mock.calls[0][1]).toBe("xml");
});
test("Sends correct data from response", async () => {
it("Sends correct data from response", async () => {
const responseData = "some data";
fetchResponseHeadersText.mockImplementation(() => responseData);
@@ -82,11 +82,11 @@ describe("server_functions tests", () => {
await cors(request, corsResponse);
expect(fetchResponseHeadersText.mock.calls.length).toBe(1);
expect(fetchResponseHeadersText.mock.calls).toHaveLength(1);
expect(sentData).toBe(responseData);
});
test("Sends error data from response", async () => {
it("Sends error data from response", async () => {
const error = new Error("error data");
fetchResponseHeadersText.mockImplementation(() => {
throw error;
@@ -99,32 +99,32 @@ describe("server_functions tests", () => {
await cors(request, corsResponse);
expect(fetchResponseHeadersText.mock.calls.length).toBe(1);
expect(fetchResponseHeadersText.mock.calls).toHaveLength(1);
expect(sentData).toBe(error);
});
test("Fetches with user agent by default", async () => {
it("Fetches with user agent by default", async () => {
await cors(request, corsResponse);
expect(fetchMock.mock.calls.length).toBe(1);
expect(fetchMock.mock.calls).toHaveLength(1);
expect(fetchMock.mock.calls[0][1]).toHaveProperty("headers");
expect(fetchMock.mock.calls[0][1].headers).toHaveProperty("User-Agent");
});
test("Fetches with specified headers", async () => {
it("Fetches with specified headers", async () => {
const headersParam = "sendheaders=header1:value1,header2:value2";
const urlParam = "http://www.test.com/path?param1=value1";
request.url = `/cors?${headersParam}&url=${urlParam}`;
await cors(request, corsResponse);
expect(fetchMock.mock.calls.length).toBe(1);
expect(fetchMock.mock.calls).toHaveLength(1);
expect(fetchMock.mock.calls[0][1]).toHaveProperty("headers");
expect(fetchMock.mock.calls[0][1].headers).toHaveProperty("header1", "value1");
expect(fetchMock.mock.calls[0][1].headers).toHaveProperty("header2", "value2");
});
test("Sends specified headers", async () => {
it("Sends specified headers", async () => {
fetchResponseHeadersGet.mockImplementation((input) => input.replace("header", "value"));
const expectedheaders = "expectedheaders=header1,header2";
@@ -133,9 +133,9 @@ describe("server_functions tests", () => {
await cors(request, corsResponse);
expect(fetchMock.mock.calls.length).toBe(1);
expect(fetchMock.mock.calls).toHaveLength(1);
expect(fetchMock.mock.calls[0][1]).toHaveProperty("headers");
expect(corsResponse.set.mock.calls.length).toBe(3);
expect(corsResponse.set.mock.calls).toHaveLength(3);
expect(corsResponse.set.mock.calls[0][0]).toBe("Content-Type");
expect(corsResponse.set.mock.calls[1][0]).toBe("header1");
expect(corsResponse.set.mock.calls[1][1]).toBe("value1");

View File

@@ -32,7 +32,7 @@ describe("Updatenotification", () => {
const { promisify } = require("util");
promisify.mockReturnValue(execMock);
const GitHelper = require(`../../../modules/default/updatenotification/git_helper`);
const GitHelper = require("../../../modules/default/updatenotification/git_helper");
gitHelper = new GitHelper();
});
@@ -103,7 +103,7 @@ describe("Updatenotification", () => {
execMock.mockRejectedValueOnce(errorMessage);
const repos = await gitHelper.getRepos();
expect(repos.length).toBe(0);
expect(repos).toHaveLength(0);
const { error } = require("logger");
expect(error).toHaveBeenCalledWith(`Failed to retrieve repo info for ${moduleName}: Failed to retrieve status`);
@@ -142,7 +142,7 @@ describe("Updatenotification", () => {
execMock.mockRejectedValueOnce(errorMessage);
const repos = await gitHelper.getRepos();
expect(repos.length).toBe(0);
expect(repos).toHaveLength(0);
const { error } = require("logger");
expect(error).toHaveBeenCalledWith(`Failed to retrieve repo info for ${moduleName}: Failed to retrieve status`);
@@ -183,7 +183,7 @@ describe("Updatenotification", () => {
execMock.mockRejectedValueOnce(errorMessage);
const repos = await gitHelper.getRepos();
expect(repos.length).toBe(0);
expect(repos).toHaveLength(0);
const { error } = require("logger");
expect(error).toHaveBeenCalledWith(`Failed to retrieve repo info for ${moduleName}: Failed to retrieve status`);
@@ -224,7 +224,7 @@ describe("Updatenotification", () => {
execMock.mockRejectedValueOnce(errorMessage);
const repos = await gitHelper.getRepos();
expect(repos.length).toBe(0);
expect(repos).toHaveLength(0);
const { error } = require("logger");
expect(error).toHaveBeenCalledWith(`Failed to retrieve repo info for ${moduleName}: Failed to retrieve status`);

View File

@@ -14,11 +14,11 @@ describe("'global.root_path' set in js/app.js", () => {
});
it("should not modify global.root_path for testing", () => {
expect(global.root_path).toBe(undefined);
expect(global.root_path).toBeUndefined();
});
it("should not modify global.version for testing", () => {
expect(global.version).toBe(undefined);
expect(global.version).toBeUndefined();
});
it("should expect the global.version equals package.json file", () => {

View File

@@ -1,3 +1,3 @@
module.exports = async () => {
process.env.TZ = "Europe/Berlin";
process.env.TZ = "UTC";
};

View File

@@ -0,0 +1,29 @@
global.moment = require("moment-timezone");
const CalendarFetcherUtils = require("../../../../../modules/default/calendar/calendarfetcherutils");
describe("Calendar fetcher utils test", () => {
const defaultConfig = {
excludedEvents: []
};
describe("filterEvents", () => {
it("no events, not crash", () => {
const minusOneHour = moment().subtract(1, "hours").toDate();
const minusTwoHours = moment().subtract(2, "hours").toDate();
const plusOneHour = moment().add(1, "hours").toDate();
const plusTwoHours = moment().add(2, "hours").toDate();
const filteredEvents = CalendarFetcherUtils.filterEvents(
{
pastEvent: { type: "VEVENT", start: minusTwoHours, end: minusOneHour, summary: "pastEvent" },
ongoingEvent: { type: "VEVENT", start: minusOneHour, end: plusOneHour, summary: "ongoingEvent" },
upcomingEvent: { type: "VEVENT", start: plusOneHour, end: plusTwoHours, summary: "upcomingEvent" }
},
defaultConfig
);
expect(filteredEvents).toHaveLength(0);
});
});
});

View File

@@ -26,7 +26,7 @@ describe("Calendar fetcher utils test", () => {
defaultConfig
);
expect(filteredEvents.length).toEqual(2);
expect(filteredEvents).toHaveLength(2);
expect(filteredEvents[0].title).toBe("ongoingEvent");
expect(filteredEvents[1].title).toBe("upcomingEvent");
});
@@ -45,7 +45,7 @@ describe("Calendar fetcher utils test", () => {
defaultConfig
);
expect(filteredEvents.length).toEqual(2);
expect(filteredEvents).toHaveLength(2);
expect(filteredEvents[0].title).toBe("ongoingEvent");
expect(filteredEvents[1].title).toBe("upcomingEvent");
});

View File

@@ -138,11 +138,16 @@ describe("Calendar utils tests", () => {
describe("titleTransform and shorten combined", () => {
it("should replace the birthday and wrap nicely", () => {
const transformedTitle = CalendarUtils.titleTransform("Michael Teeuw's birthday", {
"De verjaardag van ": "",
"'s birthday": ""
});
const transformedTitle = CalendarUtils.titleTransform("Michael Teeuw's birthday", [{ search: "'s birthday", replace: "" }]);
expect(CalendarUtils.shorten(transformedTitle, 10, true, 2)).toBe("Michael <br>Teeuw");
});
});
describe("titleTransform with yearmatchgroup", () => {
it("should replace the birthday and wrap nicely", () => {
const transformedTitle = CalendarUtils.titleTransform("Luciella '2000", [{ search: "^([^']*) '(\\d{4})$", replace: "$1 ($2.)", yearmatchgroup: 2 }]);
const expectedResult = `Luciella (${new Date().getFullYear() - 2000}.)`;
expect(transformedTitle).toBe(expectedResult);
});
});
});

View File

@@ -24,16 +24,16 @@ describe("Default modules utils tests", () => {
}
});
test("Calls correct URL once", async () => {
it("Calls correct URL once", async () => {
urlToCall = "http://www.test.com/path?param1=value1";
await performWebRequest(urlToCall, "json", true);
expect(fetchMock.mock.calls.length).toBe(1);
expect(fetchMock.mock.calls).toHaveLength(1);
expect(fetchMock.mock.calls[0][0]).toBe(`${locationProtocol}//${locationHost}/cors?url=${urlToCall}`);
});
test("Sends correct headers", async () => {
it("Sends correct headers", async () => {
urlToCall = "http://www.test.com/path?param1=value1";
const headers = [
@@ -43,22 +43,22 @@ describe("Default modules utils tests", () => {
await performWebRequest(urlToCall, "json", true, headers);
expect(fetchMock.mock.calls.length).toBe(1);
expect(fetchMock.mock.calls).toHaveLength(1);
expect(fetchMock.mock.calls[0][0]).toBe(`${locationProtocol}//${locationHost}/cors?sendheaders=header1:value1,header2:value2&url=${urlToCall}`);
});
});
describe("When not using cors proxy", () => {
test("Calls correct URL once", async () => {
it("Calls correct URL once", async () => {
urlToCall = "http://www.test.com/path?param1=value1";
await performWebRequest(urlToCall);
expect(fetchMock.mock.calls.length).toBe(1);
expect(fetchMock.mock.calls).toHaveLength(1);
expect(fetchMock.mock.calls[0][0]).toBe(urlToCall);
});
test("Sends correct headers", async () => {
it("Sends correct headers", async () => {
urlToCall = "http://www.test.com/path?param1=value1";
const headers = [
{ name: "header1", value: "value1" },
@@ -68,36 +68,36 @@ describe("Default modules utils tests", () => {
await performWebRequest(urlToCall, "json", false, headers);
const expectedHeaders = { headers: { header1: "value1", header2: "value2" } };
expect(fetchMock.mock.calls.length).toBe(1);
expect(fetchMock.mock.calls).toHaveLength(1);
expect(fetchMock.mock.calls[0][1]).toStrictEqual(expectedHeaders);
});
});
describe("When receiving json format", () => {
test("Returns undefined when no data is received", async () => {
it("Returns undefined when no data is received", async () => {
urlToCall = "www.test.com";
const response = await performWebRequest(urlToCall);
expect(response).toBe(undefined);
expect(response).toBeUndefined();
});
test("Returns object when data is received", async () => {
it("Returns object when data is received", async () => {
urlToCall = "www.test.com";
fetchResponse = new Response('{"body": "some content"}');
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 () => {
it("Returns expected headers when data is received", async () => {
urlToCall = "www.test.com";
fetchResponse = new Response('{"body": "some content"}', { headers: { header1: "value1", header2: "value2" } });
fetchResponse = new Response("{\"body\": \"some content\"}", { headers: { header1: "value1", header2: "value2" } });
const response = await performWebRequest(urlToCall, "json", false, undefined, ["header1"]);
expect(response.headers.length).toBe(1);
expect(response.headers).toHaveLength(1);
expect(response.headers[0].name).toBe("header1");
expect(response.headers[0].value).toBe("value1");
});
@@ -150,17 +150,5 @@ describe("Default modules utils tests", () => {
)
).toBe("1:13");
});
it("should convert correctly into another timezone", () => {
expect(
formatTime(
{
timeFormat: 24,
timezone: "America/Toronto"
},
time
)
).toBe("07:13");
});
});
});

View File

@@ -2,7 +2,13 @@ const weather = require("../../../../../modules/default/weather/weatherutils");
const WeatherUtils = require("../../../../../modules/default/weather/weatherutils");
describe("Weather utils tests", () => {
describe("windspeed conversion", () => {
describe("windspeed conversion to imperial", () => {
it("should convert temp correctly from Celsius to Fahrenheit", () => {
expect(Math.round(WeatherUtils.convertTemp(10, "imperial"))).toBe(50);
});
});
describe("windspeed conversion to beaufort", () => {
it("should convert windspeed correctly from mps to beaufort", () => {
expect(Math.round(WeatherUtils.convertWind(5, "beaufort"))).toBe(3);
expect(Math.round(WeatherUtils.convertWind(300, "beaufort"))).toBe(12);
@@ -32,15 +38,16 @@ describe("Weather utils tests", () => {
describe("wind direction conversion", () => {
it("should convert wind direction correctly from cardinal to value", () => {
expect(WeatherUtils.convertWindDirection("SSE")).toBe(157);
expect(WeatherUtils.convertWindDirection("XXX")).toBeNull();
});
});
describe("feelsLike calculation", () => {
it("should return a calculated feelsLike info", () => {
it("should return a calculated feelsLike info (negative value)", () => {
expect(WeatherUtils.calculateFeelsLike(0, 20, 40)).toBe(-9.444444444444445);
});
it("should return a calculated feelsLike info", () => {
it("should return a calculated feelsLike info (positiv value)", () => {
expect(WeatherUtils.calculateFeelsLike(30, 0, 60)).toBe(32.8320322777777);
});
});

View File

@@ -1,7 +1,7 @@
const TestSequencer = require("@jest/test-sequencer").default;
class CustomSequencer extends TestSequencer {
sort(tests) {
sort (tests) {
const orderPath = ["unit", "electron", "e2e"];
return tests.sort((testA, testB) => {
let indexA = -1;