mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-06-27 11:50:00 +00:00
commit
500147e130
@ -17,6 +17,7 @@
|
||||
},
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"ecmaVersion": 2017,
|
||||
"ecmaFeatures": {
|
||||
"globalReturn": true
|
||||
}
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -11,7 +11,9 @@ coverage
|
||||
.grunt
|
||||
.lock-wscript
|
||||
build/Release
|
||||
node_modules
|
||||
/node_modules/**/*
|
||||
fonts/node_modules/**/*
|
||||
vendor/node_modules/**/*
|
||||
jspm_modules
|
||||
.npm
|
||||
.node_repl_history
|
||||
@ -81,3 +83,5 @@ Temporary Items
|
||||
*.orig
|
||||
*.rej
|
||||
*.bak
|
||||
|
||||
!/tests/node_modules/**/*
|
@ -1,3 +1,4 @@
|
||||
dist: trusty
|
||||
language: node_js
|
||||
node_js:
|
||||
- "8"
|
||||
|
31
CHANGELOG.md
31
CHANGELOG.md
@ -7,6 +7,29 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror² core.
|
||||
|
||||
## [2.9.0] - 2019-10-01
|
||||
|
||||
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`. If you are having issues running Electron, make sure your [Raspbian is up to date](https://www.raspberrypi.org/documentation/raspbian/updating.md).
|
||||
|
||||
### Added
|
||||
- Spanish translation for "PRECIP".
|
||||
- Adding a Malay (Malaysian) translation for MagicMirror².
|
||||
- Add test check URLs of vendors 200 and 404 HTTP CODE.
|
||||
- Add tests for new weather module and helper to stub ajax requests.
|
||||
|
||||
### Updated
|
||||
- Updatenotification module: Display update notification for a limited (configurable) time.
|
||||
- Enabled e2e/vendor_spec.js tests.
|
||||
- The css/custom.css will be rename after the next release. We've add into `run-start.sh` a instruction by GIT to ignore with `--skip-worktree` and `rm --cached`. [#1540](https://github.com/MichMich/MagicMirror/issues/1540)
|
||||
- Disable sending of notification CLOCK_SECOND when displaySeconds is false.
|
||||
|
||||
### Fixed
|
||||
- Updatenotification module: Properly handle race conditions, prevent crash.
|
||||
- Send `NEWS_FEED` notification also for the first news messages which are shown.
|
||||
- Fixed issue where weather module would not refresh data after a network or API outage. [#1722](https://github.com/MichMich/MagicMirror/issues/1722)
|
||||
- Fixed weatherforecast module not displaying rain amount on fallback endpoint.
|
||||
- Notifications CLOCK_SECOND & CLOCK_MINUTE being from startup instead of matched against the clock and avoid drifting.
|
||||
|
||||
## [2.8.0] - 2019-07-01
|
||||
|
||||
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`. If you are having issues running Electron, make sure your [Raspbian is up to date](https://www.raspberrypi.org/documentation/raspbian/updating.md).
|
||||
@ -28,18 +51,18 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Automatically try to fix eslint errors by passing `--fix` option to it
|
||||
- Added sunrise and sunset times to weathergov weather provider [#1705](https://github.com/MichMich/MagicMirror/issues/1705)
|
||||
- Added "useLocationAsHeader" to display "location" in `config.js` as header when location name is not returned
|
||||
- Added to `newsfeed.js`: in order to design the news article better with css, three more class-names were introduced: newsfeed-desc, newsfeed-desc, newsfeed-desc
|
||||
- Added to `newsfeed.js`: in order to design the news article better with css, three more class-names were introduced: newsfeed-desc, newsfeed-desc, newsfeed-desc
|
||||
|
||||
### Updated
|
||||
- English translation for "Feels" to "Feels like"
|
||||
- Fixed the example calender url in `config.js.sample`
|
||||
- Update `ical.js` to solve various calendar issues.
|
||||
- Update weather city list url [#1676](https://github.com/MichMich/MagicMirror/issues/1676)
|
||||
- Update weather city list url [#1676](https://github.com/MichMich/MagicMirror/issues/1676)
|
||||
- Only update clock once per minute when seconds aren't shown
|
||||
|
||||
### Fixed
|
||||
- Fixed uncaught exception, race condition on module update
|
||||
- Fixed issue [#1696](https://github.com/MichMich/MagicMirror/issues/1696), some ical files start date to not parse to date type
|
||||
- Fixed uncaught exception, race condition on module update
|
||||
- Fixed issue [#1696](https://github.com/MichMich/MagicMirror/issues/1696), some ical files start date to not parse to date type
|
||||
- Allowance HTML5 autoplay-policy (policy is changed from Chrome 66 updates)
|
||||
- Handle SIGTERM messages
|
||||
- Fixes sliceMultiDayEvents so it respects maximumNumberOfDays
|
||||
|
@ -1,7 +1,7 @@
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © 2016-2017 Michael Teeuw
|
||||
Copyright © 2016-2019 Michael Teeuw
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
@ -130,13 +130,6 @@ if $NPM_INSTALL; then
|
||||
|
||||
echo -e "\e[96mInstalling npm ...\e[90m"
|
||||
|
||||
# Fetch the latest version of npm from the selected branch
|
||||
# The NODE_STABLE_BRANCH variable will need to be manually adjusted when a new branch is released. (e.g. 7.x)
|
||||
# Only tested (stable) versions are recommended as newer versions could break MagicMirror.
|
||||
|
||||
#NODE_STABLE_BRANCH="9.x"
|
||||
#curl -sL https://deb.nodesource.com/setup_$NODE_STABLE_BRANCH | sudo -E bash -
|
||||
#
|
||||
sudo apt-get install -y npm
|
||||
echo -e "\e[92mnpm installation Done!\e[0m"
|
||||
fi
|
||||
|
@ -203,7 +203,7 @@ var MM = (function() {
|
||||
*/
|
||||
var updateModuleContent = function(module, newHeader, newContent) {
|
||||
var moduleWrapper = document.getElementById(module.identifier);
|
||||
if (moduleWrapper === null) return;
|
||||
if (moduleWrapper === null) {return;}
|
||||
var headerWrapper = moduleWrapper.getElementsByClassName("module-header");
|
||||
var contentWrapper = moduleWrapper.getElementsByClassName("module-content");
|
||||
|
||||
|
@ -33,7 +33,7 @@ ical.objectHandlers['RRULE'] = function(val, params, curr, stack, line){
|
||||
var originalEnd = ical.objectHandlers['END'];
|
||||
ical.objectHandlers['END'] = function (val, params, curr, stack) {
|
||||
// Recurrence rules are only valid for VEVENT, VTODO, and VJOURNAL.
|
||||
// More specifically, we need to filter the VCALENDAR type because we might end up with a defined rrule
|
||||
// More specifically, we need to filter the VCALENDAR type because we might end up with a defined rrule
|
||||
// due to the subtypes.
|
||||
if ((val === "VEVENT") || (val === "VTODO") || (val === "VJOURNAL")) {
|
||||
if (curr.rrule) {
|
||||
|
@ -41,26 +41,40 @@ Module.register("clock",{
|
||||
|
||||
// Schedule update interval.
|
||||
var self = this;
|
||||
self.second = 0;
|
||||
self.minute = 0;
|
||||
self.lastDisplayedMinute = null;
|
||||
setInterval(function() {
|
||||
if (self.config.displaySeconds || self.lastDisplayedMinute !== moment().minute()) {
|
||||
self.updateDom();
|
||||
}
|
||||
if (self.second === 59) {
|
||||
self.second = 0;
|
||||
if (self.minute === 59){
|
||||
self.minute = 0;
|
||||
} else {
|
||||
self.minute++;
|
||||
}
|
||||
self.sendNotification("CLOCK_MINUTE", self.minute);
|
||||
self.second = moment().second();
|
||||
self.minute = moment().minute();
|
||||
|
||||
//Calculate how many ms should pass until next update depending on if seconds is displayed or not
|
||||
var delayCalculator = function(reducedSeconds) {
|
||||
if (self.config.displaySeconds) {
|
||||
return 1000 - moment().milliseconds();
|
||||
} else {
|
||||
self.second++;
|
||||
self.sendNotification("CLOCK_SECOND", self.second);
|
||||
return ((60 - reducedSeconds) * 1000) - moment().milliseconds();
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
//A recursive timeout function instead of interval to avoid drifting
|
||||
var notificationTimer = function() {
|
||||
self.updateDom();
|
||||
|
||||
//If seconds is displayed CLOCK_SECOND-notification should be sent (but not when CLOCK_MINUTE-notification is sent)
|
||||
if (self.config.displaySeconds) {
|
||||
self.second = (self.second + 1) % 60;
|
||||
if (self.second !== 0) {
|
||||
self.sendNotification("CLOCK_SECOND", self.second);
|
||||
setTimeout(notificationTimer, delayCalculator(0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//If minute changed or seconds isn't displayed send CLOCK_MINUTE-notification
|
||||
self.minute = (self.minute + 1) % 60;
|
||||
self.sendNotification("CLOCK_MINUTE", self.minute);
|
||||
setTimeout(notificationTimer, delayCalculator(0));
|
||||
};
|
||||
|
||||
//Set the initial timeout with the amount of seconds elapsed as reducedSeconds so it will trigger when the minute changes
|
||||
setTimeout(notificationTimer, delayCalculator(self.second));
|
||||
|
||||
// Set locale.
|
||||
moment.locale(config.language);
|
||||
|
@ -273,7 +273,7 @@ Module.register("currentweather",{
|
||||
if (this.config.useLocationAsHeader && this.config.location !== false) {
|
||||
return this.config.location;
|
||||
}
|
||||
|
||||
|
||||
return this.data.header;
|
||||
},
|
||||
|
||||
|
@ -327,6 +327,11 @@ Module.register("newsfeed",{
|
||||
|
||||
self.updateDom(self.config.animationSpeed);
|
||||
|
||||
// Broadcast NewsFeed if needed
|
||||
if (self.config.broadcastNewsFeeds) {
|
||||
self.sendNotification("NEWS_FEED", {items: self.newsItems});
|
||||
}
|
||||
|
||||
timer = setInterval(function() {
|
||||
self.activeItem++;
|
||||
self.updateDom(self.config.animationSpeed);
|
||||
|
@ -10,11 +10,17 @@ module.exports = NodeHelper.create({
|
||||
config: {},
|
||||
|
||||
updateTimer: null,
|
||||
updateProcessStarted: false,
|
||||
|
||||
start: function () {
|
||||
},
|
||||
|
||||
configureModules: function(modules) {
|
||||
|
||||
// Push MagicMirror itself , biggest chance it'll show up last in UI and isn't overwritten
|
||||
// others will be added in front, asynchronously
|
||||
simpleGits.push({"module": "default", "git": SimpleGit(path.normalize(__dirname + "/../../../"))});
|
||||
|
||||
for (moduleName in modules) {
|
||||
if (defaultModules.indexOf(moduleName) < 0) {
|
||||
// Default modules are included in the main MagicMirror repo
|
||||
@ -22,6 +28,7 @@ module.exports = NodeHelper.create({
|
||||
|
||||
var stat;
|
||||
try {
|
||||
//console.log("checking git for module="+moduleName)
|
||||
stat = fs.statSync(path.join(moduleFolder, ".git"));
|
||||
} catch(err) {
|
||||
// Error when directory .git doesn't exist
|
||||
@ -36,30 +43,29 @@ module.exports = NodeHelper.create({
|
||||
// No valid remote for folder, skip
|
||||
return;
|
||||
}
|
||||
|
||||
// Folder has .git and has at least one git remote, watch this folder
|
||||
simpleGits.push({"module": mn, "git": git});
|
||||
simpleGits.unshift({"module": mn, "git": git});
|
||||
});
|
||||
}(moduleName, moduleFolder);
|
||||
}
|
||||
}
|
||||
|
||||
// Push MagicMirror itself last, biggest chance it'll show up last in UI and isn't overwritten
|
||||
simpleGits.push({"module": "default", "git": SimpleGit(path.normalize(__dirname + "/../../../"))});
|
||||
},
|
||||
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
if (notification === "CONFIG") {
|
||||
this.config = payload;
|
||||
} else if(notification === "MODULES") {
|
||||
this.configureModules(payload);
|
||||
this.preformFetch();
|
||||
// if this is the 1st time thru the update check process
|
||||
if(this.updateProcessStarted==false ){
|
||||
this.updateProcessStarted=true;
|
||||
this.configureModules(payload);
|
||||
this.preformFetch();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
preformFetch() {
|
||||
var self = this;
|
||||
|
||||
simpleGits.forEach(function(sg) {
|
||||
sg.git.fetch().status(function(err, data) {
|
||||
data.module = sg.module;
|
||||
|
@ -2,40 +2,56 @@ Module.register("updatenotification", {
|
||||
|
||||
defaults: {
|
||||
updateInterval: 10 * 60 * 1000, // every 10 minutes
|
||||
refreshInterval: 24 * 60 * 60 * 1000, // one day
|
||||
},
|
||||
|
||||
status: false,
|
||||
suspended: false,
|
||||
moduleList: {},
|
||||
|
||||
start: function () {
|
||||
var self = this;
|
||||
Log.log("Start updatenotification");
|
||||
setInterval( () => { self.moduleList = {};self.updateDom(2); } , self.config.refreshInterval);
|
||||
},
|
||||
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
if (notification === "DOM_OBJECTS_CREATED") {
|
||||
this.sendSocketNotification("CONFIG", this.config);
|
||||
this.sendSocketNotification("MODULES", Module.definitions);
|
||||
this.hide(0, { lockString: self.identifier });
|
||||
//this.hide(0, { lockString: self.identifier });
|
||||
}
|
||||
},
|
||||
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
if (notification === "STATUS") {
|
||||
this.status = payload;
|
||||
this.updateUI();
|
||||
this.updateUI(payload);
|
||||
}
|
||||
},
|
||||
|
||||
updateUI: function () {
|
||||
updateUI: function (payload) {
|
||||
var self = this;
|
||||
if (this.status && this.status.behind > 0) {
|
||||
self.updateDom(0);
|
||||
self.show(1000, { lockString: self.identifier });
|
||||
if (payload && payload.behind > 0) {
|
||||
// if we haven't seen info for this module
|
||||
if(this.moduleList[payload.module] == undefined){
|
||||
// save it
|
||||
this.moduleList[payload.module]=payload;
|
||||
self.updateDom(2);
|
||||
}
|
||||
//self.show(1000, { lockString: self.identifier });
|
||||
|
||||
} else if (payload && payload.behind == 0){
|
||||
// if the module WAS in the list, but shouldn't be
|
||||
if(this.moduleList[payload.module] != undefined){
|
||||
// remove it
|
||||
delete this.moduleList[payload.module];
|
||||
self.updateDom(2);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
diffLink: function(text) {
|
||||
var localRef = this.status.hash;
|
||||
var remoteRef = this.status.tracking.replace(/.*\//, "");
|
||||
diffLink: function(module, text) {
|
||||
var localRef = module.hash;
|
||||
var remoteRef = module.tracking.replace(/.*\//, "");
|
||||
return "<a href=\"https://github.com/MichMich/MagicMirror/compare/"+localRef+"..."+remoteRef+"\" "+
|
||||
"class=\"xsmall dimmed\" "+
|
||||
"style=\"text-decoration: none;\" "+
|
||||
@ -47,41 +63,53 @@ Module.register("updatenotification", {
|
||||
// Override dom generator.
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
if(this.suspended==false){
|
||||
// process the hash of module info found
|
||||
for(key of Object.keys(this.moduleList)){
|
||||
let m= this.moduleList[key];
|
||||
|
||||
if (this.status && this.status.behind > 0) {
|
||||
var message = document.createElement("div");
|
||||
message.className = "small bright";
|
||||
var message = document.createElement("div");
|
||||
message.className = "small bright";
|
||||
|
||||
var icon = document.createElement("i");
|
||||
icon.className = "fa fa-exclamation-circle";
|
||||
icon.innerHTML = " ";
|
||||
message.appendChild(icon);
|
||||
var icon = document.createElement("i");
|
||||
icon.className = "fa fa-exclamation-circle";
|
||||
icon.innerHTML = " ";
|
||||
message.appendChild(icon);
|
||||
|
||||
var updateInfoKeyName = this.status.behind === 1 ? "UPDATE_INFO_SINGLE" : "UPDATE_INFO_MULTIPLE";
|
||||
var subtextHtml = this.translate(updateInfoKeyName, {
|
||||
COMMIT_COUNT: this.status.behind,
|
||||
BRANCH_NAME: this.status.current
|
||||
});
|
||||
var updateInfoKeyName = m.behind == 1 ? "UPDATE_INFO_SINGLE" : "UPDATE_INFO_MULTIPLE";
|
||||
|
||||
var text = document.createElement("span");
|
||||
if (this.status.module === "default") {
|
||||
text.innerHTML = this.translate("UPDATE_NOTIFICATION");
|
||||
subtextHtml = this.diffLink(subtextHtml);
|
||||
} else {
|
||||
text.innerHTML = this.translate("UPDATE_NOTIFICATION_MODULE", {
|
||||
MODULE_NAME: this.status.module
|
||||
var subtextHtml = this.translate(updateInfoKeyName, {
|
||||
COMMIT_COUNT: m.behind,
|
||||
BRANCH_NAME: m.current
|
||||
});
|
||||
|
||||
var text = document.createElement("span");
|
||||
if (m.module == "default") {
|
||||
text.innerHTML = this.translate("UPDATE_NOTIFICATION");
|
||||
subtextHtml = this.diffLink(m,subtextHtml);
|
||||
} else {
|
||||
text.innerHTML = this.translate("UPDATE_NOTIFICATION_MODULE", {
|
||||
MODULE_NAME: m.module
|
||||
});
|
||||
}
|
||||
message.appendChild(text);
|
||||
|
||||
wrapper.appendChild(message);
|
||||
|
||||
var subtext = document.createElement("div");
|
||||
subtext.innerHTML = subtextHtml;
|
||||
subtext.className = "xsmall dimmed";
|
||||
wrapper.appendChild(subtext);
|
||||
}
|
||||
message.appendChild(text);
|
||||
|
||||
wrapper.appendChild(message);
|
||||
|
||||
var subtext = document.createElement("div");
|
||||
subtext.innerHTML = subtextHtml;
|
||||
subtext.className = "xsmall dimmed";
|
||||
wrapper.appendChild(subtext);
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
},
|
||||
|
||||
suspend: function() {
|
||||
this.suspended=true;
|
||||
},
|
||||
resume: function() {
|
||||
this.suspended=false;
|
||||
this.updateDom(2);
|
||||
}
|
||||
});
|
||||
|
@ -32,7 +32,8 @@ WeatherProvider.register("darksky", {
|
||||
this.setCurrentWeather(currentWeather);
|
||||
}).catch(function(request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
});
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
},
|
||||
|
||||
fetchWeatherForecast() {
|
||||
@ -47,7 +48,8 @@ WeatherProvider.register("darksky", {
|
||||
this.setWeatherForecast(forecast);
|
||||
}).catch(function(request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
});
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
},
|
||||
|
||||
// Create a URL from the config and base URL.
|
||||
|
@ -34,6 +34,7 @@ WeatherProvider.register("openweathermap", {
|
||||
.catch(function(request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
},
|
||||
|
||||
// Overwrite the fetchCurrentWeather method.
|
||||
@ -54,6 +55,7 @@ WeatherProvider.register("openweathermap", {
|
||||
.catch(function(request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
},
|
||||
|
||||
/** OpenWeatherMap Specific Methods - These are not part of the default provider methods */
|
||||
|
@ -41,6 +41,7 @@ WeatherProvider.register("ukmetoffice", {
|
||||
.catch(function(request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
},
|
||||
|
||||
// Overwrite the fetchCurrentWeather method.
|
||||
@ -62,6 +63,7 @@ WeatherProvider.register("ukmetoffice", {
|
||||
.catch(function(request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
},
|
||||
|
||||
|
||||
|
@ -35,6 +35,7 @@ WeatherProvider.register("weathergov", {
|
||||
.catch(function(request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
},
|
||||
|
||||
// Overwrite the fetchCurrentWeather method.
|
||||
@ -53,6 +54,7 @@ WeatherProvider.register("weathergov", {
|
||||
.catch(function(request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
},
|
||||
|
||||
/** Weather.gov Specific Methods - These are not part of the default provider methods */
|
||||
|
0
modules/default/weather/weather.js
Executable file → Normal file
0
modules/default/weather/weather.js
Executable file → Normal file
@ -79,16 +79,12 @@ var WeatherProvider = Class.extend({
|
||||
setCurrentWeather: function(currentWeatherObject) {
|
||||
// We should check here if we are passing a WeatherDay
|
||||
this.currentWeatherObject = currentWeatherObject;
|
||||
|
||||
this.updateAvailable();
|
||||
},
|
||||
|
||||
// Set the weatherForecastArray and notify the delegate that new information is available.
|
||||
setWeatherForecast: function(weatherForecastArray) {
|
||||
// We should check here if we are passing a WeatherDay
|
||||
this.weatherForecastArray = weatherForecastArray;
|
||||
|
||||
this.updateAvailable();
|
||||
},
|
||||
|
||||
// Set the fetched location name.
|
||||
|
@ -353,7 +353,7 @@ Module.register("weatherforecast",{
|
||||
icon: this.config.iconTable[forecast.weather[0].icon],
|
||||
maxTemp: this.roundValue(forecast.temp.max),
|
||||
minTemp: this.roundValue(forecast.temp.min),
|
||||
rain: forecast.rain
|
||||
rain: this.processRain(forecast, data.list)
|
||||
};
|
||||
|
||||
this.forecast.push(forecastData);
|
||||
@ -434,5 +434,38 @@ Module.register("weatherforecast",{
|
||||
roundValue: function(temperature) {
|
||||
var decimals = this.config.roundTemp ? 0 : 1;
|
||||
return parseFloat(temperature).toFixed(decimals);
|
||||
},
|
||||
|
||||
/* processRain(forecast, allForecasts)
|
||||
* Calculates the amount of rain for a whole day even if long term forecasts isn't available for the appid.
|
||||
*
|
||||
* When using the the fallback endpoint forecasts are provided in 3h intervals and the rain-property is an object instead of number.
|
||||
* That object has a property "3h" which contains the amount of rain since the previous forecast in the list.
|
||||
* This code finds all forecasts that is for the same day and sums the amount of rain and returns that.
|
||||
*/
|
||||
processRain: function(forecast, allForecasts) {
|
||||
//If the amount of rain actually is a number, return it
|
||||
if (!isNaN(forecast.rain)) {
|
||||
return forecast.rain;
|
||||
}
|
||||
|
||||
//Find all forecasts that is for the same day
|
||||
var checkDateTime = (!!forecast.dt_txt) ? moment(forecast.dt_txt, "YYYY-MM-DD hh:mm:ss") : moment(forecast.dt, "X");
|
||||
var daysForecasts = allForecasts.filter(function(item) {
|
||||
var itemDateTime = (!!item.dt_txt) ? moment(item.dt_txt, "YYYY-MM-DD hh:mm:ss") : moment(item.dt, "X");
|
||||
return itemDateTime.isSame(checkDateTime, "day") && item.rain instanceof Object;
|
||||
});
|
||||
|
||||
//If no rain this day return undefined so it wont be displayed for this day
|
||||
if (daysForecasts.length == 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
//Summarize all the rain from the matching days
|
||||
return daysForecasts.map(function(item) {
|
||||
return Object.values(item.rain)[0];
|
||||
}).reduce(function(a, b) {
|
||||
return a + b;
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
|
4106
package-lock.json
generated
4106
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "magicmirror",
|
||||
"version": "2.8.0",
|
||||
"version": "2.9.0",
|
||||
"description": "The open source modular smart mirror platform.",
|
||||
"main": "js/electron.js",
|
||||
"scripts": {
|
||||
"start": "sh run-start.sh",
|
||||
"install": "cd vendor && npm install",
|
||||
"install-fonts": "cd fonts && npm install",
|
||||
"postinstall": "sh installers/postinstall/postinstall.sh && npm run install-fonts",
|
||||
"postinstall": "sh untrack-css.sh && sh installers/postinstall/postinstall.sh && npm run install-fonts",
|
||||
"test": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests --recursive",
|
||||
"test:unit": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests/unit --recursive",
|
||||
"test:e2e": "NODE_ENV=test ./node_modules/mocha/bin/mocha tests/e2e --recursive",
|
||||
@ -63,6 +63,7 @@
|
||||
"feedme": "latest",
|
||||
"helmet": "^3.9.0",
|
||||
"iconv-lite": "latest",
|
||||
"lodash": "^4.17.11",
|
||||
"moment": "latest",
|
||||
"request": "^2.88.0",
|
||||
"rrule": "^2.6.2",
|
||||
|
@ -1,3 +1,6 @@
|
||||
|
||||
./untrack-css.sh
|
||||
|
||||
if [ -z "$DISPLAY" ]; then #If not set DISPLAY is SSH remote or tty
|
||||
export DISPLAY=:0 # Set by default display
|
||||
fi
|
||||
|
35
tests/configs/modules/weather/currentweather_default.js
Normal file
35
tests/configs/modules/weather/currentweather_default.js
Normal file
@ -0,0 +1,35 @@
|
||||
/* Magic Mirror Test config default weather
|
||||
*
|
||||
* By fewieden https://github.com/fewieden
|
||||
*
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "weather",
|
||||
position: "bottom_bar",
|
||||
config: {
|
||||
location: "Munich",
|
||||
apiKey: "fake key",
|
||||
initialLoadDelay: 3000
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
40
tests/configs/modules/weather/currentweather_options.js
Normal file
40
tests/configs/modules/weather/currentweather_options.js
Normal file
@ -0,0 +1,40 @@
|
||||
/* Magic Mirror Test config default weather
|
||||
*
|
||||
* By fewieden https://github.com/fewieden
|
||||
*
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "weather",
|
||||
position: "bottom_bar",
|
||||
config: {
|
||||
location: "Munich",
|
||||
apiKey: "fake key",
|
||||
initialLoadDelay: 3000,
|
||||
useBeaufort: false,
|
||||
showWindDirectionAsArrow: true,
|
||||
showHumidity: true,
|
||||
roundTemp: true,
|
||||
degreeLabel: true
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
37
tests/configs/modules/weather/currentweather_units.js
Normal file
37
tests/configs/modules/weather/currentweather_units.js
Normal file
@ -0,0 +1,37 @@
|
||||
/* Magic Mirror Test config default weather
|
||||
*
|
||||
* By fewieden https://github.com/fewieden
|
||||
*
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "imperial",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "weather",
|
||||
position: "bottom_bar",
|
||||
config: {
|
||||
location: "Munich",
|
||||
apiKey: "fake key",
|
||||
initialLoadDelay: 3000,
|
||||
decimalSymbol: ",",
|
||||
showHumidity: true
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
37
tests/configs/modules/weather/forecastweather_default.js
Normal file
37
tests/configs/modules/weather/forecastweather_default.js
Normal file
@ -0,0 +1,37 @@
|
||||
/* Magic Mirror Test config default weather
|
||||
*
|
||||
* By fewieden https://github.com/fewieden
|
||||
*
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "weather",
|
||||
position: "bottom_bar",
|
||||
config: {
|
||||
type: "forecast",
|
||||
location: "Munich",
|
||||
apiKey: "fake key",
|
||||
weatherEndpoint: "/forecast/daily",
|
||||
initialLoadDelay: 3000
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
40
tests/configs/modules/weather/forecastweather_options.js
Normal file
40
tests/configs/modules/weather/forecastweather_options.js
Normal file
@ -0,0 +1,40 @@
|
||||
/* Magic Mirror Test config default weather
|
||||
*
|
||||
* By fewieden https://github.com/fewieden
|
||||
*
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "weather",
|
||||
position: "bottom_bar",
|
||||
config: {
|
||||
type: "forecast",
|
||||
location: "Munich",
|
||||
apiKey: "fake key",
|
||||
weatherEndpoint: "/forecast/daily",
|
||||
initialLoadDelay: 3000,
|
||||
showPrecipitationAmount: true,
|
||||
colored: true,
|
||||
tableClass: "myTableClass"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
4
tests/e2e/modules/mocks/index.js
Normal file
4
tests/e2e/modules/mocks/index.js
Normal file
@ -0,0 +1,4 @@
|
||||
const generateWeather = require("./weather_current");
|
||||
const generateWeatherForecast = require("./weather_forecast");
|
||||
|
||||
module.exports = {generateWeather, generateWeatherForecast};
|
54
tests/e2e/modules/mocks/weather_current.js
Normal file
54
tests/e2e/modules/mocks/weather_current.js
Normal file
@ -0,0 +1,54 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
function generateWeather(extendedData = {}) {
|
||||
return JSON.stringify(_.merge({}, {
|
||||
coord:{
|
||||
lon: 11.58,
|
||||
lat: 48.14
|
||||
},
|
||||
weather:[
|
||||
{
|
||||
id: 615,
|
||||
main: "Snow",
|
||||
description: "light rain and snow",
|
||||
icon: "13d"
|
||||
},
|
||||
{
|
||||
id: 500,
|
||||
main: "Rain",
|
||||
description: "light rain",
|
||||
icon: "10d"
|
||||
}
|
||||
],
|
||||
base: "stations",
|
||||
main:{
|
||||
temp: 1.49,
|
||||
pressure: 1005,
|
||||
humidity: 93.7,
|
||||
temp_min: 1,
|
||||
temp_max: 2
|
||||
},
|
||||
visibility: 7000,
|
||||
wind:{
|
||||
speed: 11.8,
|
||||
deg: 250
|
||||
},
|
||||
clouds:{
|
||||
all: 75
|
||||
},
|
||||
dt: 1547387400,
|
||||
sys:{
|
||||
type: 1,
|
||||
id: 1267,
|
||||
message: 0.0031,
|
||||
country: "DE",
|
||||
sunrise: 1547362817,
|
||||
sunset: 1547394301
|
||||
},
|
||||
id: 2867714,
|
||||
name: "Munich",
|
||||
cod: 200
|
||||
}, extendedData));
|
||||
}
|
||||
|
||||
module.exports = generateWeather;
|
97
tests/e2e/modules/mocks/weather_forecast.js
Normal file
97
tests/e2e/modules/mocks/weather_forecast.js
Normal file
@ -0,0 +1,97 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
function generateWeatherForecast(extendedData = {}) {
|
||||
return JSON.stringify(_.merge({}, {
|
||||
"city": {
|
||||
"id": 2867714,
|
||||
"name": "Munich",
|
||||
"coord": {"lon": 11.5754, "lat": 48.1371},
|
||||
"country": "DE",
|
||||
"population": 1260391,
|
||||
"timezone": 7200
|
||||
},
|
||||
"cod": "200",
|
||||
"message": 0.9653487,
|
||||
"cnt": 7,
|
||||
"list": [{
|
||||
"dt": 1568372400,
|
||||
"sunrise": 1568350044,
|
||||
"sunset": 1568395948,
|
||||
"temp": {"day": 24.44, "min": 15.35, "max": 24.44, "night": 15.35, "eve": 18, "morn": 23.03},
|
||||
"pressure": 1031.65,
|
||||
"humidity": 70,
|
||||
"weather": [{"id": 801, "main": "Clouds", "description": "few clouds", "icon": "02d"}],
|
||||
"speed": 3.35,
|
||||
"deg": 314,
|
||||
"clouds": 21
|
||||
}, {
|
||||
"dt": 1568458800,
|
||||
"sunrise": 1568436525,
|
||||
"sunset": 1568482223,
|
||||
"temp": {"day": 20.81, "min": 13.56, "max": 21.02, "night": 13.56, "eve": 16.6, "morn": 15.88},
|
||||
"pressure": 1028.81,
|
||||
"humidity": 72,
|
||||
"weather": [{"id": 500, "main": "Rain", "description": "light rain", "icon": "10d"}],
|
||||
"speed": 2.21,
|
||||
"deg": 81,
|
||||
"clouds": 100
|
||||
}, {
|
||||
"dt": 1568545200,
|
||||
"sunrise": 1568523007,
|
||||
"sunset": 1568568497,
|
||||
"temp": {"day": 22.65, "min": 13.76, "max": 22.88, "night": 15.27, "eve": 17.45, "morn": 13.76},
|
||||
"pressure": 1023.75,
|
||||
"humidity": 64,
|
||||
"weather": [{"id": 800, "main": "Clear", "description": "sky is clear", "icon": "01d"}],
|
||||
"speed": 1.15,
|
||||
"deg": 7,
|
||||
"clouds": 0
|
||||
}, {
|
||||
"dt": 1568631600,
|
||||
"sunrise": 1568609489,
|
||||
"sunset": 1568654771,
|
||||
"temp": {"day": 23.45, "min": 13.95, "max": 23.45, "night": 13.95, "eve": 17.75, "morn": 15.21},
|
||||
"pressure": 1020.41,
|
||||
"humidity": 64,
|
||||
"weather": [{"id": 800, "main": "Clear", "description": "sky is clear", "icon": "01d"}],
|
||||
"speed": 3.07,
|
||||
"deg": 298,
|
||||
"clouds": 7
|
||||
}, {
|
||||
"dt": 1568718000,
|
||||
"sunrise": 1568695970,
|
||||
"sunset": 1568741045,
|
||||
"temp": {"day": 20.55, "min": 10.95, "max": 20.55, "night": 10.95, "eve": 14.82, "morn": 13.24},
|
||||
"pressure": 1019.4,
|
||||
"humidity": 66,
|
||||
"weather": [{"id": 800, "main": "Clear", "description": "sky is clear", "icon": "01d"}],
|
||||
"speed": 2.8,
|
||||
"deg": 333,
|
||||
"clouds": 2
|
||||
}, {
|
||||
"dt": 1568804400,
|
||||
"sunrise": 1568782452,
|
||||
"sunset": 1568827319,
|
||||
"temp": {"day": 18.15, "min": 7.75, "max": 18.15, "night": 7.75, "eve": 12.45, "morn": 9.41},
|
||||
"pressure": 1017.56,
|
||||
"humidity": 52,
|
||||
"weather": [{"id": 800, "main": "Clear", "description": "sky is clear", "icon": "01d"}],
|
||||
"speed": 2.92,
|
||||
"deg": 34,
|
||||
"clouds": 0
|
||||
}, {
|
||||
"dt": 1568890800,
|
||||
"sunrise": 1568868934,
|
||||
"sunset": 1568913593,
|
||||
"temp": {"day": 14.85, "min": 5.56, "max": 15.05, "night": 5.56, "eve": 9.56, "morn": 6.25},
|
||||
"pressure": 1022.7,
|
||||
"humidity": 59,
|
||||
"weather": [{"id": 800, "main": "Clear", "description": "sky is clear", "icon": "01d"}],
|
||||
"speed": 2.89,
|
||||
"deg": 51,
|
||||
"clouds": 1
|
||||
}]
|
||||
}, extendedData));
|
||||
}
|
||||
|
||||
module.exports = generateWeatherForecast;
|
270
tests/e2e/modules/weather_spec.js
Normal file
270
tests/e2e/modules/weather_spec.js
Normal file
@ -0,0 +1,270 @@
|
||||
const expect = require("chai").expect;
|
||||
const fs = require("fs");
|
||||
const moment = require("moment");
|
||||
const path = require("path");
|
||||
const wdajaxstub = require("webdriverajaxstub");
|
||||
|
||||
const helpers = require("../global-setup");
|
||||
|
||||
const {generateWeather, generateWeatherForecast} = require("./mocks");
|
||||
|
||||
const wait = () => new Promise(res => setTimeout(res, 3000));
|
||||
|
||||
describe("Weather module", function() {
|
||||
let app;
|
||||
|
||||
helpers.setupTimeout(this);
|
||||
|
||||
async function setup(responses) {
|
||||
app = await helpers.startApplication({
|
||||
args: ["js/electron.js"]
|
||||
});
|
||||
|
||||
wdajaxstub.init(app.client, responses);
|
||||
|
||||
app.client.setupStub();
|
||||
}
|
||||
|
||||
afterEach(function() {
|
||||
return helpers.stopApplication(app);
|
||||
});
|
||||
|
||||
describe("Current weather", function() {
|
||||
let template;
|
||||
|
||||
before(function() {
|
||||
template = fs.readFileSync(path.join(__dirname, "..", "..", "..", "modules", "default", "weather", "current.njk"), "utf8");
|
||||
});
|
||||
|
||||
describe("Default configuration", function() {
|
||||
before(function() {
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/weather/currentweather_default.js";
|
||||
});
|
||||
|
||||
it("should render wind speed and wind direction", async function() {
|
||||
const weather = generateWeather();
|
||||
await setup([weather, template]);
|
||||
|
||||
return app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "6 WSW", 10000);
|
||||
});
|
||||
|
||||
it("should render sunrise", async function() {
|
||||
const sunrise = moment().startOf("day").unix();
|
||||
const sunset = moment().startOf("day").unix();
|
||||
|
||||
const weather = generateWeather({sys: {sunrise, sunset}});
|
||||
await setup([weather, template]);
|
||||
|
||||
await app.client.waitForExist(".weather .normal.medium span.wi.dimmed.wi-sunrise", 10000);
|
||||
|
||||
return app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(4)", "12:00 am", 10000);
|
||||
});
|
||||
|
||||
it("should render sunset", async function() {
|
||||
const sunrise = moment().startOf("day").unix();
|
||||
const sunset = moment().endOf("day").unix();
|
||||
|
||||
const weather = generateWeather({sys: {sunrise, sunset}});
|
||||
await setup([weather, template]);
|
||||
|
||||
await app.client.waitForExist(".weather .normal.medium span.wi.dimmed.wi-sunset", 10000);
|
||||
|
||||
return app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(4)", "11:59 pm", 10000);
|
||||
});
|
||||
|
||||
it("should render temperature with icon", async function() {
|
||||
const weather = generateWeather();
|
||||
await setup([weather, template]);
|
||||
|
||||
await app.client.waitForExist(".weather .large.light span.wi.weathericon.wi-snow", 10000);
|
||||
|
||||
return app.client.waitUntilTextExists(".weather .large.light span.bright", "1.5°", 10000);
|
||||
});
|
||||
|
||||
it("should render feels like temperature", async function() {
|
||||
const weather = generateWeather();
|
||||
await setup([weather, template]);
|
||||
|
||||
return app.client.waitUntilTextExists(".weather .normal.medium span.dimmed", "Feels like -5.6°", 10000);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Configuration Options", function() {
|
||||
before(function() {
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/weather/currentweather_options.js";
|
||||
});
|
||||
|
||||
it("should render useBeaufort = false", async function() {
|
||||
const weather = generateWeather();
|
||||
await setup([weather, template]);
|
||||
|
||||
return app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "12", 10000);
|
||||
});
|
||||
|
||||
it("should render showWindDirectionAsArrow = true", async function() {
|
||||
const weather = generateWeather();
|
||||
await setup([weather, template]);
|
||||
|
||||
await app.client.waitForExist(".weather .normal.medium sup i.fa-long-arrow-up", 10000);
|
||||
const element = await app.client.getHTML(".weather .normal.medium sup i.fa-long-arrow-up");
|
||||
|
||||
expect(element).to.include("transform:rotate(250deg);");
|
||||
});
|
||||
|
||||
it("should render showHumidity = true", async function() {
|
||||
const weather = generateWeather();
|
||||
await setup([weather, template]);
|
||||
|
||||
await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(3)", "93", 10000);
|
||||
return app.client.waitForExist(".weather .normal.medium sup i.wi-humidity", 10000);
|
||||
});
|
||||
|
||||
it("should render degreeLabel = true", async function() {
|
||||
const weather = generateWeather();
|
||||
await setup([weather, template]);
|
||||
|
||||
await app.client.waitUntilTextExists(".weather .large.light span.bright", "1°C", 10000);
|
||||
|
||||
return app.client.waitUntilTextExists(".weather .normal.medium span.dimmed", "Feels like -6°C", 10000);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Current weather units", function() {
|
||||
before(function() {
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/weather/currentweather_units.js";
|
||||
});
|
||||
|
||||
it("should render imperial units", async function() {
|
||||
const weather = generateWeather({
|
||||
main:{
|
||||
temp: 1.49 * 9 / 5 + 32,
|
||||
temp_min: 1 * 9 / 5 + 32,
|
||||
temp_max: 2 * 9 / 5 + 32
|
||||
},
|
||||
wind:{
|
||||
speed: 11.8 * 2.23694
|
||||
},
|
||||
});
|
||||
await setup([weather, template]);
|
||||
|
||||
await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(2)", "6 WSW", 10000);
|
||||
await app.client.waitUntilTextExists(".weather .large.light span.bright", "34,7°", 10000);
|
||||
return app.client.waitUntilTextExists(".weather .normal.medium span.dimmed", "22,0°", 10000);
|
||||
});
|
||||
|
||||
it("should render decimalSymbol = ','", async function() {
|
||||
const weather = generateWeather({
|
||||
main:{
|
||||
temp: 1.49 * 9 / 5 + 32,
|
||||
temp_min: 1 * 9 / 5 + 32,
|
||||
temp_max: 2 * 9 / 5 + 32
|
||||
},
|
||||
wind:{
|
||||
speed: 11.8 * 2.23694
|
||||
},
|
||||
});
|
||||
await setup([weather, template]);
|
||||
|
||||
await app.client.waitUntilTextExists(".weather .normal.medium span:nth-child(3)", "93,7", 10000);
|
||||
await app.client.waitUntilTextExists(".weather .large.light span.bright", "34,7°", 10000);
|
||||
return app.client.waitUntilTextExists(".weather .normal.medium span.dimmed", "22,0°", 10000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Weather Forecast", function() {
|
||||
let template;
|
||||
|
||||
before(function() {
|
||||
template = fs.readFileSync(path.join(__dirname, "..", "..", "..", "modules", "default", "weather", "forecast.njk"), "utf8");
|
||||
});
|
||||
|
||||
describe("Default configuration", function() {
|
||||
before(function() {
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/weather/forecastweather_default.js";
|
||||
});
|
||||
|
||||
it("should render days", async function() {
|
||||
const weather = generateWeatherForecast();
|
||||
await setup([weather, template]);
|
||||
|
||||
const days = ["Fri", "Sat", "Sun", "Mon", "Tue"];
|
||||
|
||||
for (const [index, day] of days.entries()) {
|
||||
await app.client.waitUntilTextExists(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(1)`, day, 10000);
|
||||
}
|
||||
});
|
||||
|
||||
it("should render icons", async function() {
|
||||
const weather = generateWeatherForecast();
|
||||
await setup([weather, template]);
|
||||
|
||||
const icons = ["day-cloudy", "rain", "day-sunny", "day-sunny", "day-sunny"];
|
||||
|
||||
for (const [index, icon] of icons.entries()) {
|
||||
await app.client.waitForExist(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(2) span.wi-${icon}`, 10000);
|
||||
}
|
||||
});
|
||||
|
||||
it("should render max temperatures", async function() {
|
||||
const weather = generateWeatherForecast();
|
||||
await setup([weather, template]);
|
||||
|
||||
const temperatures = ["24.4°", "21.0°", "22.9°", "23.4°", "20.6°"];
|
||||
|
||||
for (const [index, temp] of temperatures.entries()) {
|
||||
await app.client.waitUntilTextExists(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(3)`, temp, 10000);
|
||||
}
|
||||
});
|
||||
|
||||
it("should render min temperatures", async function() {
|
||||
const weather = generateWeatherForecast();
|
||||
await setup([weather, template]);
|
||||
|
||||
const temperatures = ["15.3°", "13.6°", "13.8°", "13.9°", "10.9°"];
|
||||
|
||||
for (const [index, temp] of temperatures.entries()) {
|
||||
await app.client.waitUntilTextExists(`.weather table.small tr:nth-child(${index + 1}) td:nth-child(4)`, temp, 10000);
|
||||
}
|
||||
});
|
||||
|
||||
it("should render fading of rows", async function() {
|
||||
const weather = generateWeatherForecast();
|
||||
await setup([weather, template]);
|
||||
|
||||
const opacities = [1, 1, 0.8, 0.5333333333333333, 0.2666666666666667];
|
||||
|
||||
await app.client.waitForExist('.weather table.small', 10000);
|
||||
|
||||
for (const [index, opacity] of opacities.entries()) {
|
||||
const html = await app.client.getHTML(`.weather table.small tr:nth-child(${index + 1})`);
|
||||
expect(html).to.includes(`<tr style="opacity: ${opacity};">`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Configuration Options", function() {
|
||||
before(function() {
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/weather/forecastweather_options.js";
|
||||
});
|
||||
|
||||
it("should render custom table class", async function() {
|
||||
const weather = generateWeatherForecast();
|
||||
await setup([weather, template]);
|
||||
|
||||
await app.client.waitForExist(`.weather table.myTableClass`, 10000);
|
||||
});
|
||||
|
||||
it("should render colored rows", async function() {
|
||||
const weather = generateWeatherForecast();
|
||||
await setup([weather, template]);
|
||||
|
||||
await app.client.waitForExist(`.weather table.myTableClass`, 10000);
|
||||
|
||||
const rows = await app.client.$$('.weather table.myTableClass tr.colored');
|
||||
|
||||
expect(rows.length).to.be.equal(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -6,11 +6,10 @@ const describe = global.describe;
|
||||
const it = global.it;
|
||||
const before = global.before;
|
||||
const after = global.after;
|
||||
const mlog = require("mocha-logger");
|
||||
|
||||
describe("Vendors", function () {
|
||||
|
||||
return; // Test still getting failed in Travis
|
||||
|
||||
helpers.setupTimeout(this);
|
||||
|
||||
var app = null;
|
||||
@ -40,5 +39,17 @@ describe("Vendors", function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Object.keys(vendors).forEach(vendor => {
|
||||
it(`should return 404 HTTP code for vendor https://localhost/"${vendor}"`, function() {
|
||||
urlVendor = "http://localhost:8080/" + vendors[vendor];
|
||||
request.get(urlVendor, function (err, res, body) {
|
||||
if (!err)
|
||||
expect(res.statusCode).to.equal(404);
|
||||
else
|
||||
mlog.pending(`There error vendor 404 test ${err}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
33
tests/node_modules/webdriverajaxstub/index.js
generated
vendored
Normal file
33
tests/node_modules/webdriverajaxstub/index.js
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
function plugin (wdInstance, requests) {
|
||||
if (typeof wdInstance.addCommand !== "function") {
|
||||
throw new Error("You can't use WebdriverAjaxStub with this version of WebdriverIO");
|
||||
}
|
||||
|
||||
function stub(requests, done) {
|
||||
window.XMLHttpRequest = function () {
|
||||
this.open = function (method, url) {
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
};
|
||||
|
||||
this.send = function () {
|
||||
this.status = 200;
|
||||
this.readyState = 4;
|
||||
const response = requests.shift() || [];
|
||||
this.response = response;
|
||||
this.responseText = response;
|
||||
this.onreadystatechange();
|
||||
};
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
wdInstance.addCommand("setupStub", function() {
|
||||
return wdInstance.executeAsync(stub, requests);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.init = plugin;
|
0
translations/en.json
Executable file → Normal file
0
translations/en.json
Executable file → Normal file
@ -31,5 +31,6 @@
|
||||
"UPDATE_INFO_SINGLE": "Tu actual instalación está {COMMIT_COUNT} commit cambios detrás de la rama {BRANCH_NAME}.",
|
||||
"UPDATE_INFO_MULTIPLE": "Tu actual instalación está {COMMIT_COUNT} commits cambios detrás de la rama {BRANCH_NAME}.",
|
||||
|
||||
"FEELS": "Sensación térmica de"
|
||||
"FEELS": "Sensación térmica de",
|
||||
"PRECIP": "Precipitación"
|
||||
}
|
||||
|
33
translations/ms-my.json
Normal file
33
translations/ms-my.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"LOADING": "Tunggu Sebentar …",
|
||||
|
||||
"TODAY": "Hari ini",
|
||||
"TOMORROW": "Esok",
|
||||
"DAYAFTERTOMORROW": "Lusa",
|
||||
"RUNNING": "Berakhir dalam",
|
||||
"EMPTY": "Tidak ada agenda",
|
||||
|
||||
"WEEK": "Minggu ke-{weekNumber}",
|
||||
|
||||
"N": "U",
|
||||
"NNE": "UUT",
|
||||
"NE": "TL",
|
||||
"ENE": "TTL",
|
||||
"E": "T",
|
||||
"ESE": "TT",
|
||||
"SE": "T",
|
||||
"SSE": "ST",
|
||||
"S": "S",
|
||||
"SSW": "SBD",
|
||||
"SW": "BD",
|
||||
"WSW": "BBD",
|
||||
"W": "B",
|
||||
"WNW": "BBL",
|
||||
"NW": "BL",
|
||||
"NNW": "UBL",
|
||||
|
||||
"UPDATE_NOTIFICATION": "MagicMirror² mempunyai update terkini.",
|
||||
"UPDATE_NOTIFICATION_MODULE": "Modul {MODULE_NAME} mempunyai update terkini.",
|
||||
"UPDATE_INFO_SINGLE": "Pemasangan MagicMirror² ini mempunyai {COMMIT_COUNT} commit terkebelakang dari branch {BRANCH_NAME}.",
|
||||
"UPDATE_INFO_MULTIPLE": "Pemasangan MagicMirror² ini mempunyai {COMMIT_COUNT} commit terkebelakang dari branch {BRANCH_NAME}."
|
||||
}
|
@ -40,7 +40,8 @@ var translations = {
|
||||
"cs" : "translations/cs.json", // Czech
|
||||
"hr" : "translations/hr.json", // Croatian
|
||||
"sk" : "translations/sk.json", // Slovak
|
||||
"tlh" : "translations/tlh.json" // Klingon
|
||||
"tlh" : "translations/tlh.json", // Klingon
|
||||
"ms-my" : "translations/ms-my.json" // Malay
|
||||
};
|
||||
|
||||
if (typeof module !== "undefined") {module.exports = translations;}
|
||||
|
11
untrack-css.sh
Executable file
11
untrack-css.sh
Executable file
@ -0,0 +1,11 @@
|
||||
# Long history here
|
||||
# https://github.com/MichMich/MagicMirror/pull/1540
|
||||
STATUS_CUSTOM_CSS=$(git ls-files -v css/custom.css|cut -f 1 --delimiter=" ")
|
||||
|
||||
if [ "$STATUS_CUSTOM_CSS" = "H" ]; then
|
||||
echo "We'll remove from the repository the css/custom.css"
|
||||
echo "This script apply git update-index --skip-worktree css/custom.css"
|
||||
git update-index --skip-worktree css/custom.css
|
||||
git rm --cached css/custom.css
|
||||
fi
|
||||
|
2
vendor/package-lock.json
generated
vendored
2
vendor/package-lock.json
generated
vendored
@ -5,7 +5,7 @@
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": {
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://npm.fontawesome.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.6.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.6.3.tgz",
|
||||
"integrity": "sha512-s5PLdI9NYgjBvfrv6rhirPHlAHWx+Sfo/IjsAeiXYfmemC/GSjwsyz1wLnGPazbLPXWfk62ks980o9AmsxYUEQ=="
|
||||
},
|
||||
"a-sync-waterfall": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user