├── tests
├── configs
│ ├── port_variable.env
│ ├── without_modules.js
│ ├── port_8090.js
│ ├── port_variable.js.template
│ ├── empty_ipWhiteList.js
│ ├── noIpWhiteList.js
│ ├── modules
│ │ ├── clock
│ │ │ ├── clock_24hr.js
│ │ │ ├── clock_12hr.js
│ │ │ ├── es
│ │ │ │ ├── clock_24hr.js
│ │ │ │ ├── clock_12hr.js
│ │ │ │ ├── clock_showWeek.js
│ │ │ │ ├── clock_showWeek_short.js
│ │ │ │ └── clock_showPeriodUpper.js
│ │ │ ├── clock_showWeek.js
│ │ │ ├── clock_showTime.js
│ │ │ ├── clock_showWeek_short.js
│ │ │ ├── clock_showPeriodUpper.js
│ │ │ ├── clock_displaySeconds_false.js
│ │ │ ├── clock_showSunNoEvent.js
│ │ │ ├── de
│ │ │ │ ├── clock_showWeek.js
│ │ │ │ └── clock_showWeek_short.js
│ │ │ ├── clock_analog.js
│ │ │ ├── clock_showSunMoon.js
│ │ │ └── clock_showDateAnalog.js
│ │ ├── helloworld
│ │ │ ├── helloworld_default.js
│ │ │ └── helloworld.js
│ │ ├── alert
│ │ │ ├── welcome_true.js
│ │ │ ├── welcome_false.js
│ │ │ └── welcome_string.js
│ │ ├── compliments
│ │ │ ├── compliments_remote.js
│ │ │ ├── compliments_file.js
│ │ │ ├── compliments_evening.js
│ │ │ ├── compliments_only_anytime.js
│ │ │ ├── compliments_e2e_cron_entry.js
│ │ │ ├── compliments_cron_entry.js
│ │ │ ├── compliments_anytime.js
│ │ │ ├── compliments_date.js
│ │ │ ├── compliments_animateCSS.js
│ │ │ ├── compliments_animateCSS_fallbackToDefault.js
│ │ │ ├── compliments_animateCSS_invertedAnimationName.js
│ │ │ ├── compliments_file_change.js
│ │ │ ├── compliments_specialDayUnique_false.js
│ │ │ ├── compliments_specialDayUnique_true.js
│ │ │ └── compliments_parts_day.js
│ │ ├── newsfeed
│ │ │ ├── incorrect_url.js
│ │ │ ├── default.js
│ │ │ ├── ignore_items.js
│ │ │ └── prohibited_words.js
│ │ ├── calendar
│ │ │ ├── default.js
│ │ │ ├── bad_rrule.js
│ │ │ ├── recurring.js
│ │ │ ├── fullday_until.js
│ │ │ ├── old-basic-auth.js
│ │ │ ├── auth-default.js
│ │ │ ├── changed-port.js
│ │ │ ├── symboltest.js
│ │ │ ├── rrule_until.js
│ │ │ ├── sliceMultiDayEvents.js
│ │ │ ├── basic-auth.js
│ │ │ ├── fail-basic-auth.js
│ │ │ ├── single-fullday-event.js
│ │ │ ├── long-fullday-event.js
│ │ │ ├── berlin_multi.js
│ │ │ ├── germany_at_end_of_day_repeating.js
│ │ │ ├── chicago-looking-at-ny-recurring.js
│ │ │ ├── end_of_day_berlin_moved.js
│ │ │ ├── berlin_end_of_day_repeating.js
│ │ │ ├── fullday_event_over_multiple_days_nonrepeating.js
│ │ │ ├── exdate_and_recurrence_together.js
│ │ │ ├── diff_tz_start_end.js
│ │ │ ├── berlin_whole_day_event_moved_over_dst_change.js
│ │ │ ├── chicago_late_in_timezone.js
│ │ │ ├── exdate_la_at_midnight_dst.js
│ │ │ ├── exdate_la_at_midnight_std.js
│ │ │ ├── exdate_la_before_midnight.js
│ │ │ ├── exdate_syd_at_midnight_dst.js
│ │ │ ├── exdate_syd_at_midnight_std.js
│ │ │ ├── exdate_syd_before_midnight.js
│ │ │ ├── event_with_time_over_multiple_days_non_repeating_display_end.js
│ │ │ ├── show-duplicates-in-calendar.js
│ │ │ ├── custom.js
│ │ │ ├── 3_move_first_allday_repeating_event.js
│ │ │ ├── event_with_time_over_multiple_days_non_repeating_no_display_end.js
│ │ │ └── countCalendarEvents.js
│ │ ├── display.js
│ │ ├── weather
│ │ │ ├── hourlyweather_default.js
│ │ │ ├── currentweather_default.js
│ │ │ ├── forecastweather_default.js
│ │ │ ├── currentweather_units.js
│ │ │ ├── hourlyweather_options.js
│ │ │ ├── forecastweather_absolute.js
│ │ │ ├── forecastweather_units.js
│ │ │ ├── hourlyweather_showPrecipitation.js
│ │ │ ├── forecastweather_options.js
│ │ │ ├── currentweather_options.js
│ │ │ └── currentweather_compliments.js
│ │ └── positions.js
│ ├── default.js
│ └── customregions.js
├── mocks
│ ├── compliments_file.json
│ ├── compliments_test.json
│ ├── diff_tz_start_end.ics
│ ├── event_with_time_over_multiple_days_non_repeating.ics
│ ├── chicago-nyedge.ics
│ ├── germany_at_end_of_day_repeating.ics
│ ├── fullday_event_over_multiple_days_nonrepeating.ics
│ ├── exdate_syd_at_midnight_dst.ics
│ ├── exdate_syd_at_midnight_std.ics
│ ├── exdate_syd_before_midnight.ics
│ ├── exdate_la_at_midnight_dst.ics
│ ├── exdate_la_at_midnight_std.ics
│ ├── exdate_la_before_midnight.ics
│ ├── chicago_late_in_timezone.ics
│ ├── rrule_until.ics
│ ├── whole_day_moved_over_dst_change_berlin.ics
│ ├── RepeatingEvent.Oct21.ics
│ ├── calendar_test_full_day_events.ics
│ ├── weather_current.json
│ ├── calendar_test_recurring.ics
│ ├── calendar_test_multi_day_starting_today.ics
│ ├── translation_test.json
│ ├── fullday_until.ics
│ ├── bad_rrule.ics
│ ├── 3_move_first_allday_repeating_event.ics
│ ├── end_of_day_berlin_moved.ics
│ ├── sliceMultiDayEvents.ics
│ ├── testNotification
│ │ └── testNotification.js
│ └── exdate_and_recurrence_together.ics
├── unit
│ ├── helpers
│ │ └── global-setup.js
│ ├── classes
│ │ ├── utils_spec.js
│ │ └── deprecated_spec.js
│ ├── global_vars
│ │ ├── defaults_modules_spec.js
│ │ └── root_path_spec.js
│ ├── functions
│ │ └── cmp_versions_spec.js
│ └── modules
│ │ └── default
│ │ ├── calendar
│ │ └── calendar_fetcher_utils_bad_rrule.js
│ │ └── weather
│ │ └── weather_object_spec.js
├── e2e
│ ├── template_spec.js
│ ├── vendor_spec.js
│ ├── helpers
│ │ ├── basic-auth.js
│ │ └── weather-functions.js
│ ├── modules_empty_spec.js
│ ├── modules_position_spec.js
│ ├── env_spec.js
│ ├── ipWhitelist_spec.js
│ ├── port_spec.js
│ ├── modules_display_spec.js
│ ├── fonts_spec.js
│ ├── custom_module_regions_spec.js
│ ├── modules
│ │ ├── clock_de_spec.js
│ │ └── helloworld_spec.js
│ └── serveronly_spec.js
├── electron
│ ├── helpers
│ │ └── weather-setup.js
│ ├── env_spec.js
│ └── modules
│ │ └── weather_spec.js
└── utils
│ ├── test_sequencer.js
│ └── weather_mocker.js
├── .npmrc
├── mm2.png
├── .github
├── FUNDING.yaml
├── header.png
├── dependabot.yaml
├── workflows
│ ├── dep-review.yaml
│ ├── stale.yaml
│ ├── spellcheck.yaml
│ ├── electron-rebuild.yaml
│ └── enforce-pullrequest-rules.yaml
├── ISSUE_TEMPLATE
│ ├── config.yml
│ └── change_request.yml
├── PULL_REQUEST_TEMPLATE.md
└── CONTRIBUTING.md
├── .husky
└── pre-commit
├── js
├── deprecated.js
├── module_functions.js
├── vendor.js
└── socketclient.js
├── modules
└── default
│ ├── alert
│ ├── styles
│ │ ├── left.css
│ │ ├── right.css
│ │ └── center.css
│ ├── translations
│ │ ├── eo.json
│ │ ├── hu.json
│ │ ├── nl.json
│ │ ├── en.json
│ │ ├── bg.json
│ │ ├── de.json
│ │ ├── el.json
│ │ ├── ru.json
│ │ ├── th.json
│ │ ├── da.json
│ │ ├── es.json
│ │ └── fr.json
│ ├── README.md
│ └── templates
│ │ ├── notification.njk
│ │ └── alert.njk
│ ├── newsfeed
│ ├── fullarticle.njk
│ ├── oldconfig.njk
│ ├── README.md
│ └── newsfeed.css
│ ├── updatenotification
│ ├── updatenotification.css
│ ├── README.md
│ └── updatenotification.njk
│ ├── helloworld
│ ├── helloworld.njk
│ ├── helloworld.js
│ └── README.md
│ ├── weather
│ ├── providers
│ │ └── README.md
│ ├── README.md
│ └── weather.css
│ ├── calendar
│ ├── calendar.css
│ ├── README.md
│ └── debug.js
│ ├── compliments
│ └── README.md
│ ├── clock
│ ├── README.md
│ └── faces
│ │ └── face-002.svg
│ └── defaultmodules.js
├── .prettierignore
├── .markdownlint.json
├── css
├── font-awesome.css
└── custom.css.sample
├── stylelint.config.mjs
├── .editorconfig
├── jsconfig.json
├── prettier.config.mjs
├── serveronly
└── index.js
├── translations
├── ps.json
├── zh-cn.json
├── zh-tw.json
├── ko.json
├── ja.json
├── tlh.json
├── cv.json
├── he.json
├── ar.json
├── tr.json
├── en.json
├── hi.json
├── id.json
├── is.json
├── lt.json
├── gu.json
├── cs.json
├── cy.json
├── fy.json
├── hu.json
├── pl.json
├── da.json
├── fi.json
├── ms-my.json
├── hr.json
├── pt-br.json
├── ru.json
└── sk.json
├── LICENSE.md
├── jest.config.js
├── module-types.ts
├── .gitattributes
└── .gitignore
/tests/configs/port_variable.env:
--------------------------------------------------------------------------------
1 | MM_PORT=8090
2 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 | audit=false
3 | loglevel="error"
4 |
--------------------------------------------------------------------------------
/mm2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MagicMirrorOrg/MagicMirror/HEAD/mm2.png
--------------------------------------------------------------------------------
/tests/mocks/compliments_file.json:
--------------------------------------------------------------------------------
1 | {
2 | "anytime": ["test in morning"]
3 | }
4 |
--------------------------------------------------------------------------------
/.github/FUNDING.yaml:
--------------------------------------------------------------------------------
1 | github: MichMich
2 | custom: ["https://magicmirror.builders/#donate"]
3 |
--------------------------------------------------------------------------------
/.github/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MagicMirrorOrg/MagicMirror/HEAD/.github/header.png
--------------------------------------------------------------------------------
/tests/mocks/compliments_test.json:
--------------------------------------------------------------------------------
1 | {
2 | "anytime": ["Remote compliment file works!"]
3 | }
4 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if command -v npx &> /dev/null; then
4 | npx lint-staged
5 | fi
6 |
--------------------------------------------------------------------------------
/js/deprecated.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | configs: ["kioskmode"],
3 | clock: ["secondsColor"]
4 | };
5 |
--------------------------------------------------------------------------------
/modules/default/alert/styles/left.css:
--------------------------------------------------------------------------------
1 | .ns-box {
2 | margin-right: auto;
3 | text-align: left;
4 | }
5 |
--------------------------------------------------------------------------------
/modules/default/alert/styles/right.css:
--------------------------------------------------------------------------------
1 | .ns-box {
2 | margin-left: auto;
3 | text-align: right;
4 | }
5 |
--------------------------------------------------------------------------------
/tests/unit/helpers/global-setup.js:
--------------------------------------------------------------------------------
1 | module.exports = async () => {
2 | process.env.TZ = "UTC";
3 | };
4 |
--------------------------------------------------------------------------------
/modules/default/newsfeed/fullarticle.njk:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | *.js
2 | *.mjs
3 | .husky/pre-commit
4 | .prettierignore
5 | /config
6 | /coverage
7 | package-lock.json
8 | **.ics
9 |
--------------------------------------------------------------------------------
/modules/default/alert/styles/center.css:
--------------------------------------------------------------------------------
1 | .ns-box {
2 | margin-left: auto;
3 | margin-right: auto;
4 | text-align: center;
5 | }
6 |
--------------------------------------------------------------------------------
/modules/default/alert/translations/eo.json:
--------------------------------------------------------------------------------
1 | {
2 | "sysTitle": "MagicMirror²-sciigo",
3 | "welcome": "Bonvenon, lanĉo sukcesis!"
4 | }
5 |
--------------------------------------------------------------------------------
/modules/default/updatenotification/updatenotification.css:
--------------------------------------------------------------------------------
1 | .module.updatenotification a.difflink {
2 | text-decoration: none;
3 | }
4 |
--------------------------------------------------------------------------------
/modules/default/alert/translations/hu.json:
--------------------------------------------------------------------------------
1 | {
2 | "sysTitle": "MagicMirror² értesítés",
3 | "welcome": "Üdvözöljük, indulás sikeres!"
4 | }
5 |
--------------------------------------------------------------------------------
/modules/default/alert/translations/nl.json:
--------------------------------------------------------------------------------
1 | {
2 | "sysTitle": "MagicMirror² Notificatie",
3 | "welcome": "Welkom, Succesvol gestart!"
4 | }
5 |
--------------------------------------------------------------------------------
/modules/default/alert/translations/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "sysTitle": "MagicMirror² Notification",
3 | "welcome": "Welcome, start was successful!"
4 | }
5 |
--------------------------------------------------------------------------------
/modules/default/newsfeed/oldconfig.njk:
--------------------------------------------------------------------------------
1 | {{ "MODULE_CONFIG_CHANGED" | translate({MODULE_NAME: "Newsfeed"}) | safe }}
2 |
--------------------------------------------------------------------------------
/modules/default/alert/translations/bg.json:
--------------------------------------------------------------------------------
1 | {
2 | "sysTitle": "MagicMirror² нотификация",
3 | "welcome": "Добре дошли, стартирането беше успешно"
4 | }
5 |
--------------------------------------------------------------------------------
/modules/default/alert/translations/de.json:
--------------------------------------------------------------------------------
1 | {
2 | "sysTitle": "MagicMirror² Benachrichtigung",
3 | "welcome": "Willkommen, Start war erfolgreich!"
4 | }
5 |
--------------------------------------------------------------------------------
/modules/default/alert/translations/el.json:
--------------------------------------------------------------------------------
1 | {
2 | "sysTitle": "MagicMirror² Ειδοποίηση",
3 | "welcome": "Καλώς ήρθατε, η εκκίνηση ήταν επιτυχής!"
4 | }
5 |
--------------------------------------------------------------------------------
/modules/default/alert/translations/ru.json:
--------------------------------------------------------------------------------
1 | {
2 | "sysTitle": "MagicMirror² Уведомление",
3 | "welcome": "Добро пожаловать, старт был успешным!"
4 | }
5 |
--------------------------------------------------------------------------------
/modules/default/alert/translations/th.json:
--------------------------------------------------------------------------------
1 | {
2 | "sysTitle": "การแจ้งเตือน MagicMirror²",
3 | "welcome": "ยินดีต้อนรับ การเริ่มต้นสำเร็จแล้ว!"
4 | }
5 |
--------------------------------------------------------------------------------
/.markdownlint.json:
--------------------------------------------------------------------------------
1 | {
2 | "line_length": false,
3 | "no-duplicate-heading": false,
4 | "no-inline-html": false,
5 | "no-trailing-punctuation": false
6 | }
7 |
--------------------------------------------------------------------------------
/modules/default/alert/translations/da.json:
--------------------------------------------------------------------------------
1 | {
2 | "sysTitle": "MagicMirror² Notifikation",
3 | "welcome": "Velkommen, modulet er succesfuldt startet!"
4 | }
5 |
--------------------------------------------------------------------------------
/modules/default/alert/translations/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "sysTitle": "MagicMirror² Notificaciones",
3 | "welcome": "Bienvenido, ¡se iniciado correctamente!"
4 | }
5 |
--------------------------------------------------------------------------------
/modules/default/alert/translations/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "sysTitle": "MagicMirror² Notification",
3 | "welcome": "Bienvenue, le démarrage a été un succès!"
4 | }
5 |
--------------------------------------------------------------------------------
/css/font-awesome.css:
--------------------------------------------------------------------------------
1 | @import url("../node_modules/@fortawesome/fontawesome-free/css/all.min.css");
2 | @import url("../node_modules/@fortawesome/fontawesome-free/css/v4-shims.min.css");
3 |
--------------------------------------------------------------------------------
/stylelint.config.mjs:
--------------------------------------------------------------------------------
1 | const config = {
2 | extends: ["stylelint-config-standard", "stylelint-prettier/recommended"],
3 | root: true,
4 | rules: {}
5 | };
6 |
7 | export default config;
8 |
--------------------------------------------------------------------------------
/modules/default/helloworld/helloworld.njk:
--------------------------------------------------------------------------------
1 |
5 | {{ text | safe }}
6 |
--------------------------------------------------------------------------------
/tests/configs/without_modules.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: []
4 | };
5 |
6 | /*************** DO NOT EDIT THE LINE BELOW ***************/
7 | if (typeof module !== "undefined") {
8 | module.exports = config;
9 | }
10 |
--------------------------------------------------------------------------------
/modules/default/weather/providers/README.md:
--------------------------------------------------------------------------------
1 | # Weather Module Weather Provider Development Documentation
2 |
3 | For how to develop your own weather provider, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/development/weather-provider.html).
4 |
--------------------------------------------------------------------------------
/tests/unit/classes/utils_spec.js:
--------------------------------------------------------------------------------
1 | const Utils = require("../../../js/utils");
2 |
3 | describe("Utils", () => {
4 | it("should output system information", async () => {
5 | await expect(Utils.logSystemInformation()).resolves.toContain("platform: linux");
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/tests/configs/port_8090.js:
--------------------------------------------------------------------------------
1 | let config = require(`${process.cwd()}/tests/configs/default.js`).configFactory({
2 | port: 8090
3 | });
4 |
5 | /*************** DO NOT EDIT THE LINE BELOW ***************/
6 | if (typeof module !== "undefined") {
7 | module.exports = config;
8 | }
9 |
--------------------------------------------------------------------------------
/tests/configs/port_variable.js.template:
--------------------------------------------------------------------------------
1 | let config = require(`${process.cwd()}/tests/configs/default.js`).configFactory({
2 | port: ${MM_PORT}
3 | });
4 |
5 | /*************** DO NOT EDIT THE LINE BELOW ***************/
6 | if (typeof module !== "undefined") {
7 | module.exports = config;
8 | }
9 |
--------------------------------------------------------------------------------
/modules/default/helloworld/helloworld.js:
--------------------------------------------------------------------------------
1 | Module.register("helloworld", {
2 | // Default module config.
3 | defaults: {
4 | text: "Hello World!"
5 | },
6 |
7 | getTemplate () {
8 | return "helloworld.njk";
9 | },
10 |
11 | getTemplateData () {
12 | return this.config;
13 | }
14 | });
15 |
--------------------------------------------------------------------------------
/modules/default/alert/README.md:
--------------------------------------------------------------------------------
1 | # Module: Alert
2 |
3 | The alert module is one of the default modules of the MagicMirror². This module displays notifications from other modules.
4 |
5 | For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/alert.html).
6 |
--------------------------------------------------------------------------------
/modules/default/calendar/calendar.css:
--------------------------------------------------------------------------------
1 | .calendar .symbol {
2 | display: flex;
3 | flex-direction: row;
4 | justify-content: flex-end;
5 | gap: 5px;
6 | }
7 |
8 | .calendar .title {
9 | padding: 0 10px;
10 | }
11 |
12 | .calendar .time {
13 | padding-left: 20px;
14 | text-align: right;
15 | }
16 |
--------------------------------------------------------------------------------
/tests/configs/empty_ipWhiteList.js:
--------------------------------------------------------------------------------
1 | let config = require(`${process.cwd()}/tests/configs/default.js`).configFactory({
2 | ipWhitelist: [],
3 | port: 8282
4 | });
5 |
6 | /*************** DO NOT EDIT THE LINE BELOW ***************/
7 | if (typeof module !== "undefined") {
8 | module.exports = config;
9 | }
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_size = 2
8 | indent_style = space
9 | insert_final_newline = true
10 | max_line_length = 250
11 | trim_trailing_whitespace = true
12 |
13 | [*.{js,json}]
14 | indent_size = 4
15 | indent_style = tab
16 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=759670
3 | // for the documentation about the jsconfig.json format
4 | "compilerOptions": {
5 | "target": "es6",
6 | "module": "commonjs",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "exclude": ["modules", "node_modules"]
10 | }
11 |
--------------------------------------------------------------------------------
/tests/configs/noIpWhiteList.js:
--------------------------------------------------------------------------------
1 | let config = require(`${process.cwd()}/tests/configs/default.js`).configFactory({
2 | ipWhitelist: ["x.x.x.x"],
3 | port: 8181
4 | });
5 |
6 | /*************** DO NOT EDIT THE LINE BELOW ***************/
7 | if (typeof module !== "undefined") {
8 | module.exports = config;
9 | }
10 |
--------------------------------------------------------------------------------
/modules/default/compliments/README.md:
--------------------------------------------------------------------------------
1 | # Module: Compliments
2 |
3 | The `compliments` module is one of the default modules of the MagicMirror².
4 | This module displays a random compliment.
5 |
6 | For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/compliments.html).
7 |
--------------------------------------------------------------------------------
/modules/default/helloworld/README.md:
--------------------------------------------------------------------------------
1 | # Module: Hello World
2 |
3 | The `helloworld` module is one of the default modules of the MagicMirror². It is a simple way to display a static text on the mirror.
4 |
5 | For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/helloworld.html).
6 |
--------------------------------------------------------------------------------
/modules/default/alert/templates/notification.njk:
--------------------------------------------------------------------------------
1 | {% if title %}
2 | {{ title if titleType == 'text' else title | safe }}
3 | {% endif %}
4 | {% if message %}
5 | {% if title %}
{% endif %}
6 | {{ message if messageType == 'text' else message | safe }}
7 | {% endif %}
8 |
--------------------------------------------------------------------------------
/modules/default/clock/README.md:
--------------------------------------------------------------------------------
1 | # Module: Clock
2 |
3 | The `clock` module is one of the default modules of the MagicMirror².
4 | This module displays the current date and time. The information will be updated realtime.
5 |
6 | For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/clock.html).
7 |
--------------------------------------------------------------------------------
/modules/default/weather/README.md:
--------------------------------------------------------------------------------
1 | # Weather Module
2 |
3 | This module will be configurable to be used as a current weather view, or to show the forecast. This way the module can be used twice to fulfill both purposes.
4 |
5 | For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/weather.html).
6 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/clock_24hr.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "clock",
7 | position: "middle_center"
8 | }
9 | ]
10 | };
11 |
12 | /*************** DO NOT EDIT THE LINE BELOW ***************/
13 | if (typeof module !== "undefined") {
14 | module.exports = config;
15 | }
16 |
--------------------------------------------------------------------------------
/modules/default/calendar/README.md:
--------------------------------------------------------------------------------
1 | # Module: Calendar
2 |
3 | The `calendar` module is one of the default modules of the MagicMirror².
4 | This module displays events from a public .ical calendar. It can combine multiple calendars.
5 |
6 | For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/calendar.html).
7 |
--------------------------------------------------------------------------------
/tests/configs/modules/helloworld/helloworld_default.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "helloworld",
7 | position: "bottom_bar"
8 | }
9 | ]
10 | };
11 |
12 | /*************** DO NOT EDIT THE LINE BELOW ***************/
13 | if (typeof module !== "undefined") {
14 | module.exports = config;
15 | }
16 |
--------------------------------------------------------------------------------
/prettier.config.mjs:
--------------------------------------------------------------------------------
1 | const config = {
2 | plugins: ["prettier-plugin-jinja-template"],
3 | overrides: [
4 | {
5 | files: "*.md",
6 | options: {
7 | parser: "markdown"
8 | }
9 | },
10 | {
11 | files: ["*.njk"],
12 | options: {
13 | parser: "jinja-template"
14 | }
15 | }
16 | ],
17 | trailingComma: "none"
18 | };
19 |
20 | export default config;
21 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/clock_12hr.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "clock",
9 | position: "middle_center"
10 | }
11 | ]
12 | };
13 |
14 | /*************** DO NOT EDIT THE LINE BELOW ***************/
15 | if (typeof module !== "undefined") {
16 | module.exports = config;
17 | }
18 |
--------------------------------------------------------------------------------
/tests/mocks/diff_tz_start_end.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VCALENDAR
2 | BEGIN:VEVENT
3 | DTSTART:20241029T100000Z
4 | DTEND:20241030T230000Z
5 | DTSTAMP:20241022T203806Z
6 | UID:04ivnntdi20rqsk0iesabsdhmj@google.com
7 | CREATED:20241022T203738Z
8 | LAST-MODIFIED:20241022T203738Z
9 | SEQUENCE:0
10 | STATUS:CONFIRMED
11 | SUMMARY:start/end on diff tz
12 | TRANSP:OPAQUE
13 | END:VEVENT
14 | END:VCALENDAR
15 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/es/clock_24hr.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | language: "es",
5 |
6 | modules: [
7 | {
8 | module: "clock",
9 | position: "middle_center"
10 | }
11 | ]
12 | };
13 |
14 | /*************** DO NOT EDIT THE LINE BELOW ***************/
15 | if (typeof module !== "undefined") {
16 | module.exports = config;
17 | }
18 |
--------------------------------------------------------------------------------
/serveronly/index.js:
--------------------------------------------------------------------------------
1 | const app = require("../js/app");
2 | const Log = require("../js/logger");
3 |
4 | app.start().then((config) => {
5 | const bindAddress = config.address ? config.address : "localhost";
6 | const httpType = config.useHttps ? "https" : "http";
7 | Log.info(`\n>>> Ready to go! Please point your browser to: ${httpType}://${bindAddress}:${global.mmPort || config.port} <<<`);
8 | });
9 |
--------------------------------------------------------------------------------
/modules/default/updatenotification/README.md:
--------------------------------------------------------------------------------
1 | # Module: Update Notification
2 |
3 | The `updatenotification` module is one of the default modules of the MagicMirror².
4 | This will display a message whenever a new version of the MagicMirror² application is available.
5 |
6 | For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/updatenotification.html).
7 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/es/clock_12hr.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | language: "es",
5 | timeFormat: 12,
6 |
7 | modules: [
8 | {
9 | module: "clock",
10 | position: "middle_center"
11 | }
12 | ]
13 | };
14 |
15 | /*************** DO NOT EDIT THE LINE BELOW ***************/
16 | if (typeof module !== "undefined") {
17 | module.exports = config;
18 | }
19 |
--------------------------------------------------------------------------------
/tests/configs/modules/alert/welcome_true.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "alert",
7 | config: {
8 | display_time: 1000000,
9 | welcome_message: true
10 | }
11 | }
12 | ]
13 | };
14 |
15 | /*************** DO NOT EDIT THE LINE BELOW ***************/
16 | if (typeof module !== "undefined") {
17 | module.exports = config;
18 | }
19 |
--------------------------------------------------------------------------------
/tests/mocks/event_with_time_over_multiple_days_non_repeating.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VCALENDAR
2 | BEGIN:VEVENT
3 | DTSTART:20241026T010000Z
4 | DTEND:20241026T110000Z
5 | DTSTAMP:20241024T153358Z
6 | UID:4maud6s79m41a99pj2g7j5km0a@google.com
7 | CREATED:20241024T153313Z
8 | LAST-MODIFIED:20241024T153330Z
9 | SEQUENCE:0
10 | STATUS:CONFIRMED
11 | SUMMARY:Sleep over at Bobs
12 | TRANSP:OPAQUE
13 | END:VEVENT
14 | END:VCALENDAR
15 |
--------------------------------------------------------------------------------
/tests/configs/modules/alert/welcome_false.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "alert",
7 | config: {
8 | display_time: 1000000,
9 | welcome_message: false
10 | }
11 | }
12 | ]
13 | };
14 |
15 | /*************** DO NOT EDIT THE LINE BELOW ***************/
16 | if (typeof module !== "undefined") {
17 | module.exports = config;
18 | }
19 |
--------------------------------------------------------------------------------
/tests/configs/modules/helloworld/helloworld.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "helloworld",
7 | position: "bottom_bar",
8 | config: {
9 | text: "Test HelloWorld Module"
10 | }
11 | }
12 | ]
13 | };
14 |
15 | /*************** DO NOT EDIT THE LINE BELOW ***************/
16 | if (typeof module !== "undefined") {
17 | module.exports = config;
18 | }
19 |
--------------------------------------------------------------------------------
/tests/configs/modules/alert/welcome_string.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "alert",
7 | config: {
8 | display_time: 1000000,
9 | welcome_message: "Custom welcome message!"
10 | }
11 | }
12 | ]
13 | };
14 |
15 | /*************** DO NOT EDIT THE LINE BELOW ***************/
16 | if (typeof module !== "undefined") {
17 | module.exports = config;
18 | }
19 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/clock_showWeek.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "clock",
9 | position: "middle_center",
10 | config: {
11 | showWeek: true
12 | }
13 | }
14 | ]
15 | };
16 |
17 | /*************** DO NOT EDIT THE LINE BELOW ***************/
18 | if (typeof module !== "undefined") {
19 | module.exports = config;
20 | }
21 |
--------------------------------------------------------------------------------
/tests/configs/default.js:
--------------------------------------------------------------------------------
1 | if (typeof exports === "object") {
2 | // running in nodejs (not in browser)
3 | exports.configFactory = (options) => {
4 | return Object.assign(
5 | {
6 | electronOptions: {
7 | webPreferences: {
8 | nodeIntegration: true,
9 | enableRemoteModule: true,
10 | contextIsolation: false
11 | }
12 | },
13 |
14 | modules: []
15 | },
16 | options
17 | );
18 | };
19 | }
20 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/clock_showTime.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "clock",
9 | position: "middle_center",
10 | config: {
11 | showTime: false
12 | }
13 | }
14 | ]
15 | };
16 |
17 | /*************** DO NOT EDIT THE LINE BELOW ***************/
18 | if (typeof module !== "undefined") {
19 | module.exports = config;
20 | }
21 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/clock_showWeek_short.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "clock",
9 | position: "middle_center",
10 | config: {
11 | showWeek: "short"
12 | }
13 | }
14 | ]
15 | };
16 |
17 | /*************** DO NOT EDIT THE LINE BELOW ***************/
18 | if (typeof module !== "undefined") {
19 | module.exports = config;
20 | }
21 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/clock_showPeriodUpper.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "clock",
9 | position: "middle_center",
10 | config: {
11 | showPeriodUpper: true
12 | }
13 | }
14 | ]
15 | };
16 |
17 | /*************** DO NOT EDIT THE LINE BELOW ***************/
18 | if (typeof module !== "undefined") {
19 | module.exports = config;
20 | }
21 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/clock_displaySeconds_false.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "clock",
9 | position: "middle_center",
10 | config: {
11 | displaySeconds: false
12 | }
13 | }
14 | ]
15 | };
16 |
17 | /*************** DO NOT EDIT THE LINE BELOW ***************/
18 | if (typeof module !== "undefined") {
19 | module.exports = config;
20 | }
21 |
--------------------------------------------------------------------------------
/modules/default/defaultmodules.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Default Modules List
3 | * Modules listed below can be loaded without the 'default/' prefix. Omitting the default folder name.
4 | */
5 | const defaultModules = ["alert", "calendar", "clock", "compliments", "helloworld", "newsfeed", "updatenotification", "weather"];
6 |
7 | /*************** DO NOT EDIT THE LINE BELOW ***************/
8 | if (typeof module !== "undefined") {
9 | module.exports = defaultModules;
10 | }
11 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/clock_showSunNoEvent.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "clock",
9 | position: "middle_center",
10 | config: {
11 | showSunTimes: "disableNextEvent"
12 | }
13 | }
14 | ]
15 | };
16 |
17 | /*************** DO NOT EDIT THE LINE BELOW ***************/
18 | if (typeof module !== "undefined") {
19 | module.exports = config;
20 | }
21 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/de/clock_showWeek.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | language: "de",
5 | timeFormat: 12,
6 |
7 | modules: [
8 | {
9 | module: "clock",
10 | position: "middle_center",
11 | config: {
12 | showWeek: true
13 | }
14 | }
15 | ]
16 | };
17 |
18 | /*************** DO NOT EDIT THE LINE BELOW ***************/
19 | if (typeof module !== "undefined") {
20 | module.exports = config;
21 | }
22 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/es/clock_showWeek.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | language: "es",
5 | timeFormat: 12,
6 |
7 | modules: [
8 | {
9 | module: "clock",
10 | position: "middle_center",
11 | config: {
12 | showWeek: true
13 | }
14 | }
15 | ]
16 | };
17 |
18 | /*************** DO NOT EDIT THE LINE BELOW ***************/
19 | if (typeof module !== "undefined") {
20 | module.exports = config;
21 | }
22 |
--------------------------------------------------------------------------------
/tests/mocks/chicago-nyedge.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VEVENT
2 | DTSTART;TZID=America/New_York:20240918T183000
3 | DTEND;TZID=America/New_York:20240918T203000
4 | RRULE:FREQ=WEEKLY;BYDAY=WE
5 | EXDATE;TZID=America/New_York:20241127T183000
6 | EXDATE;TZID=America/New_York:20241225T183000
7 | DTSTAMP:20250122T045443Z
8 | UID:_@google.com
9 | CREATED:20240916T131843Z
10 | LAST-MODIFIED:20241222T235014Z
11 | SEQUENCE:0
12 | STATUS:CONFIRMED
13 | SUMMARY:Derby
14 | TRANSP:OPAQUE
15 | END:VEVENT
--------------------------------------------------------------------------------
/tests/configs/modules/clock/de/clock_showWeek_short.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | language: "de",
5 | timeFormat: 12,
6 |
7 | modules: [
8 | {
9 | module: "clock",
10 | position: "middle_center",
11 | config: {
12 | showWeek: "short"
13 | }
14 | }
15 | ]
16 | };
17 |
18 | /*************** DO NOT EDIT THE LINE BELOW ***************/
19 | if (typeof module !== "undefined") {
20 | module.exports = config;
21 | }
22 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/es/clock_showWeek_short.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | language: "es",
5 | timeFormat: 12,
6 |
7 | modules: [
8 | {
9 | module: "clock",
10 | position: "middle_center",
11 | config: {
12 | showWeek: "short"
13 | }
14 | }
15 | ]
16 | };
17 |
18 | /*************** DO NOT EDIT THE LINE BELOW ***************/
19 | if (typeof module !== "undefined") {
20 | module.exports = config;
21 | }
22 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/clock_analog.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "clock",
7 | position: "middle_center",
8 | config: {
9 | displayType: "analog",
10 | analogFace: "face-006",
11 | showDate: false
12 | }
13 | }
14 | ]
15 | };
16 |
17 | /*************** DO NOT EDIT THE LINE BELOW ***************/
18 | if (typeof module !== "undefined") {
19 | module.exports = config;
20 | }
21 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/clock_showSunMoon.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "clock",
9 | position: "middle_center",
10 | config: {
11 | showSunTimes: true,
12 | showMoonTimes: true
13 | }
14 | }
15 | ]
16 | };
17 |
18 | /*************** DO NOT EDIT THE LINE BELOW ***************/
19 | if (typeof module !== "undefined") {
20 | module.exports = config;
21 | }
22 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/es/clock_showPeriodUpper.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | language: "es",
5 | timeFormat: 12,
6 |
7 | modules: [
8 | {
9 | module: "clock",
10 | position: "middle_center",
11 | config: {
12 | showPeriodUpper: true
13 | }
14 | }
15 | ]
16 | };
17 |
18 | /*************** DO NOT EDIT THE LINE BELOW ***************/
19 | if (typeof module !== "undefined") {
20 | module.exports = config;
21 | }
22 |
--------------------------------------------------------------------------------
/tests/mocks/germany_at_end_of_day_repeating.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VCALENDAR
2 | BEGIN:VEVENT
3 | DTSTART;TZID=Europe/Berlin:20241022T230000
4 | DTEND;TZID=Europe/Berlin:20241023T000000
5 | RRULE:FREQ=DAILY;WKST=MO;COUNT=4
6 | DTSTAMP:20241009T153220Z
7 | UID:2m6mt1p89l2anl74915ur3hsgm@google.com
8 | CREATED:20241009T153058Z
9 | LAST-MODIFIED:20241009T153205Z
10 | SEQUENCE:0
11 | STATUS:CONFIRMED
12 | SUMMARY:TestCal_AllDayRepeatingEvent
13 | TRANSP:TRANSPARENT
14 | END:VEVENT
15 | END:VCALENDAR
--------------------------------------------------------------------------------
/modules/default/newsfeed/README.md:
--------------------------------------------------------------------------------
1 | # Module: News Feed
2 |
3 | The `newsfeed` module is one of the default modules of the MagicMirror².
4 | This module displays news headlines based on an RSS feed. Scrolling through news headlines happens time-based (`updateInterval`), but can also be controlled by sending news feed specific notifications to the module.
5 |
6 | For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/newsfeed.html).
7 |
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_remote.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "compliments",
7 | position: "middle_center",
8 | config: {
9 | remoteFile: "http://localhost:8080/tests/mocks/compliments_test.json"
10 | }
11 | }
12 | ]
13 | };
14 |
15 | /*************** DO NOT EDIT THE LINE BELOW ***************/
16 | if (typeof module !== "undefined") {
17 | module.exports = config;
18 | }
19 |
--------------------------------------------------------------------------------
/modules/default/newsfeed/newsfeed.css:
--------------------------------------------------------------------------------
1 | iframe.newsfeed-fullarticle {
2 | width: 100vw;
3 |
4 | /* very large height value to allow scrolling */
5 | height: 3000px;
6 | top: 0;
7 | left: 0;
8 | border: none;
9 | z-index: 1;
10 | }
11 |
12 | .region.bottom.bar.newsfeed-fullarticle {
13 | bottom: inherit;
14 | top: -90px;
15 | }
16 |
17 | .newsfeed-list {
18 | list-style: none;
19 | }
20 |
21 | .newsfeed-list li {
22 | text-align: justify;
23 | margin-bottom: 0.5em;
24 | }
25 |
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_file.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "compliments",
7 | position: "bottom_bar",
8 | config: {
9 | updateInterval: 3000,
10 | remoteFile: "http://localhost:8080/tests/mocks/compliments_test.json"
11 | }
12 | }
13 | ]
14 | };
15 |
16 | /*************** DO NOT EDIT THE LINE BELOW ***************/
17 | if (typeof module !== "undefined") { module.exports = config; }
18 |
--------------------------------------------------------------------------------
/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VCALENDAR
2 | BEGIN:VEVENT
3 | DTSTART;VALUE=DATE:20241025
4 | DTEND;VALUE=DATE:20241031
5 | DTSTAMP:20241023T141110Z
6 | UID:60nobfcu0ct96jgsh5nhcia24b@google.com
7 | CREATED:20241023T141019Z
8 | DESCRIPTION:test for all day end viewing
9 | LAST-MODIFIED:20241023T141019Z
10 | SEQUENCE:0
11 | STATUS:CONFIRMED
12 | SUMMARY:simple all day event over many days (not repeating)
13 | TRANSP:TRANSPARENT
14 | END:VEVENT
15 | END:VCALENDAR
16 |
--------------------------------------------------------------------------------
/tests/configs/modules/clock/clock_showDateAnalog.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "clock",
9 | position: "middle_center",
10 | config: {
11 | showTime: true,
12 | showDate: true,
13 | displayType: "analog"
14 | }
15 | }
16 | ]
17 | };
18 |
19 | /*************** DO NOT EDIT THE LINE BELOW ***************/
20 | if (typeof module !== "undefined") {
21 | module.exports = config;
22 | }
23 |
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_evening.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "compliments",
9 | position: "middle_center",
10 | config: {
11 | compliments: {
12 | evening: ["Evening here"]
13 | }
14 | }
15 | }
16 | ]
17 | };
18 |
19 | /*************** DO NOT EDIT THE LINE BELOW ***************/
20 | if (typeof module !== "undefined") {
21 | module.exports = config;
22 | }
23 |
--------------------------------------------------------------------------------
/tests/mocks/exdate_syd_at_midnight_dst.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VEVENT
2 | DTSTART;TZID=Australia/Sydney:20230920T110000
3 | DTEND;TZID=Australia/Sydney:20230920T111000
4 | RRULE:FREQ=WEEKLY;BYDAY=WE
5 | EXDATE;TZID=Australia/Sydney:20230927T110000
6 | EXDATE;TZID=Australia/Sydney:20231004T110000
7 | DTSTAMP:20231025T233434Z
8 | UID:sdflbkasuhdb5fkauglkb@google.com
9 | CREATED:20230306T193128Z
10 | LAST-MODIFIED:20231024T222515Z
11 | SEQUENCE:0
12 | STATUS:CONFIRMED
13 | SUMMARY:My Event
14 | TRANSP:OPAQUE
15 | END:VEVENT
--------------------------------------------------------------------------------
/tests/mocks/exdate_syd_at_midnight_std.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VEVENT
2 | DTSTART;TZID=Australia/Sydney:20230920T100000
3 | DTEND;TZID=Australia/Sydney:20230920T110000
4 | RRULE:FREQ=WEEKLY;BYDAY=WE
5 | EXDATE;TZID=Australia/Sydney:20230927T100000
6 | EXDATE;TZID=Australia/Sydney:20231004T100000
7 | DTSTAMP:20231025T233434Z
8 | UID:sdflbkasuhdb5fkauglkb@google.com
9 | CREATED:20230306T193128Z
10 | LAST-MODIFIED:20231024T222515Z
11 | SEQUENCE:0
12 | STATUS:CONFIRMED
13 | SUMMARY:My Event
14 | TRANSP:OPAQUE
15 | END:VEVENT
--------------------------------------------------------------------------------
/tests/mocks/exdate_syd_before_midnight.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VEVENT
2 | DTSTART;TZID=Australia/Sydney:20230920T090000
3 | DTEND;TZID=Australia/Sydney:20230920T100000
4 | RRULE:FREQ=WEEKLY;BYDAY=WE
5 | EXDATE;TZID=Australia/Sydney:20230927T090000
6 | EXDATE;TZID=Australia/Sydney:20231004T090000
7 | DTSTAMP:20231025T233434Z
8 | UID:sdflbkasuhdb5fkauglkb@google.com
9 | CREATED:20230306T193128Z
10 | LAST-MODIFIED:20231024T222515Z
11 | SEQUENCE:0
12 | STATUS:CONFIRMED
13 | SUMMARY:My Event
14 | TRANSP:OPAQUE
15 | END:VEVENT
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_only_anytime.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "compliments",
9 | position: "middle_center",
10 | config: {
11 | compliments: {
12 | anytime: ["Anytime here"]
13 | }
14 | }
15 | }
16 | ]
17 | };
18 |
19 | /*************** DO NOT EDIT THE LINE BELOW ***************/
20 | if (typeof module !== "undefined") {
21 | module.exports = config;
22 | }
23 |
--------------------------------------------------------------------------------
/.github/dependabot.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 | target-branch: "develop"
8 | labels:
9 | - "Skip Changelog"
10 | - "dependencies"
11 |
12 | - package-ecosystem: "npm"
13 | directory: "/"
14 | schedule:
15 | interval: "monthly"
16 | target-branch: "develop"
17 | labels:
18 | - "Skip Changelog"
19 | - "dependencies"
20 | - "javascript"
21 |
--------------------------------------------------------------------------------
/tests/mocks/exdate_la_at_midnight_dst.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VEVENT
2 | DTSTART;TZID=America/Los_Angeles:20231025T170000
3 | DTEND;TZID=America/Los_Angeles:20231025T180000
4 | RRULE:FREQ=WEEKLY;BYDAY=WE
5 | EXDATE;TZID=America/Los_Angeles:20231101T170000
6 | EXDATE;TZID=America/Los_Angeles:20231108T170000
7 | DTSTAMP:20231025T233434Z
8 | UID:sdflbkasuhdb5fkauglkb@google.com
9 | CREATED:20230306T193128Z
10 | LAST-MODIFIED:20231024T222515Z
11 | SEQUENCE:0
12 | STATUS:CONFIRMED
13 | SUMMARY:My Event
14 | TRANSP:OPAQUE
15 | END:VEVENT
--------------------------------------------------------------------------------
/tests/mocks/exdate_la_at_midnight_std.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VEVENT
2 | DTSTART;TZID=America/Los_Angeles:20231025T160000
3 | DTEND;TZID=America/Los_Angeles:20231025T170000
4 | RRULE:FREQ=WEEKLY;BYDAY=WE
5 | EXDATE;TZID=America/Los_Angeles:20231101T160000
6 | EXDATE;TZID=America/Los_Angeles:20231108T160000
7 | DTSTAMP:20231025T233434Z
8 | UID:sdflbkasuhdb5fkauglkb@google.com
9 | CREATED:20230306T193128Z
10 | LAST-MODIFIED:20231024T222515Z
11 | SEQUENCE:0
12 | STATUS:CONFIRMED
13 | SUMMARY:My Event
14 | TRANSP:OPAQUE
15 | END:VEVENT
--------------------------------------------------------------------------------
/tests/mocks/exdate_la_before_midnight.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VEVENT
2 | DTSTART;TZID=America/Los_Angeles:20231025T150000
3 | DTEND;TZID=America/Los_Angeles:20231025T160000
4 | RRULE:FREQ=WEEKLY;BYDAY=WE
5 | EXDATE;TZID=America/Los_Angeles:20231101T150000
6 | EXDATE;TZID=America/Los_Angeles:20231108T150000
7 | DTSTAMP:20231025T233434Z
8 | UID:sdflbkasuhdb5fkauglkb@google.com
9 | CREATED:20230306T193128Z
10 | LAST-MODIFIED:20231024T222515Z
11 | SEQUENCE:0
12 | STATUS:CONFIRMED
13 | SUMMARY:My Event
14 | TRANSP:OPAQUE
15 | END:VEVENT
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_e2e_cron_entry.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "compliments",
7 | position: "middle_center",
8 | config: {
9 | specialDayUnique: true,
10 | compliments: {
11 | anytime: ["just a test"],
12 | "* * * * *": ["anytime cron"]
13 | }
14 | }
15 | }
16 | ]
17 | };
18 |
19 | /*************** DO NOT EDIT THE LINE BELOW ***************/
20 | if (typeof module !== "undefined") { module.exports = config; }
21 |
--------------------------------------------------------------------------------
/tests/configs/modules/newsfeed/incorrect_url.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "newsfeed",
9 | position: "bottom_bar",
10 | config: {
11 | feeds: [
12 | {
13 | title: "Incorrect Url",
14 | url: "this is not a valid url"
15 | }
16 | ]
17 | }
18 | }
19 | ]
20 | };
21 |
22 | /*************** DO NOT EDIT THE LINE BELOW ***************/
23 | if (typeof module !== "undefined") {
24 | module.exports = config;
25 | }
26 |
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_cron_entry.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "compliments",
7 | position: "middle_center",
8 | config: {
9 | specialDayUnique: true,
10 | compliments: {
11 | anytime: ["just a test"],
12 | "00-10 16-19 * * fri": ["just pub time"]
13 | }
14 | }
15 | }
16 | ]
17 | };
18 |
19 | /*************** DO NOT EDIT THE LINE BELOW ***************/
20 | if (typeof module !== "undefined") { module.exports = config; }
21 |
--------------------------------------------------------------------------------
/tests/mocks/chicago_late_in_timezone.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VEVENT
2 | CREATED:20240904T053053Z
3 | DTEND;TZID=America/Chicago:20240910T211500
4 | DTSTAMP:20240925T005517Z
5 | DTSTART;TZID=America/Chicago:20240910T201500
6 | LAST-MODIFIED:20240925T005515Z
7 | LOCATION:Dance Class
8 | RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:2D48CA37-FCE5-4E16-871
9 | 9-1F47160BDBA3
10 | RRULE:FREQ=WEEKLY;UNTIL=20250601T011500Z
11 | SEQUENCE:3
12 | SUMMARY:Wife Barre Class
13 | UID:39669340-7AFD-4685-9BD6-6CE4B715486E
14 | X-APPLE-CREATOR-IDENTITY:com.apple.mobilecal
15 | END:VEVENT
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_anytime.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "compliments",
9 | position: "middle_center",
10 | config: {
11 | compliments: {
12 | morning: [],
13 | afternoon: [],
14 | evening: [],
15 | anytime: ["Anytime here"]
16 | }
17 | }
18 | }
19 | ]
20 | };
21 |
22 | /*************** DO NOT EDIT THE LINE BELOW ***************/
23 | if (typeof module !== "undefined") {
24 | module.exports = config;
25 | }
26 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/default.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | calendars: [
12 | {
13 | maximumNumberOfDays: 10000,
14 | url: "http://localhost:8080/tests/mocks/calendar_test.ics"
15 | }
16 | ]
17 | }
18 | }
19 | ]
20 | };
21 |
22 | /*************** DO NOT EDIT THE LINE BELOW ***************/
23 | if (typeof module !== "undefined") {
24 | module.exports = config;
25 | }
26 |
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_date.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "compliments",
9 | position: "middle_center",
10 | config: {
11 | compliments: {
12 | morning: [],
13 | afternoon: [],
14 | evening: [],
15 | "....-01-01": ["Happy new year!"]
16 | }
17 | }
18 | }
19 | ]
20 | };
21 |
22 | /*************** DO NOT EDIT THE LINE BELOW ***************/
23 | if (typeof module !== "undefined") {
24 | module.exports = config;
25 | }
26 |
--------------------------------------------------------------------------------
/tests/configs/modules/newsfeed/default.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "newsfeed",
9 | position: "bottom_bar",
10 | config: {
11 | feeds: [
12 | {
13 | title: "Rodrigo Ramirez Blog",
14 | url: "http://localhost:8080/tests/mocks/newsfeed_test.xml"
15 | }
16 | ]
17 | }
18 | }
19 | ]
20 | };
21 |
22 | /*************** DO NOT EDIT THE LINE BELOW ***************/
23 | if (typeof module !== "undefined") {
24 | module.exports = config;
25 | }
26 |
--------------------------------------------------------------------------------
/tests/unit/classes/deprecated_spec.js:
--------------------------------------------------------------------------------
1 | const deprecated = require("../../../js/deprecated");
2 |
3 | describe("Deprecated", () => {
4 | it("should be an object", () => {
5 | expect(typeof deprecated).toBe("object");
6 | });
7 |
8 | it("should contain configs array with deprecated options as strings", () => {
9 | expect(Array.isArray(["deprecated.configs"])).toBe(true);
10 | for (let option of deprecated.configs) {
11 | expect(typeof option).toBe("string");
12 | }
13 | expect(deprecated.configs).toEqual(expect.arrayContaining(["kioskmode"]));
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/tests/configs/modules/display.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "helloworld",
7 | position: "top_bar",
8 | header: "test_header",
9 | config: {
10 | text: "Test Display Header"
11 | }
12 | },
13 | {
14 | module: "helloworld",
15 | position: "bottom_bar",
16 | config: {
17 | text: "Test Hide Header"
18 | }
19 | }
20 | ]
21 | };
22 |
23 | /*************** DO NOT EDIT THE LINE BELOW ***************/
24 | if (typeof module !== "undefined") {
25 | module.exports = config;
26 | }
27 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/bad_rrule.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 | logLevel: ["INFO", "LOG", "WARN", "ERROR", "DEBUG"],
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | calendars: [
12 | {
13 | url: "http://localhost:8080/tests/mocks/bad_rrule.ics"
14 | }
15 | ]
16 | }
17 | }
18 | ]
19 | };
20 |
21 | /*************** DO NOT EDIT THE LINE BELOW ***************/
22 | if (typeof module !== "undefined") {
23 | module.exports = config;
24 | }
25 |
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_animateCSS.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "compliments",
7 | position: "lower_third",
8 | animateIn: "flipInX",
9 | animateOut: "flipOutX",
10 | config: {
11 | compliments: {
12 | anytime: ["AnimateCSS Testing..."]
13 | },
14 | updateInterval: 2000,
15 | fadeSpeed: 1000
16 | }
17 | }
18 | ]
19 | };
20 |
21 | /*************** DO NOT EDIT THE LINE BELOW ***************/
22 | if (typeof module !== "undefined") {
23 | module.exports = config;
24 | }
25 |
--------------------------------------------------------------------------------
/tests/configs/modules/weather/hourlyweather_default.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "weather",
9 | position: "bottom_bar",
10 | config: {
11 | type: "hourly",
12 | location: "Berlin",
13 | weatherProvider: "openweathermap",
14 | weatherEndpoint: "/onecall",
15 | mockData: '"#####WEATHERDATA#####"'
16 | }
17 | }
18 | ]
19 | };
20 |
21 | /*************** DO NOT EDIT THE LINE BELOW ***************/
22 | if (typeof module !== "undefined") {
23 | module.exports = config;
24 | }
25 |
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_animateCSS_fallbackToDefault.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "compliments",
7 | position: "lower_third",
8 | animateIn: "foo",
9 | animateOut: "bar",
10 | config: {
11 | compliments: {
12 | anytime: ["AnimateCSS Testing..."]
13 | },
14 | updateInterval: 2000,
15 | fadeSpeed: 1000
16 | }
17 | }
18 | ]
19 | };
20 |
21 | /*************** DO NOT EDIT THE LINE BELOW ***************/
22 | if (typeof module !== "undefined") {
23 | module.exports = config;
24 | }
25 |
--------------------------------------------------------------------------------
/tests/configs/modules/newsfeed/ignore_items.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "newsfeed",
9 | position: "bottom_bar",
10 | config: {
11 | feeds: [
12 | {
13 | title: "Rodrigo Ramirez Blog",
14 | url: "http://localhost:8080/tests/mocks/newsfeed_test.xml"
15 | }
16 | ],
17 | ignoreOldItems: true
18 | }
19 | }
20 | ]
21 | };
22 |
23 | /*************** DO NOT EDIT THE LINE BELOW ***************/
24 | if (typeof module !== "undefined") {
25 | module.exports = config;
26 | }
27 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/recurring.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | calendars: [
12 | {
13 | maximumEntries: 6,
14 | maximumNumberOfDays: 3650,
15 | url: "http://localhost:8080/tests/mocks/calendar_test_recurring.ics"
16 | }
17 | ]
18 | }
19 | }
20 | ]
21 | };
22 |
23 | /*************** DO NOT EDIT THE LINE BELOW ***************/
24 | if (typeof module !== "undefined") {
25 | module.exports = config;
26 | }
27 |
--------------------------------------------------------------------------------
/tests/configs/modules/weather/currentweather_default.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "weather",
9 | position: "bottom_bar",
10 | config: {
11 | location: "Munich",
12 | showHumidity: "feelslike",
13 | weatherProvider: "openweathermap",
14 | weatherEndpoint: "/weather",
15 | mockData: '"#####WEATHERDATA#####"'
16 | }
17 | }
18 | ]
19 | };
20 |
21 | /*************** DO NOT EDIT THE LINE BELOW ***************/
22 | if (typeof module !== "undefined") {
23 | module.exports = config;
24 | }
25 |
--------------------------------------------------------------------------------
/tests/configs/modules/weather/forecastweather_default.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "weather",
9 | position: "bottom_bar",
10 | config: {
11 | type: "forecast",
12 | location: "Munich",
13 | weatherProvider: "openweathermap",
14 | weatherEndpoint: "/forecast/daily",
15 | mockData: '"#####WEATHERDATA#####"'
16 | }
17 | }
18 | ]
19 | };
20 |
21 | /*************** DO NOT EDIT THE LINE BELOW ***************/
22 | if (typeof module !== "undefined") {
23 | module.exports = config;
24 | }
25 |
--------------------------------------------------------------------------------
/tests/unit/global_vars/defaults_modules_spec.js:
--------------------------------------------------------------------------------
1 | const fs = require("node:fs");
2 | const path = require("node:path");
3 |
4 | const root_path = path.join(__dirname, "../../..");
5 |
6 | describe("Default modules set in modules/default/defaultmodules.js", () => {
7 | const expectedDefaultModules = require(`${root_path}/modules/default/defaultmodules`);
8 |
9 | for (const defaultModule of expectedDefaultModules) {
10 | it(`contains a folder for modules/default/${defaultModule}"`, () => {
11 | expect(fs.existsSync(path.join(root_path, "modules/default", defaultModule))).toBe(true);
12 | });
13 | }
14 | });
15 |
--------------------------------------------------------------------------------
/js/module_functions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Schedule the timer for the next update
3 | * @param {object} timer The timer of the module
4 | * @param {bigint} intervalMS interval in milliseconds
5 | * @param {Promise} callback function to call when the timer expires
6 | */
7 | const scheduleTimer = function (timer, intervalMS, callback) {
8 | if (process.env.JEST_WORKER_ID === undefined) {
9 | // only set timer when not running in jest
10 | let tmr = timer;
11 | clearTimeout(tmr);
12 | tmr = setTimeout(function () {
13 | callback();
14 | }, intervalMS);
15 | }
16 | };
17 |
18 | module.exports = { scheduleTimer };
19 |
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_animateCSS_invertedAnimationName.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "compliments",
7 | position: "lower_third",
8 | animateIn: "flipOutX",
9 | animateOut: "flipInX",
10 | config: {
11 | compliments: {
12 | anytime: ["AnimateCSS Testing..."]
13 | },
14 | updateInterval: 2000,
15 | fadeSpeed: 1000
16 | }
17 | }
18 | ]
19 | };
20 |
21 | /*************** DO NOT EDIT THE LINE BELOW ***************/
22 | if (typeof module !== "undefined") {
23 | module.exports = config;
24 | }
25 |
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_file_change.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "compliments",
7 | position: "bottom_bar",
8 | config: {
9 | updateInterval: 3000,
10 | remoteFileRefreshInterval: 1500,
11 | remoteFile: "http://localhost:8080/tests/mocks/compliments_test.json",
12 | remoteFile2: "http://localhost:8080/tests/mocks/compliments_file.json"
13 | }
14 | }
15 | ]
16 | };
17 |
18 | /*************** DO NOT EDIT THE LINE BELOW ***************/
19 | if (typeof module !== "undefined") { module.exports = config; }
20 |
--------------------------------------------------------------------------------
/.github/workflows/dep-review.yaml:
--------------------------------------------------------------------------------
1 | # This workflow scans your pull requests for dependency changes, and will raise an error if any vulnerabilities or invalid licenses are being introduced.
2 | # For more information see: https://github.com/actions/dependency-review-action
3 |
4 | name: "Review Dependencies"
5 |
6 | on: [pull_request]
7 |
8 | permissions:
9 | contents: read
10 |
11 | jobs:
12 | dependency-review:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: "Checkout code"
16 | uses: actions/checkout@v5
17 | - name: "Dependency Review"
18 | uses: actions/dependency-review-action@v4
19 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/fullday_until.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | hideDuplicates: false,
12 | maximumEntries: 100,
13 | calendars: [
14 | {
15 | maximumEntries: 100,
16 | url: "http://localhost:8080/tests/mocks/fullday_until.ics"
17 | }
18 | ]
19 | }
20 | }
21 | ]
22 | };
23 |
24 | /*************** DO NOT EDIT THE LINE BELOW ***************/
25 | if (typeof module !== "undefined") {
26 | module.exports = config;
27 | }
28 |
--------------------------------------------------------------------------------
/tests/configs/modules/weather/currentweather_units.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | units: "imperial",
5 |
6 | modules: [
7 | {
8 | module: "weather",
9 | position: "bottom_bar",
10 | config: {
11 | location: "Munich",
12 | weatherProvider: "openweathermap",
13 | weatherEndpoint: "/weather",
14 | mockData: '"#####WEATHERDATA#####"',
15 | decimalSymbol: ",",
16 | showHumidity: "wind"
17 | }
18 | }
19 | ]
20 | };
21 |
22 | /*************** DO NOT EDIT THE LINE BELOW ***************/
23 | if (typeof module !== "undefined") {
24 | module.exports = config;
25 | }
26 |
--------------------------------------------------------------------------------
/tests/configs/modules/weather/hourlyweather_options.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "weather",
9 | position: "bottom_bar",
10 | config: {
11 | type: "hourly",
12 | location: "Berlin",
13 | weatherProvider: "openweathermap",
14 | weatherEndpoint: "/onecall",
15 | mockData: '"#####WEATHERDATA#####"',
16 | hourlyForecastIncrements: 2
17 | }
18 | }
19 | ]
20 | };
21 |
22 | /*************** DO NOT EDIT THE LINE BELOW ***************/
23 | if (typeof module !== "undefined") {
24 | module.exports = config;
25 | }
26 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/old-basic-auth.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | calendars: [
12 | {
13 | maximumNumberOfDays: 10000,
14 | url: "http://localhost:8080/tests/mocks/calendar_test.ics",
15 | user: "MagicMirror",
16 | pass: "CallMeADog"
17 | }
18 | ]
19 | }
20 | }
21 | ]
22 | };
23 |
24 | /*************** DO NOT EDIT THE LINE BELOW ***************/
25 | if (typeof module !== "undefined") {
26 | module.exports = config;
27 | }
28 |
--------------------------------------------------------------------------------
/tests/configs/modules/weather/forecastweather_absolute.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "weather",
9 | position: "bottom_bar",
10 | config: {
11 | type: "forecast",
12 | location: "Munich",
13 | weatherProvider: "openweathermap",
14 | weatherEndpoint: "/forecast/daily",
15 | mockData: '"#####WEATHERDATA#####"',
16 | absoluteDates: true
17 | }
18 | }
19 | ]
20 | };
21 |
22 | /*************** DO NOT EDIT THE LINE BELOW ***************/
23 | if (typeof module !== "undefined") {
24 | module.exports = config;
25 | }
26 |
--------------------------------------------------------------------------------
/tests/e2e/template_spec.js:
--------------------------------------------------------------------------------
1 | const fs = require("node:fs");
2 | const helpers = require("./helpers/global-setup");
3 |
4 | describe("templated config with port variable", () => {
5 | beforeAll(async () => {
6 | await helpers.startApplication("tests/configs/port_variable.js");
7 | });
8 | afterAll(async () => {
9 | await helpers.stopApplication();
10 | try {
11 | fs.unlinkSync("tests/configs/port_variable.js");
12 | } catch (err) {
13 | // do nothing
14 | }
15 | });
16 |
17 | it("should return 200", async () => {
18 | const res = await fetch("http://localhost:8090");
19 | expect(res.status).toBe(200);
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_specialDayUnique_false.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "compliments",
7 | position: "middle_center",
8 | config: {
9 | specialDayUnique: false,
10 | compliments: {
11 | anytime: [
12 | "Typical message 1",
13 | "Typical message 2",
14 | "Typical message 3"
15 | ],
16 | "....-..-..": ["Special day message"]
17 | }
18 | }
19 | }
20 | ]
21 | };
22 |
23 | /*************** DO NOT EDIT THE LINE BELOW ***************/
24 | if (typeof module !== "undefined") { module.exports = config; }
25 |
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_specialDayUnique_true.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "compliments",
7 | position: "middle_center",
8 | config: {
9 | specialDayUnique: true,
10 | compliments: {
11 | anytime: [
12 | "Typical message 1",
13 | "Typical message 2",
14 | "Typical message 3"
15 | ],
16 | "....-..-..": ["Special day message"]
17 | }
18 | }
19 | }
20 | ]
21 | };
22 |
23 | /*************** DO NOT EDIT THE LINE BELOW ***************/
24 | if (typeof module !== "undefined") { module.exports = config; }
25 |
--------------------------------------------------------------------------------
/tests/configs/modules/newsfeed/prohibited_words.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "newsfeed",
9 | position: "bottom_bar",
10 | config: {
11 | feeds: [
12 | {
13 | title: "Rodrigo Ramirez Blog",
14 | url: "http://localhost:8080/tests/mocks/newsfeed_test.xml"
15 | }
16 | ],
17 | prohibitedWords: ["QPanel"],
18 | showDescription: true
19 | }
20 | }
21 | ]
22 | };
23 |
24 | /*************** DO NOT EDIT THE LINE BELOW ***************/
25 | if (typeof module !== "undefined") {
26 | module.exports = config;
27 | }
28 |
--------------------------------------------------------------------------------
/tests/configs/modules/compliments/compliments_parts_day.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "compliments",
9 | position: "middle_center",
10 | config: {
11 | compliments: {
12 | morning: ["Hi", "Good Morning", "Morning test"],
13 | afternoon: ["Hello", "Good Afternoon", "Afternoon test"],
14 | evening: ["Hello There", "Good Evening", "Evening test"]
15 | }
16 | }
17 | }
18 | ]
19 | };
20 |
21 | /*************** DO NOT EDIT THE LINE BELOW ***************/
22 | if (typeof module !== "undefined") {
23 | module.exports = config;
24 | }
25 |
--------------------------------------------------------------------------------
/tests/electron/helpers/weather-setup.js:
--------------------------------------------------------------------------------
1 | const { injectMockData } = require("../../utils/weather_mocker");
2 | const helpers = require("./global-setup");
3 |
4 | exports.getText = async (element, result) => {
5 | const elem = await helpers.getElement(element);
6 | await expect(elem).not.toBeNull();
7 | const text = await elem.textContent();
8 | await expect(
9 | text
10 | .trim()
11 | .replace(/(\r\n|\n|\r)/gm, "")
12 | .replace(/[ ]+/g, " ")
13 | ).toBe(result);
14 | return true;
15 | };
16 |
17 | exports.startApp = async (configFileName, systemDate) => {
18 | await helpers.startApplication(injectMockData(configFileName), systemDate);
19 | };
20 |
--------------------------------------------------------------------------------
/js/vendor.js:
--------------------------------------------------------------------------------
1 | const vendor = {
2 | "moment.js": "node_modules/moment/min/moment-with-locales.js",
3 | "moment-timezone.js": "node_modules/moment-timezone/builds/moment-timezone-with-data.js",
4 | "weather-icons.css": "node_modules/weathericons/css/weather-icons.css",
5 | "weather-icons-wind.css": "node_modules/weathericons/css/weather-icons-wind.css",
6 | "font-awesome.css": "css/font-awesome.css",
7 | "nunjucks.js": "node_modules/nunjucks/browser/nunjucks.min.js",
8 | "suncalc.js": "node_modules/suncalc/suncalc.js",
9 | "croner.js": "node_modules/croner/dist/croner.umd.js"
10 | };
11 |
12 | if (typeof module !== "undefined") {
13 | module.exports = vendor;
14 | }
15 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/auth-default.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | calendars: [
12 | {
13 | maximumNumberOfDays: 10000,
14 | url: "http://localhost:8080/tests/mocks/calendar_test.ics",
15 | auth: {
16 | user: "MagicMirror",
17 | pass: "CallMeADog"
18 | }
19 | }
20 | ]
21 | }
22 | }
23 | ]
24 | };
25 |
26 | /*************** DO NOT EDIT THE LINE BELOW ***************/
27 | if (typeof module !== "undefined") {
28 | module.exports = config;
29 | }
30 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/changed-port.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | calendars: [
12 | {
13 | maximumNumberOfDays: 10000,
14 | url: "http://localhost:8010/tests/mocks/calendar_test.ics",
15 | auth: {
16 | user: "MagicMirror",
17 | pass: "CallMeADog"
18 | }
19 | }
20 | ]
21 | }
22 | }
23 | ]
24 | };
25 |
26 | /*************** DO NOT EDIT THE LINE BELOW ***************/
27 | if (typeof module !== "undefined") {
28 | module.exports = config;
29 | }
30 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/symboltest.js:
--------------------------------------------------------------------------------
1 |
2 | let config = {
3 | address: "0.0.0.0",
4 | ipWhitelist: [],
5 | timeFormat: 12,
6 |
7 | modules: [
8 | {
9 | module: "calendar",
10 | position: "bottom_bar",
11 | config: {
12 | maximumEntries: 1,
13 | calendars: [
14 | {
15 | fetchInterval: 7 * 24 * 60 * 60 * 1000,
16 | symbol: ["calendar-check", "google"],
17 | url: "https://ics.calendarlabs.com/76/mm3137/US_Holidays.ics"
18 | }
19 | ]
20 | }
21 | }
22 | ]
23 | };
24 |
25 | /*************** DO NOT EDIT THE LINE BELOW ***************/
26 | if (typeof module !== "undefined") {
27 | module.exports = config;
28 | }
29 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/rrule_until.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | hideDuplicates: false,
12 | maximumEntries: 100,
13 | calendars: [
14 | {
15 | maximumEntries: 100,
16 | maximumNumberOfDays: 1, // Just today
17 | url: "http://localhost:8080/tests/mocks/rrule_until.ics"
18 | }
19 | ]
20 | }
21 | }
22 | ]
23 | };
24 |
25 | /*************** DO NOT EDIT THE LINE BELOW ***************/
26 | if (typeof module !== "undefined") {
27 | module.exports = config;
28 | }
29 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/sliceMultiDayEvents.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | hideDuplicates: false,
12 | maximumEntries: 100,
13 | sliceMultiDayEvents: true,
14 | calendars: [
15 | {
16 | maximumEntries: 100,
17 | url: "http://localhost:8080/tests/mocks/sliceMultiDayEvents.ics"
18 | }
19 | ]
20 | }
21 | }
22 | ]
23 | };
24 |
25 | /*************** DO NOT EDIT THE LINE BELOW ***************/
26 | if (typeof module !== "undefined") {
27 | module.exports = config;
28 | }
29 |
--------------------------------------------------------------------------------
/tests/configs/modules/weather/forecastweather_units.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | units: "imperial",
5 |
6 | modules: [
7 | {
8 | module: "weather",
9 | position: "bottom_bar",
10 | config: {
11 | type: "forecast",
12 | location: "Munich",
13 | weatherProvider: "openweathermap",
14 | weatherEndpoint: "/forecast/daily",
15 | mockData: '"#####WEATHERDATA#####"',
16 | decimalSymbol: "_",
17 | showPrecipitationAmount: true
18 | }
19 | }
20 | ]
21 | };
22 |
23 | /*************** DO NOT EDIT THE LINE BELOW ***************/
24 | if (typeof module !== "undefined") {
25 | module.exports = config;
26 | }
27 |
--------------------------------------------------------------------------------
/tests/configs/modules/weather/hourlyweather_showPrecipitation.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "weather",
9 | position: "bottom_bar",
10 | config: {
11 | type: "hourly",
12 | location: "Berlin",
13 | weatherProvider: "openweathermap",
14 | weatherEndpoint: "/onecall",
15 | mockData: '"#####WEATHERDATA#####"',
16 | showPrecipitationAmount: true,
17 | showPrecipitationProbability: true
18 | }
19 | }
20 | ]
21 | };
22 |
23 | /*************** DO NOT EDIT THE LINE BELOW ***************/
24 | if (typeof module !== "undefined") {
25 | module.exports = config;
26 | }
27 |
--------------------------------------------------------------------------------
/tests/configs/customregions.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules:
5 | // Using exotic content. This is why don't accept go to JSON configuration file
6 | (() => {
7 | let positions = ["row3_left", "top3_left1"];
8 | let modules = Array();
9 | for (let idx in positions) {
10 | modules.push({
11 | module: "helloworld",
12 | position: positions[idx],
13 | config: {
14 | text: `Text in ${positions[idx]}`
15 | }
16 | });
17 | }
18 | return modules;
19 | })()
20 | };
21 |
22 | /*************** DO NOT EDIT THE LINE BELOW ***************/
23 | if (typeof module !== "undefined") {
24 | module.exports = config;
25 | }
26 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/basic-auth.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | calendars: [
12 | {
13 | maximumNumberOfDays: 10000,
14 | url: "http://localhost:8080/tests/mocks/calendar_test.ics",
15 | auth: {
16 | user: "MagicMirror",
17 | pass: "CallMeADog",
18 | method: "basic"
19 | }
20 | }
21 | ]
22 | }
23 | }
24 | ]
25 | };
26 |
27 | /*************** DO NOT EDIT THE LINE BELOW ***************/
28 | if (typeof module !== "undefined") {
29 | module.exports = config;
30 | }
31 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/fail-basic-auth.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | calendars: [
12 | {
13 | maximumNumberOfDays: 10000,
14 | url: "http://localhost:8020/tests/mocks/calendar_test.ics",
15 | auth: {
16 | user: "MagicMirror",
17 | pass: "StairwayToHeaven",
18 | method: "basic"
19 | }
20 | }
21 | ]
22 | }
23 | }
24 | ]
25 | };
26 |
27 | /*************** DO NOT EDIT THE LINE BELOW ***************/
28 | if (typeof module !== "undefined") {
29 | module.exports = config;
30 | }
31 |
--------------------------------------------------------------------------------
/tests/configs/modules/weather/forecastweather_options.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "weather",
9 | position: "bottom_bar",
10 | config: {
11 | type: "forecast",
12 | location: "Munich",
13 | weatherProvider: "openweathermap",
14 | weatherEndpoint: "/forecast/daily",
15 | mockData: '"#####WEATHERDATA#####"',
16 | showPrecipitationAmount: true,
17 | colored: true,
18 | tableClass: "myTableClass"
19 | }
20 | }
21 | ]
22 | };
23 |
24 | /*************** DO NOT EDIT THE LINE BELOW ***************/
25 | if (typeof module !== "undefined") {
26 | module.exports = config;
27 | }
28 |
--------------------------------------------------------------------------------
/tests/configs/modules/weather/currentweather_options.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "weather",
7 | position: "bottom_bar",
8 | config: {
9 | location: "Munich",
10 | weatherProvider: "openweathermap",
11 | weatherEndpoint: "/weather",
12 | mockData: '"#####WEATHERDATA#####"',
13 | windUnits: "beaufort",
14 | showWindDirectionAsArrow: true,
15 | showSun: false,
16 | showHumidity: "wind",
17 | roundTemp: true,
18 | degreeLabel: true
19 | }
20 | }
21 | ]
22 | };
23 |
24 | /*************** DO NOT EDIT THE LINE BELOW ***************/
25 | if (typeof module !== "undefined") {
26 | module.exports = config;
27 | }
28 |
--------------------------------------------------------------------------------
/tests/e2e/vendor_spec.js:
--------------------------------------------------------------------------------
1 | const helpers = require("./helpers/global-setup");
2 |
3 | describe("Vendors", () => {
4 | beforeAll(async () => {
5 | await helpers.startApplication("tests/configs/default.js");
6 | });
7 | afterAll(async () => {
8 | await helpers.stopApplication();
9 | });
10 |
11 | describe("Get list vendors", () => {
12 | const vendors = require(`${global.root_path}/js/vendor.js`);
13 |
14 | Object.keys(vendors).forEach((vendor) => {
15 | it(`should return 200 HTTP code for vendor "${vendor}"`, async () => {
16 | const urlVendor = `http://localhost:8080/${vendors[vendor]}`;
17 | const res = await fetch(urlVendor);
18 | expect(res.status).toBe(200);
19 | });
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/tests/configs/modules/weather/currentweather_compliments.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules: [
5 | {
6 | module: "compliments",
7 | position: "top_bar",
8 | config: {
9 | compliments: {
10 | snow: ["snow"]
11 | },
12 | updateInterval: 3000
13 | }
14 | },
15 | {
16 | module: "weather",
17 | position: "bottom_bar",
18 | config: {
19 | location: "Munich",
20 | weatherProvider: "openweathermap",
21 | weatherEndpoint: "/weather",
22 | mockData: '"#####WEATHERDATA#####"'
23 | }
24 | }
25 | ]
26 | };
27 |
28 | /*************** DO NOT EDIT THE LINE BELOW ***************/
29 | if (typeof module !== "undefined") {
30 | module.exports = config;
31 | }
32 |
--------------------------------------------------------------------------------
/tests/mocks/rrule_until.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VEVENT
2 | DTSTART;TZID=America/Los_Angeles:20240229T160000
3 | DTEND;TZID=America/Los_Angeles:20240229T190000
4 | RRULE:FREQ=WEEKLY;WKST=MO;UNTIL=20240307T075959Z;BYDAY=TH
5 | DTSTAMP:20240307T180618Z
6 | CREATED:20231231T000501Z
7 | LAST-MODIFIED:20231231T005623Z
8 | SEQUENCE:2
9 | STATUS:CONFIRMED
10 | SUMMARY:My event
11 | TRANSP:OPAQUE
12 | END:VEVENT
13 | BEGIN:VEVENT
14 | DTSTART;TZID=America/Los_Angeles:20240307T160000
15 | DTEND;TZID=America/Los_Angeles:20240307T190000
16 | RRULE:FREQ=WEEKLY;WKST=MO;UNTIL=20240316T065959Z;BYDAY=TH
17 | DTSTAMP:20240307T180618Z
18 | CREATED:20231231T000501Z
19 | LAST-MODIFIED:20231231T005623Z
20 | SEQUENCE:3
21 | STATUS:CONFIRMED
22 | SUMMARY:My event
23 | TRANSP:OPAQUE
24 | END:VEVENT
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/single-fullday-event.js:
--------------------------------------------------------------------------------
1 | /*
2 | * MagicMirror² Test config for fullday calendar entries over multiple days
3 | *
4 | * By Paranoid93 https://github.com/Paranoid93/
5 | * MIT Licensed.
6 | */
7 | let config = {
8 | address: "0.0.0.0",
9 | ipWhitelist: [],
10 | timeFormat: 12,
11 |
12 | modules: [
13 | {
14 | module: "calendar",
15 | position: "bottom_bar",
16 | config: {
17 | calendars: [
18 | {
19 | maximumNumberOfDays: 2,
20 | url: "http://localhost:8080/tests/mocks/calendar_test_full_day_events.ics"
21 | }
22 | ]
23 | }
24 | }
25 | ]
26 | };
27 |
28 | /*************** DO NOT EDIT THE LINE BELOW ***************/
29 | if (typeof module !== "undefined") {
30 | module.exports = config;
31 | }
32 |
--------------------------------------------------------------------------------
/tests/e2e/helpers/basic-auth.js:
--------------------------------------------------------------------------------
1 | const path = require("node:path");
2 | const auth = require("express-basic-auth");
3 | const express = require("express");
4 |
5 | const app = express();
6 |
7 | const basicAuth = auth({
8 | realm: "MagicMirror² Area restricted.",
9 | users: { MagicMirror: "CallMeADog" }
10 | });
11 |
12 | app.use(basicAuth);
13 |
14 | // Set available directories
15 | const directories = ["/tests/configs", "/tests/mocks"];
16 |
17 | for (let directory of directories) {
18 | app.use(directory, express.static(path.resolve(`${global.root_path}/${directory}`)));
19 | }
20 |
21 | let server;
22 |
23 | exports.listen = (...args) => {
24 | server = app.listen.apply(app, args);
25 | };
26 |
27 | exports.close = async () => {
28 | await server.close();
29 | };
30 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/long-fullday-event.js:
--------------------------------------------------------------------------------
1 | /*
2 | * MagicMirror² Test config for fullday calendar entries over multiple days
3 | *
4 | * By Paranoid93 https://github.com/Paranoid93/
5 | * MIT Licensed.
6 | */
7 | let config = {
8 | address: "0.0.0.0",
9 | ipWhitelist: [],
10 | timeFormat: 12,
11 |
12 | modules: [
13 | {
14 | module: "calendar",
15 | position: "bottom_bar",
16 | config: {
17 | calendars: [
18 | {
19 | maximumNumberOfDays: 2,
20 | url: "http://localhost:8080/tests/mocks/calendar_test_multi_day_starting_today.ics"
21 | }
22 | ]
23 | }
24 | }
25 | ]
26 | };
27 |
28 | /*************** DO NOT EDIT THE LINE BELOW ***************/
29 | if (typeof module !== "undefined") {
30 | module.exports = config;
31 | }
32 |
--------------------------------------------------------------------------------
/modules/default/alert/templates/alert.njk:
--------------------------------------------------------------------------------
1 | {% if imageUrl or imageFA %}
2 | {% set imageHeight = imageHeight if imageHeight else "80px" %}
3 | {% if imageUrl %}
4 |
5 | {% else %}
6 |
11 | {% endif %}
12 |
13 | {% endif %}
14 | {% if title %}
15 | {{ title if titleType == 'text' else title | safe }}
16 | {% endif %}
17 | {% if message %}
18 | {% if title %}
{% endif %}
19 | {{ message if messageType == 'text' else message | safe }}
20 | {% endif %}
21 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/berlin_multi.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 |
5 | timeFormat: 24,
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | fade: false,
12 | urgency: 0,
13 | dateFormat: "Do.MMM, HH:mm",
14 | fullDayEventDateFormat: "Do.MMM",
15 | timeFormat: "absolute",
16 | getRelative: 0,
17 | maximumNumberOfDays: 28,
18 | showEnd: true,
19 | calendars: [
20 | {
21 | maximumEntries: 100,
22 | url: "http://localhost:8080/tests/mocks/RepeatingEvent.Oct21.ics"
23 | }
24 | ]
25 | }
26 | }
27 | ]
28 | };
29 |
30 | /*************** DO NOT EDIT THE LINE BELOW ***************/
31 | if (typeof module !== "undefined") {
32 | module.exports = config;
33 | }
34 |
--------------------------------------------------------------------------------
/tests/e2e/helpers/weather-functions.js:
--------------------------------------------------------------------------------
1 | const { injectMockData, cleanupMockData } = require("../../utils/weather_mocker");
2 | const helpers = require("./global-setup");
3 |
4 | exports.getText = async (element, result) => {
5 | const elem = await helpers.waitForElement(element);
6 | expect(elem).not.toBeNull();
7 | expect(
8 | elem.textContent
9 | .trim()
10 | .replace(/(\r\n|\n|\r)/gm, "")
11 | .replace(/[ ]+/g, " ")
12 | ).toBe(result);
13 | return true;
14 | };
15 |
16 | exports.startApplication = async (configFileName, additionalMockData) => {
17 | await helpers.startApplication(injectMockData(configFileName, additionalMockData));
18 | await helpers.getDocument();
19 | };
20 |
21 | exports.stopApplication = async () => {
22 | await helpers.stopApplication();
23 | cleanupMockData();
24 | };
25 |
--------------------------------------------------------------------------------
/tests/mocks/whole_day_moved_over_dst_change_berlin.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VCALENDAR
2 | BEGIN:VEVENT
3 | DTSTART;VALUE=DATE:20241027
4 | DTEND;VALUE=DATE:20241028
5 | RRULE:FREQ=DAILY;WKST=SU;COUNT=3
6 | DTSTAMP:20241020T152634Z
7 | UID:14nv8jl8d6dvdbl477lod4fftf@google.com
8 | CREATED:20241020T152434Z
9 | LAST-MODIFIED:20241020T152536Z
10 | SEQUENCE:1
11 | STATUS:CONFIRMED
12 | SUMMARY:test whole day moved
13 | TRANSP:TRANSPARENT
14 | END:VEVENT
15 | BEGIN:VEVENT
16 | DTSTART;VALUE=DATE:20241030
17 | DTEND;VALUE=DATE:20241031
18 | DTSTAMP:20241020T152634Z
19 | UID:14nv8jl8d6dvdbl477lod4fftf@google.com
20 | RECURRENCE-ID;VALUE=DATE:20241028
21 | CREATED:20241020T152434Z
22 | LAST-MODIFIED:20241020T152536Z
23 | SEQUENCE:2
24 | STATUS:CONFIRMED
25 | SUMMARY:test whole day moved
26 | TRANSP:TRANSPARENT
27 | END:VEVENT
28 | END:VCALENDAR
29 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/germany_at_end_of_day_repeating.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 |
5 | timeFormat: 12,
6 |
7 | modules: [
8 | {
9 | module: "calendar",
10 | position: "bottom_bar",
11 | config: {
12 | hideDuplicates: false,
13 | maximumEntries: 100,
14 | sliceMultiDayEvents: true,
15 | dateFormat: "MMM Do, HH:mm",
16 | timeFormat: "absolute",
17 | getRelative: 0,
18 | urgency: 0,
19 | calendars: [
20 | {
21 | maximumEntries: 100,
22 | url: "http://localhost:8080/tests/mocks/germany_at_end_of_day_repeating.ics"
23 | }
24 | ]
25 | }
26 | }
27 | ]
28 | };
29 |
30 | /*************** DO NOT EDIT THE LINE BELOW ***************/
31 | if (typeof module !== "undefined") {
32 | module.exports = config;
33 | }
34 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/chicago-looking-at-ny-recurring.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 |
5 | timeFormat: 24,
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | fade: false,
12 | urgency: 0,
13 | dateFormat: "Do.MMM, HH:mm",
14 | fullDayEventDateFormat: "Do.MMM",
15 | timeFormat: "absolute",
16 | getRelative: 0,
17 | maximumNumberOfDays: 28,
18 | showEnd: true,
19 | calendars: [
20 | {
21 | maximumEntries: 100,
22 | url: "http://localhost:8080/tests/mocks/chicago-nyedge.ics"
23 | }
24 | ]
25 | }
26 | }
27 | ]
28 | };
29 |
30 | /*************** DO NOT EDIT THE LINE BELOW ***************/
31 | if (typeof module !== "undefined") {
32 | module.exports = config;
33 | }
34 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/end_of_day_berlin_moved.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 |
5 | timeFormat: 24,
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | fade: false,
12 | urgency: 0,
13 | dateFormat: "Do.MMM, HH:mm",
14 | fullDayEventDateFormat: "Do.MMM",
15 | timeFormat: "absolute",
16 | getRelative: 0,
17 | maximumNumberOfDays: 28,
18 | showEnd: true,
19 | calendars: [
20 | {
21 | maximumEntries: 100,
22 | url: "http://localhost:8080/tests/mocks/end_of_day_berlin_moved.ics"
23 | }
24 | ]
25 | }
26 | }
27 | ]
28 | };
29 |
30 | /*************** DO NOT EDIT THE LINE BELOW ***************/
31 | if (typeof module !== "undefined") {
32 | module.exports = config;
33 | }
34 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/berlin_end_of_day_repeating.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 |
5 | timeFormat: 24,
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | fade: false,
12 | urgency: 0,
13 | dateFormat: "Do.MMM, HH:mm",
14 | fullDayEventDateFormat: "Do.MMM",
15 | timeFormat: "absolute",
16 | getRelative: 0,
17 | maximumNumberOfDays: 28,
18 | showEnd: true,
19 | calendars: [
20 | {
21 | maximumEntries: 100,
22 | url: "http://localhost:8080/tests/mocks/end_of_day_berlin_moved.ics"
23 | }
24 | ]
25 | }
26 | }
27 | ]
28 | };
29 |
30 | /*************** DO NOT EDIT THE LINE BELOW ***************/
31 | if (typeof module !== "undefined") {
32 | module.exports = config;
33 | }
34 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/fullday_event_over_multiple_days_nonrepeating.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 |
5 | timeFormat: 24,
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | fade: false,
12 | urgency: 0,
13 | dateFormat: "Do.MMM, HH:mm",
14 | fullDayEventDateFormat: "Do.MMM",
15 | timeFormat: "absolute",
16 | getRelative: 0,
17 | showEnd: true,
18 | calendars: [
19 | {
20 | maximumEntries: 100,
21 | url: "http://localhost:8080/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics"
22 | }
23 | ]
24 | }
25 | }
26 | ]
27 | };
28 |
29 | /*************** DO NOT EDIT THE LINE BELOW ***************/
30 | if (typeof module !== "undefined") {
31 | module.exports = config;
32 | }
33 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/exdate_and_recurrence_together.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 |
5 | timeFormat: 24,
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | fade: false,
12 | urgency: 0,
13 | dateFormat: "Do.MMM, HH:mm",
14 | fullDayEventDateFormat: "Do.MMM",
15 | timeFormat: "absolute",
16 | getRelative: 0,
17 | maximumNumberOfDays: 28,
18 | showEnd: true,
19 | calendars: [
20 | {
21 | maximumEntries: 100,
22 | url: "http://localhost:8080/tests/mocks/exdate_and_recurrence_together.ics"
23 | }
24 | ]
25 | }
26 | }
27 | ]
28 | };
29 |
30 | /*************** DO NOT EDIT THE LINE BELOW ***************/
31 | if (typeof module !== "undefined") {
32 | module.exports = config;
33 | }
34 |
--------------------------------------------------------------------------------
/tests/mocks/RepeatingEvent.Oct21.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VCALENDAR
2 | BEGIN:VEVENT
3 | DTSTART;TZID=Europe/Berlin:20241028T000000
4 | DTEND;TZID=Europe/Berlin:20241028T010000
5 | RRULE:FREQ=DAILY;COUNT=3
6 | DTSTAMP:20241020T093758Z
7 | UID:053fdshnnibo92lu97rsoeqoti@google.com
8 | CREATED:20241020T093230Z
9 | LAST-MODIFIED:20241020T093230Z
10 | SEQUENCE:0
11 | STATUS:CONFIRMED
12 | SUMMARY:RepeatingEventWeekAfterToday
13 | TRANSP:OPAQUE
14 | END:VEVENT
15 | BEGIN:VEVENT
16 | DTSTART;TZID=Europe/Berlin:20241021T000000
17 | DTEND;TZID=Europe/Berlin:20241021T010000
18 | RRULE:FREQ=DAILY;COUNT=3
19 | DTSTAMP:20241020T093758Z
20 | UID:1a6kk47pp61k4td2h9rlf0lv69@google.com
21 | CREATED:20241020T093255Z
22 | LAST-MODIFIED:20241020T093437Z
23 | SEQUENCE:1
24 | STATUS:CONFIRMED
25 | SUMMARY:RepeatingEventDayAfterToday
26 | TRANSP:OPAQUE
27 | END:VEVENT
28 | END:VCALENDAR
29 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/diff_tz_start_end.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 |
5 | timeFormat: 24,
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | fade: false,
12 | urgency: 0,
13 | dateFormat: "Do.MMM, HH:mm",
14 | dateEndFormat: "Do.MMM, HH:mm",
15 | fullDayEventDateFormat: "Do.MMM",
16 | timeFormat: "absolute",
17 | getRelative: 0,
18 | maximumNumberOfDays: 28,
19 | showEnd: true,
20 | calendars: [
21 | {
22 | maximumEntries: 100,
23 | url: "http://localhost:8080/tests/mocks/diff_tz_start_end.ics"
24 | }
25 | ]
26 | }
27 | }
28 | ]
29 | };
30 |
31 | /*************** DO NOT EDIT THE LINE BELOW ***************/
32 | if (typeof module !== "undefined") {
33 | module.exports = config;
34 | }
35 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/berlin_whole_day_event_moved_over_dst_change.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 |
5 | timeFormat: 24,
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | fade: false,
12 | urgency: 0,
13 | dateFormat: "Do.MMM, HH:mm",
14 | fullDayEventDateFormat: "Do.MMM",
15 | timeFormat: "absolute",
16 | getRelative: 0,
17 | maximumNumberOfDays: 28,
18 | showEnd: true,
19 | calendars: [
20 | {
21 | maximumEntries: 100,
22 | url: "http://localhost:8080/tests/mocks/whole_day_moved_over_dst_change_berlin.ics"
23 | }
24 | ]
25 | }
26 | }
27 | ]
28 | };
29 |
30 | /*************** DO NOT EDIT THE LINE BELOW ***************/
31 | if (typeof module !== "undefined") {
32 | module.exports = config;
33 | }
34 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yaml:
--------------------------------------------------------------------------------
1 | name: "Close stale issues and PRs"
2 |
3 | on:
4 | workflow_dispatch: # needed for manually running this workflow
5 | schedule:
6 | - cron: "30 1 * * 6" # every Saturday at 1:30
7 |
8 | permissions:
9 | issues: write
10 |
11 | jobs:
12 | stale:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/stale@v10
16 | with:
17 | stale-issue-message: "This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions."
18 | days-before-issue-stale: 60
19 | days-before-issue-close: 7
20 | operations-per-run: 100
21 | stale-issue-label: "wontfix"
22 | exempt-issue-labels: "pinned,security,under investigation,pr welcome,ready (coming with next release)"
23 |
--------------------------------------------------------------------------------
/tests/configs/modules/positions.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | modules:
5 | // Using exotic content. This is why don't accept go to JSON configuration file
6 | (() => {
7 | let positions = ["top_bar", "top_left", "top_center", "top_right", "upper_third", "middle_center", "lower_third", "bottom_left", "bottom_center", "bottom_right", "bottom_bar", "fullscreen_above", "fullscreen_below"];
8 | let modules = Array();
9 | for (let idx in positions) {
10 | modules.push({
11 | module: "helloworld",
12 | position: positions[idx],
13 | config: {
14 | text: `Text in ${positions[idx]}`
15 | }
16 | });
17 | }
18 | return modules;
19 | })()
20 | };
21 |
22 | /*************** DO NOT EDIT THE LINE BELOW ***************/
23 | if (typeof module !== "undefined") {
24 | module.exports = config;
25 | }
26 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/chicago_late_in_timezone.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 |
5 | timeFormat: 24,
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | fade: false,
12 | urgency: 0,
13 | dateFormat: "Do.MMM, HH:mm",
14 | fullDayEventDateFormat: "Do.MMM",
15 | timeFormat: "absolute",
16 | getRelative: 0,
17 | maximumNumberOfDays: 20,
18 | calendars: [
19 | {
20 | maximumEntries: 100,
21 | //url: "http://localhost:8080/tests/mocks/chicago_late_in_timezone.ics"
22 | url: "http://localhost:8080/tests/mocks/chicago_late_in_timezone.ics"
23 | }
24 | ]
25 | }
26 | }
27 | ]
28 | };
29 |
30 | /*************** DO NOT EDIT THE LINE BELOW ***************/
31 | if (typeof module !== "undefined") {
32 | module.exports = config;
33 | }
34 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/exdate_la_at_midnight_dst.js:
--------------------------------------------------------------------------------
1 | /*
2 | * MagicMirror² Test calendar exdate
3 | *
4 | * By jkriegshauser
5 | * MIT Licensed.
6 | *
7 | * See issue #3250
8 | * See tests/electron/modules/calendar_spec.js
9 | */
10 | let config = {
11 | address: "0.0.0.0",
12 | ipWhitelist: [],
13 | timeFormat: 12,
14 |
15 | modules: [
16 | {
17 | module: "calendar",
18 | position: "bottom_bar",
19 | config: {
20 | maximumEntries: 100,
21 | calendars: [
22 | {
23 | maximumEntries: 100,
24 | maximumNumberOfDays: 28, // 4 weeks, 2 of which are skipped
25 | url: "http://localhost:8080/tests/mocks/exdate_la_at_midnight_dst.ics"
26 | }
27 | ]
28 | }
29 | }
30 | ]
31 | };
32 |
33 | /*************** DO NOT EDIT THE LINE BELOW ***************/
34 | if (typeof module !== "undefined") {
35 | module.exports = config;
36 | }
37 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/exdate_la_at_midnight_std.js:
--------------------------------------------------------------------------------
1 | /*
2 | * MagicMirror² Test calendar exdate
3 | *
4 | * By jkriegshauser
5 | * MIT Licensed.
6 | *
7 | * See issue #3250
8 | * See tests/electron/modules/calendar_spec.js
9 | */
10 | let config = {
11 | address: "0.0.0.0",
12 | ipWhitelist: [],
13 | timeFormat: 12,
14 |
15 | modules: [
16 | {
17 | module: "calendar",
18 | position: "bottom_bar",
19 | config: {
20 | maximumEntries: 100,
21 | calendars: [
22 | {
23 | maximumEntries: 100,
24 | maximumNumberOfDays: 28, // 4 weeks, 2 of which are skipped
25 | url: "http://localhost:8080/tests/mocks/exdate_la_at_midnight_std.ics"
26 | }
27 | ]
28 | }
29 | }
30 | ]
31 | };
32 |
33 | /*************** DO NOT EDIT THE LINE BELOW ***************/
34 | if (typeof module !== "undefined") {
35 | module.exports = config;
36 | }
37 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/exdate_la_before_midnight.js:
--------------------------------------------------------------------------------
1 | /*
2 | * MagicMirror² Test calendar exdate
3 | *
4 | * By jkriegshauser
5 | * MIT Licensed.
6 | *
7 | * See issue #3250
8 | * See tests/electron/modules/calendar_spec.js
9 | */
10 | let config = {
11 | address: "0.0.0.0",
12 | ipWhitelist: [],
13 | timeFormat: 12,
14 |
15 | modules: [
16 | {
17 | module: "calendar",
18 | position: "bottom_bar",
19 | config: {
20 | maximumEntries: 100,
21 | calendars: [
22 | {
23 | maximumEntries: 100,
24 | maximumNumberOfDays: 28, // 4 weeks, 2 of which are skipped
25 | url: "http://localhost:8080/tests/mocks/exdate_la_before_midnight.ics"
26 | }
27 | ]
28 | }
29 | }
30 | ]
31 | };
32 |
33 | /*************** DO NOT EDIT THE LINE BELOW ***************/
34 | if (typeof module !== "undefined") {
35 | module.exports = config;
36 | }
37 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/exdate_syd_at_midnight_dst.js:
--------------------------------------------------------------------------------
1 | /*
2 | * MagicMirror² Test calendar exdate
3 | *
4 | * By jkriegshauser
5 | * MIT Licensed.
6 | *
7 | * See issue #3250
8 | * See tests/electron/modules/calendar_spec.js
9 | */
10 | let config = {
11 | address: "0.0.0.0",
12 | ipWhitelist: [],
13 | timeFormat: 12,
14 |
15 | modules: [
16 | {
17 | module: "calendar",
18 | position: "bottom_bar",
19 | config: {
20 | maximumEntries: 100,
21 | calendars: [
22 | {
23 | maximumEntries: 100,
24 | maximumNumberOfDays: 28, // 4 weeks, 2 of which are skipped
25 | url: "http://localhost:8080/tests/mocks/exdate_syd_at_midnight_dst.ics"
26 | }
27 | ]
28 | }
29 | }
30 | ]
31 | };
32 |
33 | /*************** DO NOT EDIT THE LINE BELOW ***************/
34 | if (typeof module !== "undefined") {
35 | module.exports = config;
36 | }
37 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/exdate_syd_at_midnight_std.js:
--------------------------------------------------------------------------------
1 | /*
2 | * MagicMirror² Test calendar exdate
3 | *
4 | * By jkriegshauser
5 | * MIT Licensed.
6 | *
7 | * See issue #3250
8 | * See tests/electron/modules/calendar_spec.js
9 | */
10 | let config = {
11 | address: "0.0.0.0",
12 | ipWhitelist: [],
13 | timeFormat: 12,
14 |
15 | modules: [
16 | {
17 | module: "calendar",
18 | position: "bottom_bar",
19 | config: {
20 | maximumEntries: 100,
21 | calendars: [
22 | {
23 | maximumEntries: 100,
24 | maximumNumberOfDays: 28, // 4 weeks, 2 of which are skipped
25 | url: "http://localhost:8080/tests/mocks/exdate_syd_at_midnight_std.ics"
26 | }
27 | ]
28 | }
29 | }
30 | ]
31 | };
32 |
33 | /*************** DO NOT EDIT THE LINE BELOW ***************/
34 | if (typeof module !== "undefined") {
35 | module.exports = config;
36 | }
37 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/exdate_syd_before_midnight.js:
--------------------------------------------------------------------------------
1 | /*
2 | * MagicMirror² Test calendar exdate
3 | *
4 | * By jkriegshauser
5 | * MIT Licensed.
6 | *
7 | * See issue #3250
8 | * See tests/electron/modules/calendar_spec.js
9 | */
10 | let config = {
11 | address: "0.0.0.0",
12 | ipWhitelist: [],
13 | timeFormat: 12,
14 |
15 | modules: [
16 | {
17 | module: "calendar",
18 | position: "bottom_bar",
19 | config: {
20 | maximumEntries: 100,
21 | calendars: [
22 | {
23 | maximumEntries: 100,
24 | maximumNumberOfDays: 28, // 4 weeks, 2 of which are skipped
25 | url: "http://localhost:8080/tests/mocks/exdate_syd_before_midnight.ics"
26 | }
27 | ]
28 | }
29 | }
30 | ]
31 | };
32 |
33 | /*************** DO NOT EDIT THE LINE BELOW ***************/
34 | if (typeof module !== "undefined") {
35 | module.exports = config;
36 | }
37 |
--------------------------------------------------------------------------------
/css/custom.css.sample:
--------------------------------------------------------------------------------
1 | /* Custom CSS Sample
2 | *
3 | * Change color and fonts here.
4 | *
5 | * Beware that properties cannot be unitless, so for example write '--gap-body: 0px;' instead of just '--gap-body: 0;'
6 | */
7 |
8 | /* Uncomment and adjust accordingly if you want to import another font from the google-fonts-api: */
9 | /* @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@100;300;400;700&display=swap'); */
10 |
11 | :root {
12 | --color-text: #999;
13 | --color-text-dimmed: #666;
14 | --color-text-bright: #fff;
15 | --color-background: black;
16 |
17 | --font-primary: "Roboto Condensed";
18 | --font-secondary: "Roboto";
19 |
20 | --font-size: 20px;
21 | --font-size-small: 0.75rem;
22 |
23 | --gap-body-top: 60px;
24 | --gap-body-right: 60px;
25 | --gap-body-bottom: 60px;
26 | --gap-body-left: 60px;
27 |
28 | --gap-modules: 30px;
29 | }
30 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_display_end.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 |
5 | timeFormat: 24,
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | fade: false,
12 | urgency: 0,
13 | dateFormat: "Do.MMM, HH:mm",
14 | dateEndFormat: "Do.MMM, HH:mm",
15 | fullDayEventDateFormat: "Do.MMM",
16 | timeFormat: "absolute",
17 | getRelative: 0,
18 | showEnd: true,
19 | calendars: [
20 | {
21 | maximumEntries: 100,
22 | url: "http://localhost:8080/tests/mocks/event_with_time_over_multiple_days_non_repeating.ics"
23 | }
24 | ]
25 | }
26 | }
27 | ]
28 | };
29 |
30 | /*************** DO NOT EDIT THE LINE BELOW ***************/
31 | if (typeof module !== "undefined") {
32 | module.exports = config;
33 | }
34 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/show-duplicates-in-calendar.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | maximumEntries: 30,
12 | hideDuplicates: false,
13 | calendars: [
14 | {
15 | maximumEntries: 15,
16 | maximumNumberOfDays: 10000,
17 | url: "http://localhost:8080/tests/mocks/calendar_test.ics" // contains 11 events
18 | },
19 | {
20 | maximumEntries: 15,
21 | maximumNumberOfDays: 10000,
22 | url: "http://localhost:8080/tests/mocks/calendar_test_clone.ics" // clone of upper calendar
23 | }
24 | ]
25 | }
26 | }
27 | ]
28 | };
29 |
30 | /*************** DO NOT EDIT THE LINE BELOW ***************/
31 | if (typeof module !== "undefined") {
32 | module.exports = config;
33 | }
34 |
--------------------------------------------------------------------------------
/tests/e2e/modules_empty_spec.js:
--------------------------------------------------------------------------------
1 | const helpers = require("./helpers/global-setup");
2 |
3 | describe("Check configuration without modules", () => {
4 | beforeAll(async () => {
5 | await helpers.startApplication("tests/configs/without_modules.js");
6 | await helpers.getDocument();
7 | });
8 | afterAll(async () => {
9 | await helpers.stopApplication();
10 | });
11 |
12 | it("shows the message MagicMirror² title", async () => {
13 | const elem = await helpers.waitForElement("#module_1_helloworld .module-content");
14 | expect(elem).not.toBeNull();
15 | expect(elem.textContent).toContain("MagicMirror²");
16 | });
17 |
18 | it("shows the project URL", async () => {
19 | const elem = await helpers.waitForElement("#module_5_helloworld .module-content");
20 | expect(elem).not.toBeNull();
21 | expect(elem.textContent).toContain("https://magicmirror.builders/");
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/.github/workflows/spellcheck.yaml:
--------------------------------------------------------------------------------
1 | # This workflow will run a spellcheck on the codebase.
2 | # It runs a few days before each release. At 00:00 on day-of-month 27 in March, June, September, and December.
3 |
4 | name: Run Spellcheck
5 |
6 | on:
7 | schedule:
8 | - cron: "0 0 27 3,6,9,12 *"
9 |
10 | permissions:
11 | contents: read
12 |
13 | jobs:
14 | spellcheck:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: Checkout code
18 | uses: actions/checkout@v5
19 | with:
20 | ref: develop
21 | - name: Set up Node.js
22 | uses: actions/setup-node@v5
23 | with:
24 | node-version: lts/*
25 | check-latest: true
26 | cache: "npm"
27 | - name: Install dependencies
28 | run: |
29 | node --run install-mm:dev
30 | - name: Run Spellcheck
31 | run: node --run test:spelling
32 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/custom.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 |
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | customEvents: [{ keyword: "CustomEvent", symbol: "dice", eventClass: "undo" }],
12 | forceUseCurrentTime: true,
13 | calendars: [
14 | {
15 | maximumEntries: 5,
16 | pastDaysCount: 5,
17 | broadcastPastEvents: true,
18 | maximumNumberOfDays: 10000,
19 | symbol: "birthday-cake",
20 | fullDaySymbol: "calendar-day",
21 | recurringSymbol: "undo",
22 | url: "http://localhost:8080/tests/mocks/calendar_test_icons.ics"
23 | }
24 | ]
25 | }
26 | }
27 | ]
28 | };
29 |
30 | /*************** DO NOT EDIT THE LINE BELOW ***************/
31 | if (typeof module !== "undefined") {
32 | module.exports = config;
33 | }
34 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/3_move_first_allday_repeating_event.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 |
5 | timeFormat: 24,
6 | units: "metric",
7 | modules: [
8 | {
9 | module: "calendar",
10 | position: "bottom_bar",
11 | config: {
12 | fade: false,
13 | hideDuplicates: false,
14 | maximumEntries: 100,
15 | urgency: 0,
16 | dateFormat: "Do.MMM, HH:mm",
17 | fullDayEventDateFormat: "Do.MMM",
18 | timeFormat: "absolute",
19 | getRelative: 0,
20 | maximumNumberOfDays: 28,
21 | calendars: [
22 | {
23 | maximumEntries: 100,
24 | url: "http://localhost:8080/tests/mocks/3_move_first_allday_repeating_event.ics"
25 | }
26 | ]
27 | }
28 | }
29 | ]
30 | };
31 |
32 | /*************** DO NOT EDIT THE LINE BELOW ***************/
33 | if (typeof module !== "undefined") {
34 | module.exports = config;
35 | }
36 |
--------------------------------------------------------------------------------
/tests/mocks/calendar_test_full_day_events.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VCALENDAR
2 | VERSION:2.0
3 | PRODID:-//ical.marudot.com//iCal Event Maker
4 | CALSCALE:GREGORIAN
5 | BEGIN:VTIMEZONE
6 | TZID:Europe/Berlin
7 | LAST-MODIFIED:20231222T233358Z
8 | TZURL:https://www.tzurl.org/zoneinfo-outlook/Europe/Berlin
9 | X-LIC-LOCATION:Europe/Berlin
10 | BEGIN:DAYLIGHT
11 | TZNAME:CEST
12 | TZOFFSETFROM:+0100
13 | TZOFFSETTO:+0200
14 | DTSTART:19700329T020000
15 | RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
16 | END:DAYLIGHT
17 | BEGIN:STANDARD
18 | TZNAME:CET
19 | TZOFFSETFROM:+0200
20 | TZOFFSETTO:+0100
21 | DTSTART:19701025T030000
22 | RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
23 | END:STANDARD
24 | END:VTIMEZONE
25 | BEGIN:VEVENT
26 | DTSTAMP:20240306T225415Z
27 | UID:1709765647426-75770@ical.marudot.com
28 | DTSTART;VALUE=DATE:20240306
29 | RRULE:FREQ=DAILY
30 | DTEND;VALUE=DATE:20240307
31 | SUMMARY:daily full days
32 | END:VEVENT
33 | END:VCALENDAR
34 |
--------------------------------------------------------------------------------
/tests/mocks/weather_current.json:
--------------------------------------------------------------------------------
1 | {
2 | "coord": {
3 | "lon": 11.58,
4 | "lat": 48.14
5 | },
6 | "weather": [
7 | {
8 | "id": 615,
9 | "main": "Snow",
10 | "description": "light rain and snow",
11 | "icon": "13d"
12 | },
13 | {
14 | "id": 500,
15 | "main": "Rain",
16 | "description": "light rain",
17 | "icon": "10d"
18 | }
19 | ],
20 | "base": "stations",
21 | "main": {
22 | "temp": 1.49,
23 | "pressure": 1005,
24 | "humidity": 93.7,
25 | "temp_min": 1,
26 | "temp_max": 2
27 | },
28 | "visibility": 7000,
29 | "wind": {
30 | "speed": 11.8,
31 | "deg": 250
32 | },
33 | "clouds": {
34 | "all": 75
35 | },
36 | "dt": 1547387400,
37 | "sys": {
38 | "type": 1,
39 | "id": 1267,
40 | "message": 0.0031,
41 | "country": "DE",
42 | "sunrise": 1547362817,
43 | "sunset": 1547394301
44 | },
45 | "id": 2867714,
46 | "name": "Munich",
47 | "cod": 200
48 | }
49 |
--------------------------------------------------------------------------------
/tests/mocks/calendar_test_recurring.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VCALENDAR
2 | PRODID:-//Google Inc//Google Calendar 70.9054//EN
3 | VERSION:2.0
4 | CALSCALE:GREGORIAN
5 | METHOD:PUBLISH
6 | X-WR-CALNAME:xxx@gmail.com
7 | X-WR-TIMEZONE:Europe/Zurich
8 | BEGIN:VTIMEZONE
9 | TZID:Etc/UTC
10 | X-LIC-LOCATION:Etc/UTC
11 | BEGIN:STANDARD
12 | TZOFFSETFROM:+0000
13 | TZOFFSETTO:+0000
14 | TZNAME:GMT
15 | DTSTART:19700101T000000
16 | END:STANDARD
17 | END:VTIMEZONE
18 | BEGIN:VEVENT
19 | DTSTART;VALUE=DATE:20210325
20 | DTEND;VALUE=DATE:20210326
21 | RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1
22 | DTSTAMP:20210421T154106Z
23 | UID:zzz@google.com
24 | REATED:20200831T200244Z
25 | DESCRIPTION:
26 | LAST-MODIFIED:20200831T200244Z
27 | LOCATION:
28 | SEQUENCE:0
29 | STATUS:CONFIRMED
30 | SUMMARY:Birthday
31 | TRANSP:OPAQUE
32 | BEGIN:VALARM
33 | ACTION:DISPLAY
34 | DESCRIPTION:This is an event reminder
35 | TRIGGER:-P0DT7H0M0S
36 | END:VALARM
37 | END:VEVENT
38 |
--------------------------------------------------------------------------------
/tests/mocks/calendar_test_multi_day_starting_today.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VCALENDAR
2 | VERSION:2.0
3 | PRODID:-//ical.marudot.com//iCal Event Maker
4 | CALSCALE:GREGORIAN
5 | BEGIN:VTIMEZONE
6 | TZID:Europe/Berlin
7 | LAST-MODIFIED:20231222T233358Z
8 | TZURL:https://www.tzurl.org/zoneinfo-outlook/Europe/Berlin
9 | X-LIC-LOCATION:Europe/Berlin
10 | BEGIN:DAYLIGHT
11 | TZNAME:CEST
12 | TZOFFSETFROM:+0100
13 | TZOFFSETTO:+0200
14 | DTSTART:19700329T020000
15 | RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
16 | END:DAYLIGHT
17 | BEGIN:STANDARD
18 | TZNAME:CET
19 | TZOFFSETFROM:+0200
20 | TZOFFSETTO:+0100
21 | DTSTART:19701025T030000
22 | RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
23 | END:STANDARD
24 | END:VTIMEZONE
25 | BEGIN:VEVENT
26 | DTSTAMP:20240306T222634Z
27 | UID:1709763965312-82782@ical.marudot.com
28 | DTSTART;VALUE=DATE:20240301
29 | RRULE:FREQ=DAILY
30 | DTEND;VALUE=DATE:20240303
31 | SUMMARY:2 day events
32 | END:VEVENT
33 | END:VCALENDAR
34 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: 📚 Documentation
4 | url: https://github.com/MagicMirrorOrg/MagicMirror-Documentation/issues
5 | about: This issue tracker is not for documentation issues. Please file documentation issues on the docs repo.
6 | - name: 🤔 Support Question
7 | url: https://forum.magicmirror.builders/
8 | about: Problems installing or configuring your MagicMirror? Please post your question on the MagicMirror² Forum.
9 | - name: 💬 Exchange of ideas
10 | url: https://discord.gg/AmGBBwPph5
11 | about: This issue tracker is not for general discussion. Please use the Discord channel.
12 | - name: 📦 Issues with a 3rd-party module
13 | url: https://kristjanesperanto.github.io/MagicMirror-3rd-Party-Modules/
14 | about: This issue tracker is not for 3rd-party module issues. Please file 3rd-party module issues on the module's repo.
15 |
--------------------------------------------------------------------------------
/tests/mocks/translation_test.json:
--------------------------------------------------------------------------------
1 | {
2 | "LOADING": "Loading …",
3 |
4 | "TODAY": "Today",
5 | "TOMORROW": "Tomorrow",
6 | "DAYAFTERTOMORROW": "In 2 days",
7 | "RUNNING": "Ends in",
8 | "EMPTY": "No upcoming events.",
9 |
10 | "WEEK": "Week {weekNumber}",
11 |
12 | "N": "N",
13 | "NNE": "NNE",
14 | "NE": "NE",
15 | "ENE": "ENE",
16 | "E": "E",
17 | "ESE": "ESE",
18 | "SE": "SE",
19 | "SSE": "SSE",
20 | "S": "S",
21 | "SSW": "SSW",
22 | "SW": "SW",
23 | "WSW": "WSW",
24 | "W": "W",
25 | "WNW": "WNW",
26 | "NW": "NW",
27 | "NNW": "NNW",
28 |
29 | "UPDATE_NOTIFICATION": "MagicMirror² update available.",
30 | "UPDATE_NOTIFICATION_MODULE": "Update available for MODULE_NAME module.",
31 | "UPDATE_INFO_SINGLE": "The current installation is COMMIT_COUNT commit behind on the BRANCH_NAME branch.",
32 | "UPDATE_INFO_MULTIPLE": "The current installation is COMMIT_COUNT commits behind on the BRANCH_NAME branch."
33 | }
34 |
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_no_display_end.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 |
5 | timeFormat: 24,
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 | config: {
11 | fade: false,
12 | urgency: 0,
13 | dateFormat: "Do.MMM, HH:mm",
14 | dateEndFormat: "Do.MMM, HH:mm",
15 | fullDayEventDateFormat: "Do.MMM",
16 | timeFormat: "absolute",
17 | getRelative: 0,
18 | showEnd: true,
19 | showEndsOnlyWithDuration: true,
20 | calendars: [
21 | {
22 | maximumEntries: 100,
23 | url: "http://localhost:8080/tests/mocks/event_with_time_over_multiple_days_non_repeating.ics"
24 | }
25 | ]
26 | }
27 | }
28 | ]
29 | };
30 |
31 | /*************** DO NOT EDIT THE LINE BELOW ***************/
32 | if (typeof module !== "undefined") {
33 | module.exports = config;
34 | }
35 |
--------------------------------------------------------------------------------
/tests/e2e/modules_position_spec.js:
--------------------------------------------------------------------------------
1 | const helpers = require("./helpers/global-setup");
2 |
3 | describe("Position of modules", () => {
4 | beforeAll(async () => {
5 | await helpers.startApplication("tests/configs/modules/positions.js");
6 | await helpers.getDocument();
7 | });
8 | afterAll(async () => {
9 | await helpers.stopApplication();
10 | });
11 |
12 | const positions = ["top_bar", "top_left", "top_center", "top_right", "upper_third", "middle_center", "lower_third", "bottom_left", "bottom_center", "bottom_right", "bottom_bar", "fullscreen_above", "fullscreen_below"];
13 |
14 | for (const position of positions) {
15 | const className = position.replace("_", ".");
16 | it(`should show text in ${position}`, async () => {
17 | const elem = await helpers.waitForElement(`.${className}`);
18 | expect(elem).not.toBeNull();
19 | expect(elem.textContent).toContain(`Text in ${position}`);
20 | });
21 | }
22 | });
23 |
--------------------------------------------------------------------------------
/tests/mocks/fullday_until.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VCALENDAR
2 | BEGIN:VEVENT
3 | DESCRIPTION:\n
4 | RRULE:FREQ=YEARLY;UNTIL=20250504T230000Z;INTERVAL=1;BYMONTHDAY=5;BYMONTH=5
5 | UID:040000008200E00074C5B7101A82E00800000000DAEF6ED30D9FDA01000000000000000
6 | 010000000D37F812F0777844A93E97B96AD2D278B
7 | SUMMARY:Person A's Birthday
8 | DTSTART;VALUE=DATE:20250505
9 | DTEND;VALUE=DATE:20250506
10 | CLASS:PUBLIC
11 | PRIORITY:5
12 | DTSTAMP:20250428T133000Z
13 | TRANSP:TRANSPARENT
14 | STATUS:CONFIRMED
15 | SEQUENCE:0
16 | LOCATION:
17 | X-MICROSOFT-CDO-APPT-SEQUENCE:0
18 | X-MICROSOFT-CDO-BUSYSTATUS:FREE
19 | X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
20 | X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
21 | X-MICROSOFT-CDO-IMPORTANCE:1
22 | X-MICROSOFT-CDO-INSTTYPE:1
23 | X-MICROSOFT-DONOTFORWARDMEETING:FALSE
24 | X-MICROSOFT-DISALLOW-COUNTER:FALSE
25 | X-MICROSOFT-REQUESTEDATTENDANCEMODE:DEFAULT
26 | X-MICROSOFT-ISRESPONSEREQUESTED:FALSE
27 | END:VEVENT
28 | END:VCALENDAR
29 |
--------------------------------------------------------------------------------
/tests/mocks/bad_rrule.ics:
--------------------------------------------------------------------------------
1 | BEGIN:VCALENDAR
2 | BEGIN:VEVENT
3 | DTSTAMP:20210413T203456Z
4 | UID:E689AEB8C02C4E2CADD8C7D3D303CEAD0
5 | DTSTART;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210415T190000
6 | DTEND;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210415T210000
7 | CLASS:PUBLIC
8 | LOCATION:albert heijn
9 | SUMMARY:xxx xxxx
10 | SEQUENCE:10
11 | RRULE:FREQ=DAILY;UNTIL=20210418T170000Z
12 | EXDATE;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210417T190000
13 | EXDATE;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210416T190000
14 | EXDATE;TZID="Amsterdam, Belgrade, Berlin, Brussels, Budapest, Madrid, Paris, Prague, Stockholm":20210415T190000
15 | BEGIN:VALARM
16 | ACTION:DISPLAY
17 | TRIGGER;RELATED=START:-PT15M
18 | END:VALARM
19 | END:VEVENT
20 | END:VCALENDAR
--------------------------------------------------------------------------------
/tests/configs/modules/calendar/countCalendarEvents.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | address: "0.0.0.0",
3 | ipWhitelist: [],
4 | timeFormat: 12,
5 | foreignModulesDir: "tests/mocks",
6 | modules: [
7 | {
8 | module: "calendar",
9 | position: "bottom_bar",
10 |
11 | config: {
12 | maximumEntries: 1,
13 | calendars: [
14 | {
15 | fetchInterval: 10000, //7 * 24 * 60 * 60 * 1000,
16 | symbol: ["calendar-check", "google"],
17 | url: "http://localhost:8080/tests/mocks/12_events.ics"
18 | }
19 | ]
20 | }
21 | },
22 | {
23 | module: "testNotification",
24 | position: "bottom_bar",
25 | config: {
26 | debug: true,
27 | match: {
28 | matchtype: "count",
29 | notificationID: "CALENDAR_EVENTS"
30 | }
31 | }
32 | }
33 | ]
34 | };
35 |
36 | /*************** DO NOT EDIT THE LINE BELOW ***************/
37 | if (typeof module !== "undefined") {
38 | module.exports = config;
39 | }
40 |
--------------------------------------------------------------------------------
/tests/e2e/env_spec.js:
--------------------------------------------------------------------------------
1 | const helpers = require("./helpers/global-setup");
2 |
3 | describe("App environment", () => {
4 | beforeAll(async () => {
5 | await helpers.startApplication("tests/configs/default.js");
6 | await helpers.getDocument();
7 | });
8 | afterAll(async () => {
9 | await helpers.stopApplication();
10 | });
11 |
12 | it("get request from http://localhost:8080 should return 200", async () => {
13 | const res = await fetch("http://localhost:8080");
14 | expect(res.status).toBe(200);
15 | });
16 |
17 | it("get request from http://localhost:8080/nothing should return 404", async () => {
18 | const res = await fetch("http://localhost:8080/nothing");
19 | expect(res.status).toBe(404);
20 | });
21 |
22 | it("should show the title MagicMirror²", async () => {
23 | const elem = await helpers.waitForElement("title");
24 | expect(elem).not.toBeNull();
25 | expect(elem.textContent).toBe("MagicMirror²");
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/modules/default/weather/weather.css:
--------------------------------------------------------------------------------
1 | .weather .weathericon,
2 | .weather .fa-home {
3 | font-size: 75%;
4 | }
5 |
6 | .weather .humidity-icon {
7 | padding-right: 4px;
8 | }
9 |
10 | .weather .humidity-padding {
11 | padding-bottom: 6px;
12 | }
13 |
14 | .weather .day {
15 | padding-left: 0;
16 | padding-right: 25px;
17 | }
18 |
19 | .weather .weather-icon {
20 | padding-right: 30px;
21 | text-align: center;
22 | }
23 |
24 | .weather .min-temp {
25 | padding-left: 20px;
26 | padding-right: 0;
27 | }
28 |
29 | .weather .precipitation-amount,
30 | .weather .precipitation-prob,
31 | .weather .humidity-hourly,
32 | .weather .uv-index {
33 | padding-left: 20px;
34 | padding-right: 0;
35 | }
36 |
37 | .weather tr.colored .min-temp {
38 | color: #bcddff;
39 | }
40 |
41 | .weather tr.colored .max-temp {
42 | color: #ff8e99;
43 | }
44 |
45 | .weather .type-temp {
46 | display: flex;
47 | align-items: baseline;
48 | gap: 10px;
49 | }
50 |
--------------------------------------------------------------------------------
/tests/e2e/ipWhitelist_spec.js:
--------------------------------------------------------------------------------
1 | const helpers = require("./helpers/global-setup");
2 |
3 | describe("ipWhitelist directive configuration", () => {
4 | describe("Set ipWhitelist without access", () => {
5 | beforeAll(async () => {
6 | await helpers.startApplication("tests/configs/noIpWhiteList.js");
7 | });
8 | afterAll(async () => {
9 | await helpers.stopApplication();
10 | });
11 |
12 | it("should return 403", async () => {
13 | const res = await fetch("http://localhost:8181");
14 | expect(res.status).toBe(403);
15 | });
16 | });
17 |
18 | describe("Set ipWhitelist []", () => {
19 | beforeAll(async () => {
20 | await helpers.startApplication("tests/configs/empty_ipWhiteList.js");
21 | });
22 | afterAll(async () => {
23 | await helpers.stopApplication();
24 | });
25 |
26 | it("should return 200", async () => {
27 | const res = await fetch("http://localhost:8282");
28 | expect(res.status).toBe(200);
29 | });
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/tests/e2e/port_spec.js:
--------------------------------------------------------------------------------
1 | const helpers = require("./helpers/global-setup");
2 |
3 | describe("port directive configuration", () => {
4 | describe("Set port 8090", () => {
5 | beforeAll(async () => {
6 | await helpers.startApplication("tests/configs/port_8090.js");
7 | });
8 | afterAll(async () => {
9 | await helpers.stopApplication();
10 | });
11 |
12 | it("should return 200", async () => {
13 | const res = await fetch("http://localhost:8090");
14 | expect(res.status).toBe(200);
15 | });
16 | });
17 |
18 | describe("Set port 8100 on environment variable MM_PORT", () => {
19 | beforeAll(async () => {
20 | await helpers.startApplication("tests/configs/port_8090.js", (process.env.MM_PORT = 8100));
21 | });
22 | afterAll(async () => {
23 | await helpers.stopApplication();
24 | });
25 |
26 | it("should return 200", async () => {
27 | const res = await fetch("http://localhost:8100");
28 | expect(res.status).toBe(200);
29 | });
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/tests/e2e/modules_display_spec.js:
--------------------------------------------------------------------------------
1 | const helpers = require("./helpers/global-setup");
2 |
3 | describe("Display of modules", () => {
4 | beforeAll(async () => {
5 | await helpers.startApplication("tests/configs/modules/display.js");
6 | await helpers.getDocument();
7 | });
8 | afterAll(async () => {
9 | await helpers.stopApplication();
10 | });
11 |
12 | it("should show the test header", async () => {
13 | const elem = await helpers.waitForElement("#module_0_helloworld .module-header");
14 | expect(elem).not.toBeNull();
15 | // textContent returns lowercase here, the uppercase is realized by CSS, which therefore does not end up in textContent
16 | expect(elem.textContent).toBe("test_header");
17 | });
18 |
19 | it("should show no header if no header text is specified", async () => {
20 | const elem = await helpers.waitForElement("#module_1_helloworld .module-header");
21 | expect(elem).not.toBeNull();
22 | expect(elem.textContent).toBe("undefined");
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/translations/ps.json:
--------------------------------------------------------------------------------
1 | {
2 | "LOADING": "پیلېدل",
3 |
4 | "DAYBEFOREYESTERDAY": "پرون ورځ",
5 | "YESTERDAY": "پرون",
6 | "TODAY": "نن",
7 | "TOMORROW": "سبا",
8 | "DAYAFTERTOMORROW": "بل سبا",
9 | "RUNNING": "روان",
10 | "EMPTY": "تش",
11 | "WEEK": "{weekNumber}. اونۍ",
12 |
13 | "N": "N",
14 | "NNE": "NNO",
15 | "NE": "NO",
16 | "ENE": "ONO",
17 | "E": "O",
18 | "ESE": "OSO",
19 | "SE": "SO",
20 | "SSE": "SSO",
21 | "S": "S",
22 | "SSW": "SSW",
23 | "SW": "SW",
24 | "WSW": "WSW",
25 | "W": "W",
26 | "WNW": "WNW",
27 | "NW": "NW",
28 | "NNW": "NNW",
29 |
30 | "FEELS": "حس کېږی {DEGREE}",
31 |
32 | "MODULE_CONFIG_CHANGED": "د {MODULE_NAME} بڼی تغیر کړی دی. \n هیله دی چی اسناد و ګوری!",
33 |
34 | "UPDATE_NOTIFICATION": "د MagicMirror² نوې نسخه سته ",
35 | "UPDATE_NOTIFICATION_MODULE": "د {MODULE_NAME} نوی نسخه سته",
36 | "UPDATE_INFO_SINGLE": "اوسنی برخه {COMMIT_COUNT} د {BRANCH_NAME} څخه وروسته پاته ده",
37 | "UPDATE_INFO_MULTIPLE": "اوسنی برخه {COMMIT_COUNT} د {BRANCH_NAME} څخه وروسته پاته ده"
38 | }
39 |
--------------------------------------------------------------------------------
/tests/unit/functions/cmp_versions_spec.js:
--------------------------------------------------------------------------------
1 | const path = require("node:path");
2 | const { JSDOM } = require("jsdom");
3 |
4 | describe("Test function cmpVersions in js/module.js", () => {
5 | let cmp;
6 |
7 | beforeAll(() => {
8 | return new Promise((done) => {
9 | const dom = new JSDOM(
10 | `\
11 |