Merge pull request #2664 from khassel/test-setup

This commit is contained in:
Michael Teeuw 2021-09-28 11:51:19 +02:00 committed by GitHub
commit e10f620cf9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 719 additions and 706 deletions

View File

@ -23,7 +23,6 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- run: |
node -v
Xvfb :99 -screen 0 1024x768x16 &
export DISPLAY=:99
npm install
@ -32,3 +31,4 @@ jobs:
npm run test:css
npm run test:unit
npm run test:e2e
npm run test:electron

View File

@ -27,6 +27,7 @@ _This release is scheduled to be released on 2021-10-01._
- Refactored methods from weatherproviders into weatherobject (isDaytime, updateSunTime).
- Use of `logger.js` in jest tests.
- Run prettier over all relevant files.
- Move test needing electron in new category `electron`, use `server only` mode in `e2e` tests.
### Fixed

View File

@ -48,6 +48,7 @@ process.on("uncaughtException", function (err) {
*/
function App() {
let nodeHelpers = [];
let httpServer;
/**
* Loads the config file. Combines it with the defaults, and runs the
@ -222,7 +223,7 @@ function App() {
}
loadModules(modules, function () {
const server = new Server(config, function (app, io) {
httpServer = new Server(config, function (app, io) {
Log.log("Server started ...");
for (let nodeHelper of nodeHelpers) {
@ -253,6 +254,7 @@ function App() {
nodeHelper.stop();
}
}
httpServer.close();
};
/**

View File

@ -9,12 +9,13 @@
*/
(function (root, factory) {
if (typeof exports === "object") {
// add timestamps in front of log messages
require("console-stamp")(console, {
pattern: "yyyy-mm-dd HH:MM:ss.l",
include: ["debug", "log", "info", "warn", "error"]
});
if (process.env.JEST_WORKER_ID === undefined) {
// add timestamps in front of log messages
require("console-stamp")(console, {
pattern: "yyyy-mm-dd HH:MM:ss.l",
include: ["debug", "log", "info", "warn", "error"]
});
}
// Node, CommonJS-like
module.exports = factory(root.config);
} else {
@ -22,32 +23,48 @@
root.Log = factory(root.config);
}
})(this, function (config) {
let logLevel = {
debug: Function.prototype.bind.call(console.debug, console),
log: Function.prototype.bind.call(console.log, console),
info: Function.prototype.bind.call(console.info, console),
warn: Function.prototype.bind.call(console.warn, console),
error: Function.prototype.bind.call(console.error, console),
group: Function.prototype.bind.call(console.group, console),
groupCollapsed: Function.prototype.bind.call(console.groupCollapsed, console),
groupEnd: Function.prototype.bind.call(console.groupEnd, console),
time: Function.prototype.bind.call(console.time, console),
timeEnd: Function.prototype.bind.call(console.timeEnd, console)
};
let logLevel;
if ((typeof exports === "object" && process.env.JEST_WORKER_ID === undefined) || typeof exports !== "object") {
logLevel.timeStamp = Function.prototype.bind.call(console.timeStamp, console);
}
logLevel = {
debug: Function.prototype.bind.call(console.debug, console),
log: Function.prototype.bind.call(console.log, console),
info: Function.prototype.bind.call(console.info, console),
warn: Function.prototype.bind.call(console.warn, console),
error: Function.prototype.bind.call(console.error, console),
group: Function.prototype.bind.call(console.group, console),
groupCollapsed: Function.prototype.bind.call(console.groupCollapsed, console),
groupEnd: Function.prototype.bind.call(console.groupEnd, console),
time: Function.prototype.bind.call(console.time, console),
timeEnd: Function.prototype.bind.call(console.timeEnd, console),
timeStamp: Function.prototype.bind.call(console.timeStamp, console)
};
logLevel.setLogLevel = function (newLevel) {
if (newLevel) {
Object.keys(logLevel).forEach(function (key, index) {
if (!newLevel.includes(key.toLocaleUpperCase())) {
logLevel[key] = function () {};
}
});
}
};
logLevel.setLogLevel = function (newLevel) {
if (newLevel) {
Object.keys(logLevel).forEach(function (key, index) {
if (!newLevel.includes(key.toLocaleUpperCase())) {
logLevel[key] = function () {};
}
});
}
};
} else {
logLevel = {
debug: function () {},
log: function () {},
info: function () {},
warn: function () {},
error: function () {},
group: function () {},
groupCollapsed: function () {},
groupEnd: function () {},
time: function () {},
timeEnd: function () {},
timeStamp: function () {}
};
logLevel.setLogLevel = function () {};
}
return logLevel;
});

View File

@ -92,6 +92,10 @@ function Server(config, callback) {
if (typeof callback === "function") {
callback(app, io);
}
this.close = function () {
server.close();
};
}
module.exports = Server;

1016
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@
"postinstall": "npm run install-fonts && echo \"MagicMirror installation finished successfully! \n\"",
"test": "NODE_ENV=test jest -i --forceExit",
"test:coverage": "NODE_ENV=test nyc --reporter=lcov --reporter=text jest -i --forceExit",
"test:electron": "NODE_ENV=test jest --selectProjects electron -i --forceExit",
"test:e2e": "NODE_ENV=test jest --selectProjects e2e -i --forceExit",
"test:unit": "NODE_ENV=test jest --selectProjects unit -i --forceExit",
"test:prettier": "prettier . --check",
@ -51,11 +52,11 @@
"eslint-plugin-prettier": "^4.0.0",
"express-basic-auth": "^1.2.0",
"husky": "^7.0.2",
"jest": "^27.1.1",
"jest": "^27.2.0",
"jsdom": "^17.0.0",
"lodash": "^4.17.21",
"nyc": "^15.1.0",
"prettier": "^2.4.0",
"prettier": "^2.4.1",
"pretty-quick": "^3.1.1",
"sinon": "^11.1.2",
"spectron": "^15.0.0",
@ -66,7 +67,7 @@
"suncalc": "^1.8.0"
},
"optionalDependencies": {
"electron": "^13.3.0"
"electron": "^13.4.0"
},
"dependencies": {
"colors": "^1.4.0",
@ -106,13 +107,26 @@
"<rootDir>/tests/unit/mocks"
]
},
{
"displayName": "electron",
"testMatch": [
"**/tests/electron/**/*.[jt]s?(x)"
],
"testPathIgnorePatterns": [
"<rootDir>/tests/electron/modules/mocks",
"<rootDir>/tests/electron/global-setup.js",
"<rootDir>/tests/electron/modules/basic-auth.js"
]
},
{
"displayName": "e2e",
"testMatch": [
"**/tests/e2e/**/*.[jt]s?(x)"
],
"modulePaths": [
"<rootDir>/js/"
],
"testPathIgnorePatterns": [
"<rootDir>/tests/e2e/modules/mocks",
"<rootDir>/tests/e2e/global-setup.js"
]
}

