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';
class Embark {
constructor(events, plugins, config) {
constructor(events, plugins, config = {}, ipc) {
this.events = events;
this.plugins = plugins;
this.config = config || {};
this.config.plugins = plugins;
this.ipc = ipc;
this.config.ipc = ipc;
this.assert = new EmbarkAssert(this);
this.fs = fs;
@ -32,12 +34,14 @@ class Embark {
}
teardown() {
this.config = { plugins: this.plugins };
this.config = { plugins: this.plugins, ipc: this.ipc };
this.plugins.teardown();
this.ipc.teardown();
this.events.teardown();
}
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() {
this.commandHandlers = {};
this.handlers = {};
this.emissions = {};
this.assert = new EventsAssert(this);
}
@ -18,14 +19,13 @@ class Events {
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]) {
return;
}
@ -34,12 +34,12 @@ class Events {
}
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));
}
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) => {
args.push((err, ...res) => {
if (err) {
@ -53,6 +53,12 @@ class Events {
this.commandHandlers[cmd](...args);
});
}
teardown() {
this.commandHandlers = {};
this.handlers = {};
this.emissions = {};
}
}
class EventsAssert {
@ -61,7 +67,7 @@ class EventsAssert {
}
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) {
@ -69,11 +75,32 @@ class EventsAssert {
sinon.assert.called(this.events.commandHandlers[cmd]);
}
commandHandlerNotCalled(cmd) {
this.commandHandlerRegistered(cmd);
assert(!this.events.commandHandlers[cmd].called);
}
commandHandlerCalledWith(cmd, ...args) {
this.commandHandlerRegistered(cmd);
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;

View File

@ -2,22 +2,14 @@ const Embark = require('./embark');
const Events = require('./events');
const Plugins = require('./plugin');
const HttpMockServer = require('./httpServer');
const Ipc = require('./ipc');
const fakeEmbark = (config) => {
const fakeEmbark = (config = {}) => {
const events = new Events();
const plugins = new Plugins();
const ipc = config.ipc ?? new Ipc();
const ipc = {
isServer: () => { return true; },
broadcast: () => {},
on: () => {},
isClient: () => { return false; }
};
config = config || {};
config.ipc = config.ipc || ipc;
const embark = new Embark(events, plugins, config);
const embark = new Embark(events, plugins, config, ipc);
return {
embark,
plugins
@ -29,6 +21,6 @@ module.exports = {
Events,
Plugins,
HttpMockServer,
Ipc,
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() {
this.plugin.listeners = {};
this.plugin.teardown();
this.plugins.forEach(plugin => plugin.teardown());
}
@ -108,6 +108,10 @@ class Plugin {
}
teardown() {
this.listeners = {};
this.apiCalls = {};
this.pluginTypes = [];
this.console = [];
this.compilers = [];
}
}
@ -135,6 +139,22 @@ class PluginsAssert {
const index = (method + endpoint).toLowerCase();
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 {
@ -142,6 +162,38 @@ class PluginsMock {
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) {
const index = (method + endpoint).toLowerCase();
const apiFn = this.plugins.plugin.apiCalls[index];