diff --git a/packages/utils/testing/src/embark.js b/packages/utils/testing/src/embark.js index cb4b1ae83..4c4c7fd90 100644 --- a/packages/utils/testing/src/embark.js +++ b/packages/utils/testing/src/embark.js @@ -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 }; } } diff --git a/packages/utils/testing/src/events.js b/packages/utils/testing/src/events.js index bd526a6db..0e617f23e 100644 --- a/packages/utils/testing/src/events.js +++ b/packages/utils/testing/src/events.js @@ -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,19 +67,40 @@ 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) { this.commandHandlerRegistered(cmd); 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; diff --git a/packages/utils/testing/src/index.js b/packages/utils/testing/src/index.js index a69d42ac9..9e4fde344 100644 --- a/packages/utils/testing/src/index.js +++ b/packages/utils/testing/src/index.js @@ -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 }; diff --git a/packages/utils/testing/src/lib/ipc.js b/packages/utils/testing/src/lib/ipc.js new file mode 100644 index 000000000..164a73f73 --- /dev/null +++ b/packages/utils/testing/src/lib/ipc.js @@ -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; diff --git a/packages/utils/testing/src/plugin.js b/packages/utils/testing/src/plugin.js index be9caefa3..27c6d94fe 100644 --- a/packages/utils/testing/src/plugin.js +++ b/packages/utils/testing/src/plugin.js @@ -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];