View File

@ -1,42 +1,13 @@
const helpers = require("./global-setup");
const fetch = require("node-fetch");
const helpers = require("./global-setup");
let app = null;
describe("Electron app environment", function () {
helpers.setupTimeout(this);
let app = null;
beforeAll(function () {
// Set config sample for use in test
process.env.MM_CONFIG_FILE = "tests/configs/env.js";
app = helpers.startApplication("tests/configs/env.js");
});
beforeEach(function () {
return helpers
.startApplication({
args: ["js/electron.js"]
})
.then(function (startedApp) {
app = startedApp;
});
});
afterEach(function () {
return helpers.stopApplication(app);
});
it("should open a browserwindow", async function () {
await app.client.waitUntilWindowLoaded();
app.browserWindow.focus();
expect(await app.client.getWindowCount()).toBe(1);
expect(await app.browserWindow.isMinimized()).toBe(false);
expect(await app.browserWindow.isDevToolsOpened()).toBe(false);
expect(await app.browserWindow.isVisible()).toBe(true);
expect(await app.browserWindow.isFocused()).toBe(true);
const bounds = await app.browserWindow.getBounds();
expect(bounds.width).toBeGreaterThan(0);
expect(bounds.height).toBeGreaterThan(0);
expect(await app.browserWindow.getTitle()).toBe("MagicMirror²");
afterAll(function () {
helpers.stopApplication(app);
});
it("get request from http://localhost:8080 should return 200", function (done) {

View File

@ -1,10 +1,8 @@
const helpers = require("./global-setup");
const fetch = require("node-fetch");
const helpers = require("./global-setup");
let app = null;
describe("All font files from roboto.css should be downloadable", function () {
helpers.setupTimeout(this);
let app;
const fontFiles = [];
// Statements below filters out all 'url' lines in the CSS file
const fileContent = require("fs").readFileSync(__dirname + "/../../fonts/roboto.css", "utf8");
@ -18,20 +16,10 @@ describe("All font files from roboto.css should be downloadable", function () {
}
beforeAll(function () {
// Set config sample for use in test
process.env.MM_CONFIG_FILE = "tests/configs/without_modules.js";
return helpers
.startApplication({
args: ["js/electron.js"]
})
.then(function (startedApp) {
app = startedApp;
});
app = helpers.startApplication("tests/configs/without_modules.js");
});
afterAll(function () {
return helpers.stopApplication(app);
helpers.stopApplication(app);
});
test.each(fontFiles)("should return 200 HTTP code for file '%s'", (fontFile, done) => {

View File

@ -1,53 +1,16 @@
/*
* Magic Mirror Global Setup Test Suite
*
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
* MIT Licensed.
*/
const Application = require("spectron").Application;
const assert = require("assert");
const path = require("path");
const EventEmitter = require("events");
exports.startApplication = function (configFilename, exec) {
jest.resetModules();
// Set config sample for use in test
process.env.MM_CONFIG_FILE = configFilename;
if (exec) exec;
const app = require("app.js");
app.start();
exports.getElectronPath = function () {
let electronPath = path.join(__dirname, "..", "..", "node_modules", ".bin", "electron");
if (process.platform === "win32") {
electronPath += ".cmd";
}
return electronPath;
};
// Set timeout - if this is run as CI Job, increase timeout
exports.setupTimeout = function (test) {
if (process.env.CI) {
jest.setTimeout(30000);
} else {
jest.setTimeout(10000);
}
};
exports.startApplication = function (options) {
const emitter = new EventEmitter();
emitter.setMaxListeners(100);
options.path = exports.getElectronPath();
if (process.env.CI) {
options.startTimeout = 30000;
}
const app = new Application(options);
return app.start().then(function () {
assert.strictEqual(app.isRunning(), true);
return app;
});
return app;
};
exports.stopApplication = function (app) {
if (!app || !app.isRunning()) {
return;
if (app) {
app.stop();
}
return app.stop().then(function () {
assert.strictEqual(app.isRunning(), false);
});
};

View File

@ -1,29 +1,14 @@
const helpers = require("./global-setup");
const fetch = require("node-fetch");
const helpers = require("./global-setup");
let app = null;
describe("ipWhitelist directive configuration", function () {
helpers.setupTimeout(this);
let app = null;
beforeEach(function () {
return helpers
.startApplication({
args: ["js/electron.js"]
})
.then(function (startedApp) {
app = startedApp;
});
});
afterEach(function () {
return helpers.stopApplication(app);
});
describe("Set ipWhitelist without access", function () {
beforeAll(function () {
// Set config sample for use in test
process.env.MM_CONFIG_FILE = "tests/configs/noIpWhiteList.js";
app = helpers.startApplication("tests/configs/noIpWhiteList.js");
});
afterAll(function () {
helpers.stopApplication(app);
});
it("should return 403", function (done) {
@ -36,8 +21,10 @@ describe("ipWhitelist directive configuration", function () {
describe("Set ipWhitelist []", function () {
beforeAll(function () {
// Set config sample for use in test
process.env.MM_CONFIG_FILE = "tests/configs/empty_ipWhiteList.js";
app = helpers.startApplication("tests/configs/empty_ipWhiteList.js");
});
afterAll(function () {
helpers.stopApplication(app);
});
it("should return 200", function (done) {

View File

@ -1,29 +1,14 @@
const helpers = require("./global-setup");
const fetch = require("node-fetch");
const helpers = require("./global-setup");
let app = null;
describe("port directive configuration", function () {
helpers.setupTimeout(this);
let app = null;
beforeEach(function () {
return helpers
.startApplication({
args: ["js/electron.js"]
})
.then(function (startedApp) {
app = startedApp;
});
});
afterEach(function () {
return helpers.stopApplication(app);
});
describe("Set port 8090", function () {
beforeAll(function () {
// Set config sample for use in this test
process.env.MM_CONFIG_FILE = "tests/configs/port_8090.js";
app = helpers.startApplication("tests/configs/port_8090.js");
});
afterAll(function () {
helpers.stopApplication(app);
});
it("should return 200", function (done) {
@ -36,13 +21,10 @@ describe("port directive configuration", function () {
describe("Set port 8100 on environment variable MM_PORT", function () {
beforeAll(function () {
process.env.MM_PORT = 8100;
// Set config sample for use in this test
process.env.MM_CONFIG_FILE = "tests/configs/port_8090.js";
app = helpers.startApplication("tests/configs/port_8090.js", (process.env.MM_PORT = 8100));
});
afterAll(function () {
delete process.env.MM_PORT;
helpers.stopApplication(app);
});
it("should return 200", function (done) {

View File

@ -1,24 +1,13 @@
const helpers = require("./global-setup");
const fetch = require("node-fetch");
const helpers = require("./global-setup");
let app = null;
describe("Vendors", function () {
helpers.setupTimeout(this);
let app = null;
beforeAll(function () {
process.env.MM_CONFIG_FILE = "tests/configs/env.js";
return helpers
.startApplication({
args: ["js/electron.js"]
})
.then(function (startedApp) {
app = startedApp;
});
app = helpers.startApplication("tests/configs/env.js");
});
afterAll(function () {
return helpers.stopApplication(app);
helpers.stopApplication(app);
});
describe("Get list vendors", function () {

View File

@ -0,0 +1,40 @@
const helpers = require("./global-setup");
describe("Electron app environment", function () {
helpers.setupTimeout(this);
let app = null;
beforeAll(function () {
// Set config sample for use in test
process.env.MM_CONFIG_FILE = "tests/configs/env.js";
});
beforeEach(function () {
return helpers
.startApplication({
args: ["js/electron.js"]
})
.then(function (startedApp) {
app = startedApp;
});
});
afterEach(function () {
return helpers.stopApplication(app);
});
it("should open a browserwindow", async function () {
await app.client.waitUntilWindowLoaded();
app.browserWindow.focus();
expect(await app.client.getWindowCount()).toBe(1);
expect(await app.browserWindow.isMinimized()).toBe(false);
expect(await app.browserWindow.isDevToolsOpened()).toBe(false);
expect(await app.browserWindow.isVisible()).toBe(true);
expect(await app.browserWindow.isFocused()).toBe(true);
const bounds = await app.browserWindow.getBounds();
expect(bounds.width).toBeGreaterThan(0);
expect(bounds.height).toBeGreaterThan(0);
expect(await app.browserWindow.getTitle()).toBe("MagicMirror²");
});
});

View File

@ -0,0 +1,53 @@
/*
* Magic Mirror Global Setup Test Suite
*
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
* MIT Licensed.
*/
const Application = require("spectron").Application;
const assert = require("assert");
const path = require("path");
const EventEmitter = require("events");
exports.getElectronPath = function () {
let electronPath = path.join(__dirname, "..", "..", "node_modules", ".bin", "electron");
if (process.platform === "win32") {
electronPath += ".cmd";
}
return electronPath;
};
// Set timeout - if this is run as CI Job, increase timeout
exports.setupTimeout = function (test) {
if (process.env.CI) {
jest.setTimeout(30000);
} else {
jest.setTimeout(10000);
}
};
exports.startApplication = function (options) {
const emitter = new EventEmitter();
emitter.setMaxListeners(100);
options.path = exports.getElectronPath();
if (process.env.CI) {
options.startTimeout = 30000;
}
const app = new Application(options);
return app.start().then(function () {
assert.strictEqual(app.isRunning(), true);
return app;
});
};
exports.stopApplication = function (app) {
if (!app || !app.isRunning()) {
return;
}
return app.stop().then(function () {
assert.strictEqual(app.isRunning(), false);
});
};

View File

@ -12,7 +12,7 @@ app.use(basicAuth);
// Set available directories
const directories = ["/tests/configs"];
const rootPath = path.resolve(__dirname + "/../../");
const rootPath = path.resolve(__dirname + "/../../../");
for (let directory of directories) {
app.use(directory, express.static(path.resolve(rootPath + directory)));

View File

@ -1,5 +1,5 @@
const helpers = require("../global-setup");
const serverBasicAuth = require("../../servers/basic-auth.js");
const serverBasicAuth = require("./basic-auth.js");
describe("Calendar module", function () {
helpers.setupTimeout(this);