chore(@embark/testing): Add testables to embark test objects

1. Add IPC mock and assertions.
2. Add console command registration assertion and console command mock.
3. Add event emission assertions
This commit is contained in:
emizzle 2020-02-19 10:48:55 +11:00 committed by Pascal Precht
parent d5e0897231
commit 56d5b45bbb
5 changed files with 184 additions and 24 deletions

View File

@ -2,11 +2,13 @@ const sinon = require('sinon');
import fs from 'fs-extra'; import fs from 'fs-extra';
class Embark { class Embark {
constructor(events, plugins, config) { constructor(events, plugins, config = {}, ipc) {
this.events = events; this.events = events;
this.plugins = plugins; this.plugins = plugins;
this.config = config || {}; this.config = config || {};
this.config.plugins = plugins; this.config.plugins = plugins;
this.ipc = ipc;
this.config.ipc = ipc;
this.assert = new EmbarkAssert(this); this.assert = new EmbarkAssert(this);
this.fs = fs; this.fs = fs;
@ -32,12 +34,14 @@ class Embark {
} }
teardown() { teardown() {
this.config = { plugins: this.plugins }; this.config = { plugins: this.plugins, ipc: this.ipc };
this.plugins.teardown(); this.plugins.teardown();
this.ipc.teardown();
this.events.teardown();
} }
setConfig(config) { setConfig(config) {
this.config = { ...config, plugins: this.plugins }; this.config = { ...config, plugins: this.plugins, ipc: this.ipc };
} }
} }

View File

@ -5,6 +5,7 @@ class Events {
constructor() { constructor() {
this.commandHandlers = {}; this.commandHandlers = {};
this.handlers = {}; this.handlers = {};
this.emissions = {};
this.assert = new EventsAssert(this); this.assert = new EventsAssert(this);
} }
@ -18,14 +19,13 @@ class Events {
this.handlers[ev] = []; this.handlers[ev] = [];
} }
this.handlers[ev].push(cb); this.handlers[ev].push(sinon.spy(cb));
} }
emit() { emit(ev, ...args) {
} this.emissions[ev] = args;
trigger(ev, ...args) {
if (!this.handlers[ev]) { if (!this.handlers[ev]) {
return; return;
} }
@ -34,12 +34,12 @@ class Events {
} }
request(cmd, ...args) { request(cmd, ...args) {
assert(this.commandHandlers[cmd], `command handler for ${cmd} not registered`); assert(this.commandHandlers[cmd], `command handler for '${cmd}' not registered`);
Promise.resolve(this.commandHandlers[cmd](...args)); Promise.resolve(this.commandHandlers[cmd](...args));
} }
request2(cmd, ...args) { request2(cmd, ...args) {
assert(this.commandHandlers[cmd], `command handler for ${cmd} not registered`); assert(this.commandHandlers[cmd], `command handler for '${cmd}' not registered`);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
args.push((err, ...res) => { args.push((err, ...res) => {
if (err) { if (err) {
@ -53,6 +53,12 @@ class Events {
this.commandHandlers[cmd](...args); this.commandHandlers[cmd](...args);
}); });
} }
teardown() {
this.commandHandlers = {};
this.handlers = {};
this.emissions = {};
}
} }
class EventsAssert { class EventsAssert {
@ -61,19 +67,40 @@ class EventsAssert {
} }
commandHandlerRegistered(cmd) { commandHandlerRegistered(cmd) {
assert(this.events.commandHandlers[cmd], `command handler for ${cmd} wanted, but not registered`); assert(this.events.commandHandlers[cmd], `command handler for '${cmd}' wanted, but not registered`);
} }
commandHandlerCalled(cmd) { commandHandlerCalled(cmd) {
this.commandHandlerRegistered(cmd); this.commandHandlerRegistered(cmd);
sinon.assert.called(this.events.commandHandlers[cmd]); sinon.assert.called(this.events.commandHandlers[cmd]);
} }
commandHandlerNotCalled(cmd) {
this.commandHandlerRegistered(cmd);
assert(!this.events.commandHandlers[cmd].called);
}
commandHandlerCalledWith(cmd, ...args) { commandHandlerCalledWith(cmd, ...args) {
this.commandHandlerRegistered(cmd); this.commandHandlerRegistered(cmd);
sinon.assert.calledWith(this.events.commandHandlers[cmd], ...args); sinon.assert.calledWith(this.events.commandHandlers[cmd], ...args);
} }
listenerRegistered(name) {
assert(this.events.handlers[name], `event listener for '${name}' wanted, but not registered`);
}
emitted(name) {
assert(this.events.emissions[name]);
}
notEmitted(name) {
assert(!Object.keys(this.events.emissions).includes(name));
}
emittedWith(name, ...args) {
assert.equal(this.events.emissions[name], ...args);
}
} }
module.exports = Events; module.exports = Events;

View File

@ -2,22 +2,14 @@ const Embark = require('./embark');
const Events = require('./events'); const Events = require('./events');
const Plugins = require('./plugin'); const Plugins = require('./plugin');
const HttpMockServer = require('./httpServer'); const HttpMockServer = require('./httpServer');
const Ipc = require('./ipc');
const fakeEmbark = (config) => { const fakeEmbark = (config = {}) => {
const events = new Events(); const events = new Events();
const plugins = new Plugins(); const plugins = new Plugins();
const ipc = config.ipc ?? new Ipc();
const ipc = { const embark = new Embark(events, plugins, config, ipc);
isServer: () => { return true; },
broadcast: () => {},
on: () => {},
isClient: () => { return false; }
};
config = config || {};
config.ipc = config.ipc || ipc;
const embark = new Embark(events, plugins, config);
return { return {
embark, embark,
plugins plugins
@ -29,6 +21,6 @@ module.exports = {
Events, Events,
Plugins, Plugins,
HttpMockServer, HttpMockServer,
Ipc,
fakeEmbark fakeEmbark
}; };

View File

@ -0,0 +1,85 @@
const assert = require('assert');
const sinon = require('sinon');
class Ipc {
constructor(isServer = true) {
this._isServer = isServer;
this.handlers = {};
this.assert = new IpcAssert(this);
this._client = null;
this.broadcasts = {};
}
get connected() {
return true;
}
get client() {
if (this._client !== null) {
return this._client;
}
this._client = new Ipc(false);
return this._client;
}
broadcast(ev, ...args) {
this.broadcasts[ev] = args;
}
isClient() {
return !this._isServer;
}
isServer() {
return this._isServer;
}
on(ev, cb) {
if (!this.handlers[ev]) {
this.handlers[ev] = [];
}
this.handlers[ev].push(cb);
}
request(ev, ...args) {
if (!this.handlers[ev]) {
return;
}
this.handlers[ev].forEach(h => h(...args));
}
teardown() {
this.handlers = {};
this._client = null;
this.broadcasts = {};
}
}
class IpcAssert {
constructor(ipc) {
this.ipc = ipc;
}
listenerRegistered(cmd) {
assert(this.ipc.handlers[cmd], `listener for '${cmd}' wanted, but not registered`);
}
listenerNotRegistered(cmd) {
assert(!Object.keys(this.ipc.handlers).some(command => command === cmd), `listener for '${cmd}' registered, but expected to not be registered`);
}
listenerRequested(cmd) {
this.listenerRegistered(cmd);
sinon.assert.called(this.events.handlers[cmd]);
}
listenerRequestedWith(cmd, ...args) {
this.listenerRequested(cmd);
sinon.assert.calledWith(this.events.handlers[cmd], ...args);
}
}
module.exports = Ipc;

View File

@ -46,7 +46,7 @@ class Plugins {
} }
teardown() { teardown() {
this.plugin.listeners = {}; this.plugin.teardown();
this.plugins.forEach(plugin => plugin.teardown()); this.plugins.forEach(plugin => plugin.teardown());
} }
@ -108,6 +108,10 @@ class Plugin {
} }
teardown() { teardown() {
this.listeners = {};
this.apiCalls = {};
this.pluginTypes = [];
this.console = [];
this.compilers = []; this.compilers = [];
} }
} }
@ -135,6 +139,22 @@ class PluginsAssert {
const index = (method + endpoint).toLowerCase(); const index = (method + endpoint).toLowerCase();
assert(this.plugins.plugin.apiCalls[index], `API call for '${method} ${endpoint}' wanted, but not registered`); assert(this.plugins.plugin.apiCalls[index], `API call for '${method} ${endpoint}' wanted, but not registered`);
} }
consoleCommandRegistered(command) {
const registered = this.plugins.plugin.console.some(cmd => {
if (!cmd.matches) {
return false;
}
if (Array.isArray(cmd.matches)) {
return cmd.matches.some(matches => matches.includes(command));
}
if (typeof cmd.matches === 'function') {
return cmd.matches(command);
}
return false;
});
assert(registered);
}
} }
class PluginsMock { class PluginsMock {
@ -142,6 +162,38 @@ class PluginsMock {
this.plugins = plugins; this.plugins = plugins;
} }
consoleCommand(cmd) {
const command = this.plugins.plugin.console.find(c => {
if (!c.matches) {
return;
}
if (Array.isArray(c.matches) && c.matches.some(matches => matches.includes(cmd))) {
return c;
}
if (typeof c.matches === 'function' && c.matches(cmd)) {
return c;
}
return;
});
assert(command, `Console command for '${cmd}' wanted, but not registered`);
const cb = sinon.fake();
return new Promise((resolve, reject) => {
command.process(cmd, (err, ...res) => {
if (err) {
return reject(err);
}
if (res.length && res.length > 1) {
cb(res);
return resolve(cb);
}
cb(res[0]);
return resolve(cb);
});
resolve(cb);
});
}
async apiCall(method, endpoint, params) { async apiCall(method, endpoint, params) {
const index = (method + endpoint).toLowerCase(); const index = (method + endpoint).toLowerCase();
const apiFn = this.plugins.plugin.apiCalls[index]; const apiFn = this.plugins.plugin.apiCalls[index];