From ed79b4409560da2dce25abe348c758cd4ba88995 Mon Sep 17 00:00:00 2001 From: Bryan Forbes Date: Tue, 10 Oct 2017 15:21:21 -0500 Subject: [PATCH] Update Grunt task * Allow grunt options to specify 'config' to point to a config file and/or section * Change node/util.getConfig to accept an optional second argument to allow passing in other command line arguments * Updated task tests Fixes #812 --- src/lib/node/util.ts | 6 +- src/tasks/intern.ts | 30 +++++-- tests/unit/tasks/intern.ts | 174 +++++++++++++++++++++++++------------ 3 files changed, 147 insertions(+), 63 deletions(-) diff --git a/src/lib/node/util.ts b/src/lib/node/util.ts index 1c742803c..52393cc9f 100644 --- a/src/lib/node/util.ts +++ b/src/lib/node/util.ts @@ -48,15 +48,15 @@ export function expandFiles(patterns?: string[] | string) { * Get the user-supplied config data, which may include command line args and a * config file. */ -export function getConfig(file?: string) { +export function getConfig(file?: string, argv: string[] = process.argv) { let args: { [key: string]: any } = {}; if (process.env['INTERN_ARGS']) { mixin(args, parseArgs(parse(process.env['INTERN_ARGS']))); } - if (process.argv.length > 2) { - mixin(args, parseArgs(process.argv.slice(2))); + if (argv.length > 2) { + mixin(args, parseArgs(argv.slice(2))); } if (file) { diff --git a/src/tasks/intern.ts b/src/tasks/intern.ts index 4c58e4138..5ff54b64f 100644 --- a/src/tasks/intern.ts +++ b/src/tasks/intern.ts @@ -1,26 +1,46 @@ import global from '@dojo/shim/global'; +import { assign } from '@dojo/core/lang'; import Node from '../lib/executors/Node'; import { Config } from '../lib/executors/Executor'; +import { getConfig } from '../lib/node/util'; interface TaskOptions extends grunt.task.ITaskOptions, Partial { [key: string]: any; } +function configure(options: TaskOptions): Promise<{ + config: Partial; + options: TaskOptions; +}> { + if (options.config) { + return getConfig(options.config, []).then(({ config }) => { + delete options.config; + + return { config, options }; + }); + } + + return Promise.resolve({ config: {}, options }); +} + export = function(grunt: IGrunt) { grunt.registerMultiTask('intern', function() { const done = this.async(); - const config = this.options({}); + const options = assign({}, this.options({})); // Force colored output for istanbul report process.env.FORCE_COLOR = 'true'; - const intern = (global.intern = new Node(config)); - intern.run().then(finish, finish); + configure(options).then(({ config, options }) => { + const intern = (global.intern = new Node()); + intern.configure(config); + intern.configure(options); + + return intern.run(); + }).then(finish, finish); function finish(error?: any) { - // Remove the global intern object when we're done; this will allow - // Intern to be run again in the same process global.intern = null; done(error); } diff --git a/tests/unit/tasks/intern.ts b/tests/unit/tasks/intern.ts index 73a1a0af2..09ff49276 100644 --- a/tests/unit/tasks/intern.ts +++ b/tests/unit/tasks/intern.ts @@ -1,28 +1,36 @@ -import { spy, stub } from 'sinon'; +import * as sinon from 'sinon'; import _gruntTask from 'src/tasks/intern'; const mockRequire = intern.getPlugin('mockRequire'); registerSuite('tasks/intern', function() { - const mockDone = stub(); + const sandbox = sinon.sandbox.create(); + + let mockDone: sinon.SinonSpy; + + function setupDone() { + return new Promise(resolve => { + mockDone = sinon.spy(() => resolve()); + }); + } const mockGrunt = { - registerMultiTask: spy(() => {}), - async: spy(() => mockDone), - options: spy(() => { - return { foo: 'bar' }; - }) + registerMultiTask: sandbox.stub(), + async: sandbox.spy(() => mockDone), + options: sandbox.stub() }; - const mockRun = stub(); + const mockRun = sandbox.stub(); + const mockConfigure = sandbox.stub(); + const mockGetConfig = sandbox.stub(); class MockNode { - config: any; run: Function; - constructor(config: any) { + configure: Function; + constructor() { executors.push(this); - this.config = config; this.run = mockRun; + this.configure = mockConfigure; } } @@ -34,7 +42,8 @@ registerSuite('tasks/intern', function() { before() { return mockRequire(require, 'src/tasks/intern', { 'src/lib/executors/Node': { default: MockNode }, - '@dojo/shim/global': { default: {} } + '@dojo/shim/global': { default: {} }, + 'src/lib/node/util': { getConfig: mockGetConfig } }).then(handle => { removeMocks = handle.remove; gruntTask = handle.module; @@ -46,15 +55,15 @@ registerSuite('tasks/intern', function() { }, beforeEach() { - mockRun.reset(); + sandbox.reset(); mockRun.resolves(); - mockDone.reset(); executors = []; }, tests: { 'task registration'() { gruntTask(mockGrunt); + assert.equal( mockGrunt.registerMultiTask.callCount, 1, @@ -67,55 +76,110 @@ registerSuite('tasks/intern', function() { ); }, - 'run task'() { - const dfd = this.async(); - gruntTask(mockGrunt); - const callback = mockGrunt.registerMultiTask.getCall(0).args[1]; - callback.call(mockGrunt); - assert.lengthOf( - executors, - 1, - '1 executor should have been created' - ); - assert.equal( - mockRun.callCount, - 1, - 'intern should have been run' - ); - assert.deepEqual(executors[0].config, { foo: 'bar' }); - setTimeout( - dfd.callback(() => { + 'run task': { + 'config'() { + mockGrunt.registerMultiTask.callsArgOn(1, mockGrunt); + mockGrunt.options.returns({ + config: '@coverage', + foo: 'bar' + }); + mockGetConfig.resolves({ + config: { + spam: 'ham' + } + }); + const done = setupDone(); + + gruntTask(mockGrunt); + + return done.then(() => { + assert.lengthOf( + executors, + 1, + '1 executor should have been created' + ); + assert.equal( + mockRun.callCount, + 1, + 'intern should have been run' + ); + assert.equal(mockGetConfig.callCount, 1); + assert.equal( + mockGetConfig.getCall(0).args[0], + '@coverage' + ); + assert.equal(mockConfigure.callCount, 2); + assert.deepEqual(mockConfigure.getCall(0).args[0], { + spam: 'ham' + }); + assert.deepEqual(mockConfigure.getCall(1).args[0], { + foo: 'bar' + }); assert.equal(mockDone.callCount, 1); // First arg is an error, so it should be undefined here assert.isUndefined(mockDone.getCall(0).args[0]); - }) - ); + }); + }, + + 'no config'() { + mockGrunt.registerMultiTask.callsArgOn(1, mockGrunt); + mockGrunt.options.returns({ + foo: 'bar' + }); + const done = setupDone(); + + gruntTask(mockGrunt); + + return done.then(() => { + assert.lengthOf( + executors, + 1, + '1 executor should have been created' + ); + assert.equal( + mockRun.callCount, + 1, + 'intern should have been run' + ); + assert.equal(mockGetConfig.callCount, 0); + assert.equal(mockConfigure.callCount, 2); + assert.deepEqual(mockConfigure.getCall(0).args[0], {}); + assert.deepEqual(mockConfigure.getCall(1).args[0], { + foo: 'bar' + }); + assert.equal(mockDone.callCount, 1); + // First arg is an error, so it should be undefined here + assert.isUndefined(mockDone.getCall(0).args[0]); + }); + } }, error() { - const dfd = this.async(); - gruntTask(mockGrunt); - const callback = mockGrunt.registerMultiTask.getCall(0).args[1]; + mockGrunt.registerMultiTask.callsArgOn(1, mockGrunt); + mockGrunt.options.returns({ + foo: 'bar' + }); const error = new Error('bad'); mockRun.rejects(error); - callback.call(mockGrunt); - assert.lengthOf( - executors, - 1, - '1 executor should have been created' - ); - assert.equal( - mockRun.callCount, - 1, - 'intern should have been run' - ); - assert.deepEqual(executors[0].config, { foo: 'bar' }); - setTimeout( - dfd.callback(() => { - assert.equal(mockDone.callCount, 1); - assert.equal(mockDone.getCall(0).args[0], error); - }) - ); + + const done = setupDone(); + + gruntTask(mockGrunt); + + return done.then(() => { + assert.lengthOf( + executors, + 1, + '1 executor should have been created' + ); + assert.equal( + mockRun.callCount, + 1, + 'intern should have been run' + ); + assert.equal(mockDone.callCount, 1); + assert.equal(mockDone.getCall(0).args[0], error); + }); } } };