From a1f76e5aa6aaac2d4388799b80503c6564e2339e Mon Sep 17 00:00:00 2001 From: James Date: Mon, 23 Sep 2019 19:56:43 +0100 Subject: [PATCH 1/3] Add recursive extends support for deep .dojorc merging --- src/configurationHelper.ts | 49 +++++++++++++++++- tests/unit/configurationHelper.ts | 82 +++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 2 deletions(-) diff --git a/src/configurationHelper.ts b/src/configurationHelper.ts index bef16225..c4546866 100644 --- a/src/configurationHelper.ts +++ b/src/configurationHelper.ts @@ -81,15 +81,60 @@ function writeDojoRcConfig(config: Config, indent: string | number) { writeFileSync(dojoRcPath, json); } +function mergeConfigs(config: Config) { + const configs = getExtendingConfigs(config); + let mergedConfig: Config = config; + if (configs.length) { + let baseConfig: Config; + while ((baseConfig = configs.shift())) { + mergedConfig = merge(mergedConfig, baseConfig); + } + } + return mergedConfig; +} + +function getExtendingConfigs(config: Config) { + const configs = []; + let extendingConfig = config; + while (typeof extendingConfig.extends === 'string') { + const extendedConfig = JSON.parse(readFileSync(extendingConfig.extends, 'utf8')); + configs.push(extendedConfig); + extendingConfig = extendedConfig; + } + return configs; +} + +function merge(extendingObj: { [key: string]: any }, baseObj: { [key: string]: any }) { + Object.keys(baseObj).forEach((prop) => { + if (extendingObj[prop] === undefined) { + extendingObj[prop] = baseObj[prop]; + } else if (isObject(baseObj[prop])) { + extendingObj[prop] = merge(extendingObj[prop], baseObj[prop]); + } + }); + return extendingObj; +} + +function isObject(item: any) { + return item && typeof item === 'object' && !Array.isArray(item); +} + +function checkIfConfigExtends(config?: Config) { + if (config && config.extends) { + config = mergeConfigs(config); + } + return config; +} + export function getConfig(): Config | undefined { const { packageJsonConfig, dojoRcConfig } = parseConfigs(); const hasPackageConfig = typeof packageJsonConfig === 'object'; const hasDojoRcConfig = typeof dojoRcConfig === 'object'; if (!hasDojoRcConfig && hasPackageConfig) { - return packageJsonConfig; + return checkIfConfigExtends(packageJsonConfig); } else { - return dojoRcConfig; + return checkIfConfigExtends(dojoRcConfig); } } diff --git a/tests/unit/configurationHelper.ts b/tests/unit/configurationHelper.ts index 179d346e..4a48d657 100644 --- a/tests/unit/configurationHelper.ts +++ b/tests/unit/configurationHelper.ts @@ -185,6 +185,88 @@ registerSuite('Configuration Helper', { assert.equal(mockFs.readFileSync.firstCall.args[0], dojoRcPath); assert.deepEqual(config, existingConfig); }, + 'Should shallow extend configs correctly'() { + mockFs.existsSync.onCall(0).returns(true); + mockFs.existsSync.onCall(1).returns(false); + mockFs.readFileSync.onCall(0).returns( + JSON.stringify({ + extends: './path/to/dojorc/to/.extend', + 'testGroupName-testCommandName': { + prop: 'config' + } + }) + ); + mockFs.readFileSync.onCall(1).returns( + JSON.stringify({ + 'testGroupName-testCommandName': { + prop: 'config-extended' + } + }) + ); + const config = configurationHelper.sandbox('testGroupName', 'testCommandName').get(); + assert.isTrue(mockFs.readFileSync.calledTwice); + assert.equal(mockFs.readFileSync.firstCall.args[0], dojoRcPath); + assert.deepEqual(config, { + prop: 'config' + }); + }, + 'Supports deep extension of configs correctly'() { + mockFs.existsSync.onCall(0).returns(true); + mockFs.existsSync.onCall(1).returns(false); + mockFs.readFileSync.onCall(0).returns( + JSON.stringify({ + extends: './path/to/dojorc/to/.extend', + 'testGroupName-testCommandName': { + prop: 'config' + } + }) + ); + mockFs.readFileSync.onCall(1).returns( + JSON.stringify({ + extends: './path/to/dojorc/to/.extend1', + 'testGroupName-testCommandName': { + prop: 'config-extended-1', + prop1: 'config-extended-1', + prop3: { + innerProp: 'config-inner-prop', + innerProp1: { + deepProp: 'config-deep-prop', + deepProp1: 'config-deep-prop' + } + } + } + }) + ); + mockFs.readFileSync.onCall(2).returns( + JSON.stringify({ + 'testGroupName-testCommandName': { + prop: 'config-extended-2', + prop2: 'config-extended-2', + prop3: { + innerProp: 'config-inner-prop-1', + innerProp1: { + deepProp: 'config-deep-prop' + } + } + } + }) + ); + const config = configurationHelper.sandbox('testGroupName', 'testCommandName').get(); + assert.equal(mockFs.readFileSync.callCount, 3); + assert.equal(mockFs.readFileSync.firstCall.args[0], dojoRcPath); + assert.deepEqual(config, { + prop: 'config', + prop1: 'config-extended-1', + prop2: 'config-extended-2', + prop3: { + innerProp: 'config-inner-prop', + innerProp1: { + deepProp: 'config-deep-prop', + deepProp1: 'config-deep-prop' + } + } + }); + }, 'Should accept and ignore commandName parameter'() { const newConfig = { foo: 'bar' }; mockFs.readFileSync = sinon.stub().returns(JSON.stringify({ 'testGroupName-testCommandName': {} })); From 3372e5226139c1ec84a13b2809d928a893bfdda5 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 24 Sep 2019 14:02:15 +0100 Subject: [PATCH 2/3] Use @speed/json-extends rather than custom code --- package-lock.json | 49 +++++++++++++++----- package.json | 1 + src/configurationHelper.ts | 59 ++++-------------------- src/index.ts | 2 +- src/loadCommands.ts | 3 +- tests/unit/configurationHelper.ts | 75 +++++++++++++++++++++---------- tests/unit/index.ts | 2 +- 7 files changed, 102 insertions(+), 89 deletions(-) diff --git a/package-lock.json b/package-lock.json index 16466747..f45ac85e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -146,6 +146,14 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, + "@speedy/json-extends": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@speedy/json-extends/-/json-extends-1.2.0.tgz", + "integrity": "sha512-cM/tuoAoyCYdBkn596H4oWkS1B0Kn/G+wXLtbg5/+vsJsCbh4EfwMYsw5q8lbjy/W5Ko+MagDcD/YcmcquN6yw==", + "requires": { + "lodash": "^4.17.4" + } + }, "@theintern/digdug": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@theintern/digdug/-/digdug-2.1.2.tgz", @@ -2866,7 +2874,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2887,12 +2896,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2907,17 +2918,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3034,7 +3048,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3046,6 +3061,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3060,6 +3076,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3067,12 +3084,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3091,6 +3110,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3171,7 +3191,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3183,6 +3204,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3268,7 +3290,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3304,6 +3327,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3323,6 +3347,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3366,12 +3391,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/package.json b/package.json index 08d0fb93..855373d9 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "typings.json" ], "dependencies": { + "@speedy/json-extends": "^1.2.0", "ajv": "6.6.2", "chalk": "2.4.1", "configstore": "3.1.2", diff --git a/src/configurationHelper.ts b/src/configurationHelper.ts index c4546866..4c3deba4 100644 --- a/src/configurationHelper.ts +++ b/src/configurationHelper.ts @@ -4,9 +4,9 @@ import { join } from 'path'; import { Config, ConfigurationHelper, ConfigWrapper } from './interfaces'; import * as readlineSync from 'readline-sync'; import * as detectIndent from 'detect-indent'; +import { json } from '@speedy/json-extends'; const pkgDir = require('pkg-dir'); - const appPath = pkgDir.sync(process.cwd()); export function getDojoRcConfigOption(): string { @@ -37,18 +37,20 @@ function parseConfigs(): ConfigWrapper { if (existsSync(dojoRcPath)) { try { const dojoRcFile = readFileSync(dojoRcPath, 'utf8'); + const extendedConfig = json.readSync(dojoRcPath); configWrapper.dojoRcIndent = detectIndent(dojoRcFile).indent; - configWrapper.dojoRcConfig = JSON.parse(dojoRcFile); + configWrapper.dojoRcConfig = extendedConfig; } catch (error) { - throw Error(chalk.red(`Could not parse the .dojorc file to get config : ${error}`)); + throw Error(chalk.red(`Could not parse the .dojorc file to get config: ${error}`)); } } if (existsSync(packageJsonPath)) { try { const packageJsonFile = readFileSync(packageJsonPath, 'utf8'); - const packageJson = JSON.parse(packageJsonFile); + const packageJson: any = json.readSync(packageJsonPath); configWrapper.packageJsonIndent = detectIndent(packageJsonFile).indent; + configWrapper.packageJsonConfig = packageJson.dojo; } catch (error) { throw Error(chalk.red(`Could not parse the package.json file to get config: ${error}`)); @@ -81,60 +83,15 @@ function writeDojoRcConfig(config: Config, indent: string | number) { writeFileSync(dojoRcPath, json); } -function mergeConfigs(config: Config) { - const configs = getExtendingConfigs(config); - let mergedConfig: Config = config; - if (configs.length) { - let baseConfig: Config; - while ((baseConfig = configs.shift())) { - mergedConfig = merge(mergedConfig, baseConfig); - } - } - return mergedConfig; -} - -function getExtendingConfigs(config: Config) { - const configs = []; - let extendingConfig = config; - while (typeof extendingConfig.extends === 'string') { - const extendedConfig = JSON.parse(readFileSync(extendingConfig.extends, 'utf8')); - configs.push(extendedConfig); - extendingConfig = extendedConfig; - } - return configs; -} - -function merge(extendingObj: { [key: string]: any }, baseObj: { [key: string]: any }) { - Object.keys(baseObj).forEach((prop) => { - if (extendingObj[prop] === undefined) { - extendingObj[prop] = baseObj[prop]; - } else if (isObject(baseObj[prop])) { - extendingObj[prop] = merge(extendingObj[prop], baseObj[prop]); - } - }); - return extendingObj; -} - -function isObject(item: any) { - return item && typeof item === 'object' && !Array.isArray(item); -} - -function checkIfConfigExtends(config?: Config) { - if (config && config.extends) { - config = mergeConfigs(config); - } - return config; -} - export function getConfig(): Config | undefined { const { packageJsonConfig, dojoRcConfig } = parseConfigs(); const hasPackageConfig = typeof packageJsonConfig === 'object'; const hasDojoRcConfig = typeof dojoRcConfig === 'object'; if (!hasDojoRcConfig && hasPackageConfig) { - return checkIfConfigExtends(packageJsonConfig); + return packageJsonConfig; } else { - return checkIfConfigExtends(dojoRcConfig); + return dojoRcConfig; } } diff --git a/src/index.ts b/src/index.ts index 8adf1bbc..b2ec0875 100755 --- a/src/index.ts +++ b/src/index.ts @@ -21,7 +21,7 @@ export async function init() { registerCommands(yargs, mergedCommands); } catch (err) { - console.log(`Commands are not available: ${err}`); + console.log(`Commands are not available:\n ${err.message}`); } try { diff --git a/src/loadCommands.ts b/src/loadCommands.ts index be9e15c9..3f1a81be 100644 --- a/src/loadCommands.ts +++ b/src/loadCommands.ts @@ -2,6 +2,7 @@ import * as globby from 'globby'; import { resolve as pathResolve, join } from 'path'; import { CliConfig, CommandWrapper, GroupMap } from './interfaces'; import configurationHelper from './configurationHelper'; +import chalk from 'chalk'; export function isEjected(groupName: string, command: string): boolean { const config: any = configurationHelper.sandbox(groupName, command).get(); @@ -72,7 +73,7 @@ export async function loadCommands(paths: string[], load: (path: string) => Comm } } } catch (error) { - error.message = `Failed to load module ${path}\nNested error: ${error.message}`; + error.message = `${chalk.red(`Failed to load module ${path}`)}\n\nNested error:\n ${error.message}`; reject(error); } }); diff --git a/tests/unit/configurationHelper.ts b/tests/unit/configurationHelper.ts index 4a48d657..e26cfff9 100644 --- a/tests/unit/configurationHelper.ts +++ b/tests/unit/configurationHelper.ts @@ -52,7 +52,8 @@ registerSuite('Configuration Helper', { tests: { 'Should write new config to file when save called'() { const newConfig = { foo: 'bar' }; - mockFs.readFileSync = sinon.stub().returns(JSON.stringify({ 'testGroupName-testCommandName': {} })); + const config = { 'testGroupName-testCommandName': {} }; + mockFs.readFileSync = sinon.stub().returns(JSON.stringify(config)); configurationHelper.sandbox('testGroupName', 'testCommandName').set(newConfig); assert.isTrue(consoleWarnStub.notCalled); assert.isTrue(mockFs.writeFileSync.calledOnce); @@ -64,7 +65,8 @@ registerSuite('Configuration Helper', { }, 'Should write new config to file when save called without commandName'() { const newConfig = { foo: 'bar' }; - mockFs.readFileSync = sinon.stub().returns(JSON.stringify({ testGroupName: {} })); + const config = { testGroupName: {} }; + mockFs.readFileSync = sinon.stub().returns(JSON.stringify(config)); configurationHelper.sandbox('testGroupName').set(newConfig); assert.isTrue(consoleWarnStub.notCalled); assert.isTrue(mockFs.writeFileSync.calledOnce); @@ -78,8 +80,8 @@ registerSuite('Configuration Helper', { const newConfig = { foo: 'bar' }; const existingConfig = { existing: 'config' }; const mergedConfigs = Object.assign(existingConfig, newConfig); - - mockFs.readFileSync.returns(JSON.stringify({ 'testGroupName-testCommandName': existingConfig })); + const config = { 'testGroupName-testCommandName': existingConfig }; + mockFs.readFileSync.returns(JSON.stringify(config)); configurationHelper.sandbox('testGroupName', 'testCommandName').set(newConfig); assert.isTrue(consoleErrorStub.notCalled); assert.isTrue(consoleWarnStub.notCalled); @@ -107,7 +109,8 @@ registerSuite('Configuration Helper', { 'Should merge new commandNames with existing command config to .dojorc when set called'() { const newConfig = { foo: 'bar' }; const existingConfig = { existing: 'config' }; - mockFs.readFileSync.returns(JSON.stringify({ existingCommandName: existingConfig })); + const config = { existingCommandName: existingConfig }; + mockFs.readFileSync.returns(JSON.stringify(config)); configurationHelper.sandbox('testGroupName', 'testCommandName').set(newConfig); assert.isTrue(consoleErrorStub.notCalled); assert.isTrue(consoleWarnStub.notCalled); @@ -128,10 +131,10 @@ registerSuite('Configuration Helper', { 'Should write .dojorc with current .dojorc identation of 4 spaces'() { const newConfig = { foo: 'bar' }; const existingConfig = { existing: 'config' }; - mockFs.readFileSync - .onCall(0) - .returns(JSON.stringify({ existingCommandName: existingConfig }, null, ' ')); - mockFs.readFileSync.onCall(1).returns('{}'); + const config = { existingCommandName: existingConfig }; + mockFs.readFileSync.onCall(0).returns(JSON.stringify(config, null, ' ')); + mockFs.readFileSync.onCall(1).returns(JSON.stringify(config, null, ' ')); + mockFs.readFileSync.onCall(2).returns('{}'); configurationHelper.sandbox('testGroupName', 'testCommandName').set(newConfig); assert.isTrue(consoleWarnStub.notCalled); assert.isTrue(consoleErrorStub.notCalled); @@ -150,10 +153,11 @@ registerSuite('Configuration Helper', { 'Should write .dojorc with current .dojorc identation of 2 spaces'() { const newConfig = { foo: 'bar' }; const existingConfig = { existing: 'config' }; - mockFs.readFileSync - .onCall(0) - .returns(JSON.stringify({ existingCommandName: existingConfig }, null, ' ')); - mockFs.readFileSync.onCall(1).returns('{}'); + const config = { existingCommandName: existingConfig }; + + mockFs.readFileSync.onCall(0).returns(JSON.stringify(config, null, ' ')); + mockFs.readFileSync.onCall(1).returns(JSON.stringify(config, null, ' ')); + mockFs.readFileSync.onCall(2).returns('{}'); configurationHelper.sandbox('testGroupName', 'testCommandName').set(newConfig); assert.isTrue(consoleWarnStub.notCalled); assert.isTrue(consoleErrorStub.notCalled); @@ -179,9 +183,11 @@ registerSuite('Configuration Helper', { const existingConfig = { existing: 'config' }; mockFs.existsSync.onCall(0).returns(true); mockFs.existsSync.onCall(1).returns(false); - mockFs.readFileSync.returns(JSON.stringify({ 'testGroupName-testCommandName': existingConfig })); + const readConfig = { 'testGroupName-testCommandName': existingConfig }; + mockFs.readFileSync.onCall(0).returns(JSON.stringify(readConfig)); + mockFs.readFileSync.onCall(1).returns(JSON.stringify(readConfig)); const config = configurationHelper.sandbox('testGroupName', 'testCommandName').get(); - assert.isTrue(mockFs.readFileSync.calledOnce); + assert.isTrue(mockFs.readFileSync.calledTwice); assert.equal(mockFs.readFileSync.firstCall.args[0], dojoRcPath); assert.deepEqual(config, existingConfig); }, @@ -197,6 +203,14 @@ registerSuite('Configuration Helper', { }) ); mockFs.readFileSync.onCall(1).returns( + JSON.stringify({ + extends: './path/to/dojorc/to/.extend', + 'testGroupName-testCommandName': { + prop: 'config' + } + }) + ); + mockFs.readFileSync.onCall(2).returns( JSON.stringify({ 'testGroupName-testCommandName': { prop: 'config-extended' @@ -204,7 +218,7 @@ registerSuite('Configuration Helper', { }) ); const config = configurationHelper.sandbox('testGroupName', 'testCommandName').get(); - assert.isTrue(mockFs.readFileSync.calledTwice); + assert.equal(mockFs.readFileSync.callCount, 3); assert.equal(mockFs.readFileSync.firstCall.args[0], dojoRcPath); assert.deepEqual(config, { prop: 'config' @@ -222,6 +236,14 @@ registerSuite('Configuration Helper', { }) ); mockFs.readFileSync.onCall(1).returns( + JSON.stringify({ + extends: './path/to/dojorc/to/.extend', + 'testGroupName-testCommandName': { + prop: 'config' + } + }) + ); + mockFs.readFileSync.onCall(2).returns( JSON.stringify({ extends: './path/to/dojorc/to/.extend1', 'testGroupName-testCommandName': { @@ -237,7 +259,7 @@ registerSuite('Configuration Helper', { } }) ); - mockFs.readFileSync.onCall(2).returns( + mockFs.readFileSync.onCall(3).returns( JSON.stringify({ 'testGroupName-testCommandName': { prop: 'config-extended-2', @@ -252,7 +274,7 @@ registerSuite('Configuration Helper', { }) ); const config = configurationHelper.sandbox('testGroupName', 'testCommandName').get(); - assert.equal(mockFs.readFileSync.callCount, 3); + assert.equal(mockFs.readFileSync.callCount, 4); assert.equal(mockFs.readFileSync.firstCall.args[0], dojoRcPath); assert.deepEqual(config, { prop: 'config', @@ -283,7 +305,9 @@ registerSuite('Configuration Helper', { mockFs.existsSync.returns(true); mockFs.readFileSync = sinon.stub(); mockFs.readFileSync.onCall(0).returns('{}'); - mockFs.readFileSync.onCall(1).returns('{]'); + mockFs.readFileSync.onCall(1).returns('{}'); + mockFs.readFileSync.onCall(2).returns('{]'); + mockFs.readFileSync.onCall(3).returns('{]'); const test = () => configurationHelper.sandbox('testGroupName', 'testCommandName').get(); assert.throws( @@ -293,7 +317,7 @@ registerSuite('Configuration Helper', { `Could not parse the package.json file to get config: SyntaxError: Unexpected token ] in JSON at position 1` ) ); - assert.equal(mockFs.readFileSync.callCount, 2, 'both package.json and .dojorc should be read'); + assert.equal(mockFs.readFileSync.callCount, 4, 'both package.json and .dojorc should be read'); } } }, @@ -453,10 +477,13 @@ registerSuite('Configuration Helper', { mockPkgDir = mockModule.getMock('pkg-dir'); mockPkgDir.ctor.sync = sandbox.stub().returns(null); mockFs = mockModule.getMock('fs'); + mockFs.existsSync = sandbox.stub().returns(true); mockFs.readFileSync = sandbox.stub(); - mockFs.readFileSync.onFirstCall().returns('{}'); - mockFs.readFileSync.onSecondCall(1).returns('{ "dojo": {} }'); + mockFs.readFileSync.onCall(0).returns('{}'); + mockFs.readFileSync.onCall(1).returns('{}'); + mockFs.readFileSync.onCall(2).returns('{ "dojo": {} }'); + mockFs.readFileSync.onCall(3).returns('{ "dojo": {} }'); mockPath = mockModule.getMock('path'); mockPath.join = sandbox.stub(); consoleWarnStub = sandbox.stub(console, 'warn'); @@ -471,8 +498,8 @@ registerSuite('Configuration Helper', { tests: { 'should warn about having multi configs'() { mockcheckForMultiConfig(); - assert.isTrue(mockFs.existsSync.calledTwice); - assert.isTrue(mockFs.readFileSync.calledTwice); + assert.equal(mockFs.existsSync.callCount, 2); + assert.equal(mockFs.readFileSync.callCount, 4); assert.equal(consoleWarnStub.callCount, 1); assert.equal( consoleWarnStub.firstCall.args[0], diff --git a/tests/unit/index.ts b/tests/unit/index.ts index 2f56aff6..b9b0295b 100644 --- a/tests/unit/index.ts +++ b/tests/unit/index.ts @@ -77,7 +77,7 @@ describe('cli main module', () => { it('should catch runtime errors', () => { describe('runtime error inner', () => { - const errMessage = 'ugh - oh noes'; + const errMessage = '\n ugh - oh noes'; const expectedError = new Error(errMessage); beforeEach(() => { From 7a3077badad0a75cab17fd3592808acf3cd3b550 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 24 Sep 2019 14:51:57 +0100 Subject: [PATCH 3/3] Pin dep, fix unit test --- package.json | 2 +- tests/unit/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 855373d9..8b08a77d 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "typings.json" ], "dependencies": { - "@speedy/json-extends": "^1.2.0", + "@speedy/json-extends": "1.2.0", "ajv": "6.6.2", "chalk": "2.4.1", "configstore": "3.1.2", diff --git a/tests/unit/index.ts b/tests/unit/index.ts index b9b0295b..81a26fbb 100644 --- a/tests/unit/index.ts +++ b/tests/unit/index.ts @@ -104,7 +104,7 @@ describe('cli main module', () => { assert.throw(mockUpdate.default, Error, errMessage); assert.equal( (console.log as sinon.SinonStub).args[0][0], - `Commands are not available: Error: ${errMessage}` + `Commands are not available:\n ${errMessage}` ); }); });