From c0410be40ebebaa69a8c8800f287dcd107c3bfca Mon Sep 17 00:00:00 2001 From: matteofigus Date: Wed, 20 Dec 2017 16:18:49 +0000 Subject: [PATCH 1/2] [DX-296] Mocks can have the same signature as real plugins --- src/cli/domain/get-mocked-plugins.js | 82 ++++++++++++---------- src/resources/index.js | 36 ++++++++-- test/unit/cli-domain-get-mocked-plugins.js | 78 +++++++++++++------- 3 files changed, 126 insertions(+), 70 deletions(-) diff --git a/src/cli/domain/get-mocked-plugins.js b/src/cli/domain/get-mocked-plugins.js index bd3460420..553d2ad4b 100644 --- a/src/cli/domain/get-mocked-plugins.js +++ b/src/cli/domain/get-mocked-plugins.js @@ -1,6 +1,5 @@ 'use strict'; -const colors = require('colors/safe'); const fs = require('fs-extra'); const path = require('path'); const _ = require('lodash'); @@ -8,66 +7,71 @@ const _ = require('lodash'); const settings = require('../../resources/settings'); const strings = require('../../resources/'); -const registerStaticMocks = function(mocks, logger) { - return _.map(mocks, (mockedValue, pluginName) => { - logger.log(colors.green('├── ' + pluginName + ' () => ' + mockedValue)); +const isMockValid = plugin => { + const isFunction = _.isFunction(plugin); + const isValidObject = + _.isObject(plugin) && + _.isFunction(plugin.register) && + _.isFunction(plugin.execute); + return isFunction || isValidObject; +}; + +const defaultRegister = (options, dependencies, next) => next(); + +const registerStaticMocks = (mocks, logger) => + _.map(mocks, (mockedValue, pluginName) => { + logger.ok(`├── ${pluginName} () => ${mockedValue}`); return { name: pluginName, register: { - register: function(options, dependencies, next) { - return next(); - }, - execute: function() { - return mockedValue; - } + register: defaultRegister, + execute: () => mockedValue } }; }); -}; -const registerDynamicMocks = function(ocJsonLocation, mocks, logger) { - return _.map(mocks, (source, pluginName) => { - let p; +const registerDynamicMocks = (ocJsonLocation, mocks, logger) => + _.map(mocks, (source, pluginName) => { + let pluginMock; try { - p = require(path.resolve(ocJsonLocation, source)); + pluginMock = require(path.resolve(ocJsonLocation, source)); } catch (er) { logger.err(er.toString()); return; } - if (!_.isFunction(p)) { - logger.err(strings.errors.cli.MOCK_PLUGIN_IS_NOT_A_FUNCTION); + if (!isMockValid(pluginMock)) { + logger.err(`├── ${pluginName} () => Error (skipping)`); + logger.err(strings.errors.cli.MOCK_PLUGIN_IS_NOT_VALID); return; } - logger.log(colors.green('├── ' + pluginName + ' () => [Function]')); + const register = pluginMock.register || defaultRegister; + const execute = pluginMock.execute || pluginMock; + + logger.ok(`├── ${pluginName} () => [Function]`); + return { name: pluginName, - register: { - register: function(options, dependencies, next) { - return next(); - }, - execute: p - } + register: { execute, register } }; - }).filter(p => p); -}; + }).filter(pluginMock => pluginMock); const findPath = function(pathToResolve, fileName) { - const rootDir = fs.realpathSync('.'), - fileToResolve = path.join(pathToResolve, fileName); + const rootDir = fs.realpathSync('.'); + const fileToResolve = path.join(pathToResolve, fileName); if (!fs.existsSync(fileToResolve)) { if (pathToResolve === rootDir) { return undefined; } else { - const getParent = function(x) { - return x - .split('/') - .slice(0, -1) - .join('/'); - }, - parentDir = pathToResolve ? getParent(pathToResolve) : rootDir; + const getParent = x => + x + .split('/') + .slice(0, -1) + .join('/'); + + const parentDir = pathToResolve ? getParent(pathToResolve) : rootDir; return findPath(parentDir, fileName); } @@ -80,15 +84,15 @@ module.exports = function(logger, componentsDir) { componentsDir = path.resolve(componentsDir || '.'); let plugins = []; - const ocJsonFileName = settings.configFile.src.replace('./', ''), - ocJsonPath = findPath(componentsDir, ocJsonFileName); + const ocJsonFileName = settings.configFile.src.replace('./', ''); + const ocJsonPath = findPath(componentsDir, ocJsonFileName); if (!ocJsonPath) { return plugins; } - const content = fs.readJsonSync(ocJsonPath), - ocJsonLocation = ocJsonPath.slice(0, -ocJsonFileName.length); + const content = fs.readJsonSync(ocJsonPath); + const ocJsonLocation = ocJsonPath.slice(0, -ocJsonFileName.length); if (!content.mocks || !content.mocks.plugins) { return plugins; diff --git a/src/resources/index.js b/src/resources/index.js index 959640a19..f6b6663ae 100644 --- a/src/resources/index.js +++ b/src/resources/index.js @@ -1,6 +1,25 @@ 'use strict'; const colors = require('colors/safe'); +const validFunctionMock = `// simplified mock signature +module.exports = (a, b) => a+b;`; + +const validMockObject = `// standard plugin-like signature + +const dbClient = require('my-db-client'); +let cache; + +module.exports.register = (options, dependencies, next) => { + let client = dbClient(options); + client.init((err, response) => { + cache = response; + next(err); + }); +}; + +module.exports.execute = key => cache[key]; +`; + module.exports = { commands: { cli: { @@ -91,7 +110,9 @@ module.exports = { }, cli: { scaffoldError: (url, error) => - `Scaffolding failed. Please open an issue on ${url} with the following information: ${error}`, + `Scaffolding failed. Please open an issue on ${ + url + } with the following information: ${error}`, COMPONENT_HREF_NOT_FOUND: "The specified path is not a valid component's url", COMPONENTS_NOT_FOUND: 'no components found in specified path', @@ -102,8 +123,13 @@ module.exports = { DEV_FAIL: 'An error happened when initialising the dev runner: {0}', INIT_FAIL: 'An error happened when initialising the component: {0}', INVALID_CREDENTIALS: 'Invalid credentials', - MOCK_PLUGIN_IS_NOT_A_FUNCTION: - 'Looks like you are trying to register a dynamic mock plugin but the file you specified is not a function', + MOCK_PLUGIN_IS_NOT_VALID: `Looks like you are trying to register a dynamic mock plugin but the file you specified is not a valid mock. +The entry point should be a synchronous function or an object containing an asynchronous register() function and a synchronous execute() function. +Example: + +${colors.yellow(validFunctionMock)} + +${colors.yellow(validMockObject)}`, NAME_NOT_VALID: 'the name is not valid. Allowed characters are alphanumeric, _, -', NODE_CLI_VERSION_NEEDS_UPGRADE: @@ -164,7 +190,9 @@ Happy coding installCompilerSuccess: (template, compiler, version) => `${colors.green('✔')} Installed ${compiler} [${template} v${version}]`, legacyTemplateDeprecationWarning: (legacyType, newType) => - `Template-type "${legacyType}" has been deprecated and is now replaced by "${newType}"`, + `Template-type "${ + legacyType + }" has been deprecated and is now replaced by "${newType}"`, CHANGES_DETECTED: 'Changes detected on file: {0}', CHECKING_DEPENDENCIES: 'Ensuring dependencies are loaded...', COMPONENT_INITED: 'Success! Created "{0}"', diff --git a/test/unit/cli-domain-get-mocked-plugins.js b/test/unit/cli-domain-get-mocked-plugins.js index 49b41ad22..4aad7cd1f 100644 --- a/test/unit/cli-domain-get-mocked-plugins.js +++ b/test/unit/cli-domain-get-mocked-plugins.js @@ -6,11 +6,14 @@ const sinon = require('sinon'); const _ = require('lodash'); describe('cli : domain : get-mocked-plugins', () => { - const dynamicPluginModule = function(a) { - return a ? 'blarg' : 'flarg'; - }, - notAFunctionModule = { foo: 'bar' }; + const dynamicPluginModule = a => (a ? 'blarg' : 'flarg'); + const notAFunctionModule = { foo: 'bar' }; + const dynamicObjectPluginModule = { + register: (opts, deps, next) => next(), + execute: () => 'result' + }; + const logMock = { err: _.noop, log: _.noop, ok: _.noop, warn: _.noop }; let fsMock, getMockedPlugins; const initialise = function(fs, pathJoinStub) { @@ -37,6 +40,7 @@ describe('cli : domain : get-mocked-plugins', () => { join: pathJoinStub || fakePathFunc, resolve: fakePathFunc }, + '/root/components/dynamic-object-plugin.js': dynamicObjectPluginModule, '/root/components/dynamic-plugin.js': dynamicPluginModule, '/root/components/not-a-function.js': notAFunctionModule }); @@ -48,7 +52,7 @@ describe('cli : domain : get-mocked-plugins', () => { beforeEach(() => { initialise({}, joinStub); - getMockedPlugins({ log: _.noop }, undefined); + getMockedPlugins(logMock, undefined); }); it('should use . as default', () => { @@ -61,7 +65,7 @@ describe('cli : domain : get-mocked-plugins', () => { beforeEach(() => { initialise({}, joinStub); - getMockedPlugins({ log: _.noop }); + getMockedPlugins(logMock); }); it('should use . as default', () => { @@ -87,10 +91,7 @@ describe('cli : domain : get-mocked-plugins', () => { existsSync: sinon.stub().returns(true), readJsonSync: readMock }); - result = getMockedPlugins( - { log: () => {}, warn: () => {} }, - '/root/components/' - ); + result = getMockedPlugins(logMock, '/root/components/'); }); it('should use components folder oc.json as default', () => { @@ -133,10 +134,7 @@ describe('cli : domain : get-mocked-plugins', () => { existsSync: existsMock, readJsonSync: readMock }); - result = getMockedPlugins( - { log: () => {}, warn: () => {} }, - '/root/components/' - ); + result = getMockedPlugins(logMock, '/root/components/'); }); it('should use root oc.json', () => { @@ -148,7 +146,7 @@ describe('cli : domain : get-mocked-plugins', () => { let result; beforeEach(() => { initialise({ existsSync: sinon.stub().returns(false) }); - result = getMockedPlugins(console, '/root/components/'); + result = getMockedPlugins(logMock, '/root/components/'); }); it('should return an empty array', () => { @@ -170,7 +168,7 @@ describe('cli : domain : get-mocked-plugins', () => { existsSync: sinon.stub().returns(true), readJsonSync: sinon.stub().returns(ocJson) }); - result = getMockedPlugins({ warn: sinon.stub() }, '/root/components/'); + result = getMockedPlugins(logMock, '/root/components/'); }); it('should return an empty array', () => { @@ -196,10 +194,7 @@ describe('cli : domain : get-mocked-plugins', () => { existsSync: sinon.stub().returns(true), readJsonSync: sinon.stub().returns(ocJson) }); - result = getMockedPlugins( - { log: () => {}, warn: () => {} }, - '/root/components/' - ); + result = getMockedPlugins(logMock, '/root/components/'); }); it('should return the static plugin', () => { @@ -212,7 +207,7 @@ describe('cli : domain : get-mocked-plugins', () => { }); }); - describe('when a dynamic plugin is specified', () => { + describe('when a dynamic plugin with a function signature is specified', () => { let result; const ocJson = { registries: [], @@ -230,10 +225,7 @@ describe('cli : domain : get-mocked-plugins', () => { existsSync: sinon.stub().returns(true), readJsonSync: sinon.stub().returns(ocJson) }); - result = getMockedPlugins( - { log: () => {}, warn: () => {} }, - '/root/components/' - ); + result = getMockedPlugins(logMock, '/root/components/'); }); it('should return the dynamic plugin', () => { @@ -247,6 +239,37 @@ describe('cli : domain : get-mocked-plugins', () => { }); }); + describe('when a dynamic plugin with an object signature is specified', () => { + let result; + const ocJson = { + registries: [], + mocks: { + plugins: { + dynamic: { + myPlugin: './dynamic-object-plugin.js' + } + } + } + }; + + beforeEach(() => { + initialise({ + existsSync: sinon.stub().returns(true), + readJsonSync: sinon.stub().returns(ocJson) + }); + result = getMockedPlugins(logMock, '/root/components/'); + }); + + it('should return the dynamic plugin', () => { + expect(result.length).to.equal(1); + expect(result[0].name).to.equal('myPlugin'); + }); + + it('should set up the execute method to run the module', () => { + expect(result[0].register.execute()).to.equal('result'); + }); + }); + describe('when a dynamic plugin is specified and the referenced file is missing', () => { let result; const ocJson = { @@ -303,8 +326,9 @@ describe('cli : domain : get-mocked-plugins', () => { }); it('should log an error', () => { - expect(logger.err.args[0][0]).to.contain( - 'Looks like you are trying to register a dynamic mock plugin but the file you specified is not a function' + expect(logger.err.args[0][0]).to.contain('foo () => Error (skipping)'); + expect(logger.err.args[1][0]).to.contain( + 'Looks like you are trying to register a dynamic mock plugin but the file you specified is not a valid mock' ); }); From 0d48ade118023530217fbd3ef7b4090a2fc1c505 Mon Sep 17 00:00:00 2001 From: matteofigus Date: Wed, 20 Dec 2017 17:00:10 +0000 Subject: [PATCH 2/2] x --- src/cli/domain/get-mocked-plugins.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/domain/get-mocked-plugins.js b/src/cli/domain/get-mocked-plugins.js index 553d2ad4b..bafd19d96 100644 --- a/src/cli/domain/get-mocked-plugins.js +++ b/src/cli/domain/get-mocked-plugins.js @@ -65,8 +65,8 @@ const findPath = function(pathToResolve, fileName) { if (pathToResolve === rootDir) { return undefined; } else { - const getParent = x => - x + const getParent = pathToResolve => + pathToResolve .split('/') .slice(0, -1) .join('/');