Skip to content

Commit

Permalink
- Moves env and generator instatiation to _onReady so user have…
Browse files Browse the repository at this point in the history
… a chance to alter

the environment to the a specific way and expect `env` and `generator` reflects such
settings.

- Emits `ready` event right before calling `generator.run()` to allow any
last minute operation on the `generator`

- Deprecating `onEnd`, instead emits 'end' event as `generator.on('end')`

- Implement `withGenerator()` to mock generator dependencies

- accepts optional second parameter in `inDir` as a callback function.

fixes yeoman#564, yeoman#565, yeoman#573
  • Loading branch information
Brian Lai committed Jun 4, 2014
1 parent 2dda90f commit 473af98
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 28 deletions.
82 changes: 61 additions & 21 deletions lib/test/run-context.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use strict';
var path = require('path');
var _ = require('lodash');
var yeoman = require('../..');
var util = require('util');
Expand All @@ -16,26 +17,12 @@ var helpers = require('./helpers');
*/

var RunContext = module.exports = function RunContext(Generator) {
var namespace;

this._asyncHolds = 0;
this.runned = false;
this.env = yeoman();
this.args = [];
this.options = {};

if (_.isString(Generator)) {
namespace = this.env.namespace(Generator);
this.env.register(Generator);
} else {
namespace = 'gen:test';
this.env.registerStub(Generator, namespace);
}

this.generator = this.env.create(namespace);

// Mock prompt by default
this.withPrompt();
this._dependencies = [];
this.Generator = Generator;

setTimeout(this._onReady.bind(this), 0);
};
Expand Down Expand Up @@ -64,11 +51,37 @@ RunContext.prototype._holdExec = function () {
RunContext.prototype._onReady = function () {
if (this._asyncHolds !== 0 || this.runned) return;
this.runned = true;

var namespace;
this.env = yeoman();

this._dependencies.forEach(function (d) {
if (d instanceof Array) {
this.env.registerStub(d[0], d[1]);
} else {
this.env.register(d);
}
}.bind(this));

if (_.isString(this.Generator)) {
namespace = this.env.namespace(this.Generator);
this.env.register(this.Generator);
} else {
namespace = 'gen:test';
this.env.registerStub(this.Generator, namespace);
}

this.generator = this.env.create(namespace);
helpers.mockPrompt(this.generator, this._answers);

this.generator.args = this.args;
this.generator.arguments = this.args;
this.generator.options = _.extend({
'skip-install': true
}, this.options);

this.generator.once('end', this.emit.bind(this, 'end'));
this.emit('ready', this.generator);
this.generator.run();
};

Expand All @@ -79,9 +92,10 @@ RunContext.prototype._onReady = function () {
* @return {this}
*/

RunContext.prototype.inDir = function (dirPath) {
RunContext.prototype.inDir = function (dirPath, cb) {
var release = this._holdExec();
helpers.testDirectory(dirPath, release);
var callBackThenRelease = _.compose(release, (cb || _.noop).bind(this, path.resolve(dirPath)));
helpers.testDirectory(dirPath, callBackThenRelease);
return this;
};

Expand Down Expand Up @@ -114,17 +128,43 @@ RunContext.prototype.withOptions = function (options) {
*/

RunContext.prototype.withPrompt = function (answers) {
helpers.mockPrompt(this.generator, answers);
this._answers = answers;
return this;
};

/**
* Provide denpendent generators
* @param {Array} dependencies - paths to the generators dependencies
* @return {this}
* @example
* var deps = ['../../common',
* '../../controller',
* '../../main',
* [helpers.createDummyGenerator(), 'testacular:app']
* ];
* var angular = new RunContext('../../app');
* angular.withGenerator(deps);
* angular.withPrompt({
* compass: true,
* bootstrap: true
* });
* angular.onEnd(function () {
* // assert something
* });
*/

RunContext.prototype.withGenerators = function (dependencies) {
this._dependencies = dependencies || [];
return this;
};

/**
* Add a callback to be called after the generator has ran
* @deprecated `onEnd` is deprecated, use .on('end', onEndHandler) instead.
* @param {Function} callback
* @return {this}
*/

RunContext.prototype.onEnd = function (cb) {
this.generator.once('end', cb);
return this;
return this.on('end', cb);
};
69 changes: 62 additions & 7 deletions test/run-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@ describe('RunContext', function () {
});

describe('constructor', function () {
it('accept path parameter', function () {
it('accept path parameter', function (done) {
var ctx = new RunContext(path.join(__dirname, './fixtures/custom-generator-simple'));
assert(ctx.env.get('simple:app'));
ctx.on('ready', function () {
assert(ctx.env.get('simple:app'));
done();
});
});

it('accept generator constructor parameter (and assign gen:test as namespace)', function () {
assert(this.ctx.env.get('gen:test'));
it('accept generator constructor parameter (and assign gen:test as namespace)', function (done) {
this.ctx.on('ready', function () {
assert(this.ctx.env.get('gen:test'));
done();
}.bind(this));
});

it('run the generator asynchronously', function (done) {
Expand Down Expand Up @@ -61,6 +67,17 @@ describe('RunContext', function () {
it('is chainable', function () {
assert.equal(this.ctx.inDir(this.tmp), this.ctx);
});

it('accepts optional `cb` to be invoked with resolved `dir`', function (done) {
var ctx = new RunContext(this.Dummy);
var cb = sinon.spy(function () {
assert(cb.calledOnce);
assert(cb.calledOn(ctx));
assert(cb.calledWith(path.resolve(this.tmp)));
done();
}.bind(this));
ctx.inDir(this.tmp, cb);
});
});

describe('#withArguments()', function () {
Expand All @@ -87,7 +104,9 @@ describe('RunContext', function () {

describe('#withOptions()', function () {
it('provide options to the generator', function (done) {
this.ctx.withOptions({ foo: 'bar' });
this.ctx.withOptions({
foo: 'bar'
});
this.ctx.onEnd(function () {
assert.equal(this.execSpy.firstCall.thisValue.options.foo, 'bar');
done();
Expand All @@ -102,7 +121,9 @@ describe('RunContext', function () {
});

it('allow skip-install to be overriden', function (done) {
this.ctx.withOptions({ 'skip-install': false });
this.ctx.withOptions({
'skip-install': false
});
this.ctx.onEnd(function () {
assert.equal(this.execSpy.firstCall.thisValue.options['skip-install'], false);
done();
Expand Down Expand Up @@ -140,14 +161,48 @@ describe('RunContext', function () {
done();
});
};
this.ctx.withPrompt({ yeoman: 'yes please' });
this.ctx.withPrompt({
yeoman: 'yes please'
});
});

it('is chainable', function () {
assert.equal(this.ctx.withPrompt({}), this.ctx);
});
});

describe('#withGenerators()', function () {
it('should register paths', function (done) {
this.ctx.withGenerators([
path.join(__dirname, './fixtures/custom-generator-simple')
]).on('ready', function () {
assert(this.ctx.env.get('simple:app'));
done();
}.bind(this));
});

it('should register mocked generator', function (done) {
this.ctx.withGenerators([
[helpers.createDummyGenerator(), 'dummy:gen']
]).on('ready', function () {
assert(this.ctx.env.get('dummy:gen'));
done();
}.bind(this));
});

it('should only take the generators passed in with the last call', function (done) {
this.ctx.withGenerators([
path.join(__dirname, './fixtures/custom-generator-simple')
]).withGenerators([
[helpers.createDummyGenerator(), 'dummy:gen']
]).on('ready', function () {
assert(this.ctx.env.get('dummy:gen'));
assert.equal(this.ctx.env.get('simple:app'), null);
done();
}.bind(this));
});
});

describe('#onEnd()', function () {
it('is called after the generator ran', function (done) {
assert(this.execSpy.notCalled);
Expand Down

0 comments on commit 473af98

Please sign in to comment.