UNPKG

generator-modx-package

Version:
599 lines (494 loc) 20.1 kB
/*global it, describe, before, beforeEach */ var fs = require('fs'); var path = require('path'); var util = require('util'); var assert = require('assert'); var sinon = require('sinon'); var generators = require('..'); var helpers = generators.test; var events = require('events'); var Base = generators.Base; var Environment = require('../lib/env'); var Store = require('../lib/env/store'); describe('Environment', function () { beforeEach(function () { this.env = new Environment(); process.chdir(__dirname); }); afterEach(function () { this.env.removeAllListeners(); }); it('is an instance of EventEmitter', function () { assert.ok(new Environment() instanceof events.EventEmitter); }); describe('constructor', function () { it('take arguments option', function () { var args = ['foo']; assert.equal(new Environment(args).arguments, args); }); it('take arguments parameter option as string', function () { var args = 'foo bar'; assert.deepEqual(new Environment(args).arguments, args.split(' ')); }); it('take options parameter', function () { var opts = { foo : 'bar' }; assert.equal(new Environment(null, opts).options, opts); }); }); describe('#help', function () { beforeEach(function () { this.env .register('./fixtures/custom-generator-simple') .register('./fixtures/custom-generator-extend'); this.expected = fs.readFileSync(path.join(__dirname, 'fixtures/help.txt'), 'utf8').trim(); // lazy "update the help fixtures because something changed" statement // fs.writeFileSync(path.join(__dirname, 'fixtures/help.txt'), env.help().trim()); }); it('output the general help', function () { helpers.assertTextEqual(this.env.help().trim(), this.expected); }); it('output the help with a custom bin name', function () { this.expected = this.expected.replace('Usage: init', 'Usage: gg'); helpers.assertTextEqual(this.env.help('gg').trim(), this.expected); }); }); describe('#create', function () { beforeEach(function () { this.Generator = helpers.createDummyGenerator(); this.env.registerStub(this.Generator, 'stub'); this.env.registerStub(this.Generator, 'stub:foo:bar'); }); it('instantiate a generator', function () { assert.ok(this.env.create('stub') instanceof this.Generator); }); it('pass options.arguments', function () { var args = ['foo', 'bar']; var generator = this.env.create('stub', { arguments: args }); assert.equal(generator.arguments, args); }); it('pass options.arguments as string', function () { var args = 'foo bar'; var generator = this.env.create('stub', { arguments: args }); assert.deepEqual(generator.arguments, args.split(' ')); }); it('pass options.args (as `arguments` alias)', function () { var args = ['foo', 'bar']; var generator = this.env.create('stub', { args: args }); assert.equal(generator.arguments, args); }); it('prefer options.arguments over options.args', function () { var arguments = ['yo', 'unicorn']; var args = ['foo', 'bar']; var generator = this.env.create('stub', { arguments: arguments, args: args }); assert.equal(generator.arguments, arguments); assert.notEqual(generator.arguments, args); }); it('default arguments to `env.arguments`', function () { var args = ['foo', 'bar']; this.env.arguments = args; var generator = this.env.create('stub'); assert.notEqual(generator.arguments, args, 'not passed by reference'); assert.deepEqual(generator.arguments, args); }); it('pass options.options', function () { var opts = { 'foo' : 'bar' }; var generator = this.env.create('stub', { options: opts }); assert.equal(generator.options, opts); }); it('default options to `env.options` content', function () { this.env.options = { 'foo' : 'bar' }; assert.equal(this.env.create('stub').options.foo, 'bar'); }); it('throws if Generator is not registered', function () { assert.throws(this.env.create.bind(this.end, 'i:do:not:exist')); }); it('add a name property on the options', function () { assert.equal(this.env.create('stub').options.name, 'stub'); assert.equal(this.env.create('stub:foo:bar').options.name, 'bar'); }); it('add the env as property on the options', function () { assert.equal(this.env.create('stub').options.env, this.env); }); it('add the Generator resolved path on the options', function() { assert.equal(this.env.create('stub').options.resolved, this.env.get('stub').resolved); }); it('adds the namespace on the options', function() { assert.equal(this.env.create('stub').options.namespace, 'stub'); }); it('adds the namespace as called on the options', function() { assert.equal(this.env.create('stub:foo:bar').options.namespace, 'stub:foo:bar'); }); describe('NamedBase', function () { beforeEach(function () { this.env.register('./fixtures/custom-generator-extend', 'scaffold'); this.NamedGenerator = this.env.get('scaffold'); }); it('does not raise an error when the help option is provided but the required name parameter is not', function () { assert.doesNotThrow(this.env.create.bind(this.env, 'scaffold', { options: { help: true } })); }); it('calls the named base generator with the help option and provides the required name parameter', function () { var generator = this.env.create('scaffold', { args: [ 'foo' ], options: { help: true } }); assert.equal(generator.options.help, true); assert.equal(generator.name, 'foo'); }); it('throws an error when the required name parameter is not provided', function () { assert.throws(this.env.create.bind(this.env, 'scaffold')); }); }); }); describe('#run', function () { beforeEach(function () { var self = this; this.stub = function () { self.args = arguments; Base.apply(this, arguments); }; this.runMethod = sinon.spy(Base.prototype, 'run'); util.inherits(this.stub, Base); this.env.registerStub(this.stub, 'stub:run'); }); afterEach(function() { this.runMethod.restore(); }); it('runs a registered generator', function (done) { this.env.run(['stub:run'], function() { assert.ok(this.runMethod.calledOnce); done(); }.bind(this)); }); it('pass args and options to the runned generator', function (done) { var args = ['stub:run', 'module']; var options = {}; this.env.run(args, options, function () { assert.ok(this.runMethod.calledOnce); assert.equal(this.args[0], 'module'); assert.equal(this.args[1], options); done(); }.bind(this)); }); it('without options, it default to env.options', function (done) { var args = ['stub:run', 'foo']; this.env.options = { some: 'stuff' }; this.env.run(args, function () { assert.ok(this.runMethod.calledOnce); assert.equal(this.args[0], 'foo'); assert.equal(this.args[1], this.env.options); done(); }.bind(this)); }); it('without args, it default to env.arguments', function (done) { this.env.arguments = ['stub:run', 'my-args']; this.env.options = { some: 'stuff' }; this.env.run(function () { assert.ok(this.runMethod.calledOnce); assert.equal(this.args[0], 'my-args'); assert.equal(this.args[1], this.env.options); done(); }.bind(this)); }); it('can take string as args', function (done) { var args = 'stub:run module'; this.env.run(args, function () { assert.ok(this.runMethod.calledOnce); assert.equal(this.args[0], 'module'); done(); }.bind(this)); }); it('can take no arguments', function () { this.env.arguments = ['stub:run']; this.env.run(); assert.ok(this.runMethod.calledOnce); }); it('launch error if generator is not found', function (done) { this.env.on('error', function (err) { assert.ok(err.message.indexOf('some:unknown:generator') >= 0) done(); }); this.env.run('some:unknown:generator'); }); it('returns the generator', function () { assert.ok(this.env.run('stub:run') instanceof Base); }); }); describe('#register', function () { beforeEach(function () { this.simplePath = './fixtures/custom-generator-simple'; this.extendPath = './fixtures/custom-generator-extend'; assert.equal(this.env.namespaces().length, 0, 'env should be empty'); this.env .register(this.simplePath, 'fixtures:custom-generator-simple') .register(this.extendPath, 'scaffold'); }); it('store registered generators', function () { assert.equal(this.env.namespaces().length, 2); }); it('determine registered Generator namespace and resolved path', function () { var simple = this.env.get('fixtures:custom-generator-simple'); assert.ok(typeof simple === 'function'); assert.ok(simple.namespace, 'fixtures:custom-generator-simple'); assert.ok(simple.resolved, path.resolve(this.simplePath)); var extend = this.env.get('scaffold'); assert.ok(typeof extend === 'function'); assert.ok(extend.namespace, 'scaffold'); assert.ok(extend.resolved, path.resolve(this.extendPath)); }); it('throw when String is not passed as first parameter', function () { assert.throws(function () { this.env.register(function () {}, 'blop'); }); assert.throws(function () { this.env.register([], 'blop'); }); assert.throws(function () { this.env.register(false, 'blop'); }); }); }); describe('#appendLookup', function () { it('have default lookups', function () { assert.equal(this.env.lookups.slice(-1)[0], 'lib/generators'); }); it('adds new filepath to the lookup\'s paths', function () { this.env.appendLookup('support/scaffold'); assert.equal(this.env.lookups.slice(-1)[0], 'support/scaffold'); }); it('must receive a filepath', function () { assert.throws(this.env.appendLookup.bind(this.env)); }); }); describe('#appendPath', function () { it('have default paths', function () { assert.equal(this.env.paths[0], '.'); }); it('adds new filepath to the load paths', function () { this.env.appendPath('support/scaffold'); assert.equal(this.env.paths.slice(-1)[0], 'support/scaffold'); }); it('must receive a filepath', function () { assert.throws(this.env.appendPath.bind(this.env)); }); }); describe('#namespace', function () { beforeEach(function () { this.env .register('./fixtures/custom-generator-simple') .register('./fixtures/custom-generator-extend') .register('./fixtures/custom-generator-extend', 'support:scaffold'); }); it('get the list of namespaces', function () { assert.deepEqual(this.env.namespaces(), ['simple', 'extend:support:scaffold', 'support:scaffold']); }); }); describe('#get', function () { beforeEach(function () { this.generator = require('./fixtures/mocha-generator'); this.env .register('./fixtures/mocha-generator', 'fixtures:mocha-generator') .register('./fixtures/mocha-generator', 'mocha:generator'); }); it('get a specific generator', function () { assert.equal(this.env.get('mocha:generator'), this.generator); assert.equal(this.env.get('fixtures:mocha-generator'), this.generator); }); it('walks recursively the namespace to get the closest match', function () { assert.equal(this.env.get('mocha:generator:too:many'), this.generator); }); it('returns undefined if namespace is not found', function () { assert.equal(this.env.get('not:there'), undefined); assert.equal(this.env.get(), undefined); }); }); describe('#registerStub', function () { beforeEach(function () { this.simpleDummy = sinon.spy(); this.completeDummy = function () {}; util.inherits(this.completeDummy, Base); this.env .registerStub(this.simpleDummy, 'dummy:simple') .registerStub(this.completeDummy, 'dummy:complete'); }); it('register a function under a namespace', function () { assert.equal(this.completeDummy, this.env.get('dummy:complete')); }); it('extend simple function with Base', function () { assert.ok(this.env.get('dummy:simple').super_ === Base); this.env.run('dummy:simple'); assert.ok(this.simpleDummy.calledOnce); }); it('throws if invalid generator', function () { assert.throws(this.env.registerStub.bind(this.env, [], 'dummy'), /stub\sfunction/); }); it('throws if invalid namespace', function () { assert.throws(this.env.registerStub.bind(this.env, this.simpleDummy), /namespace/); }); }); describe('#error', function () { it('delegate error handling to the listener', function (done) { var error = new Error('foo bar'); this.env.on('error', function (err) { assert.equal(error, err); done(); }); this.env.error(error); }); it('throws error if no listener is set', function () { assert.throws(this.env.error.bind(this.env, new Error())); }); it('returns the error', function () { var error = new Error('foo bar'); this.env.on('error', function () {}); assert.equal(this.env.error(error), error); }); }); // Events // ------ // A series of events are emitted during the generation process. Both on // the global `generators` handler and each individual generators // involved in the process. describe('Events', function () { before(function () { var Generator = this.Generator = function () { generators.Base.apply(this, arguments); }; Generator.namespace = 'angular:all'; util.inherits(Generator, generators.Base); Generator.prototype.createSomething = function () {}; Generator.prototype.createSomethingElse = function () {}; }); it('emits the series of event on a specific generator', function (done) { var angular = new this.Generator([], { env: generators(), resolved: __filename }); var lifecycle = ['start', 'createSomething', 'createSomethingElse', 'end']; function assertEvent(e) { return function() { assert.equal(e, lifecycle.shift()); if (e === 'end') { done(); } }; } angular // Start event, emitted right before "running" the generator. .on('start', assertEvent('start')) // End event, emitted after the generation process, when every generator method and hooks are executed .on('end', assertEvent('end')) // Emitted when a conflict is detected, right after the prompt happens. // .on('conflict', assertEvent('conflict')) // Emitted on every prompt, both for conflict state and generators one. // .on('prompt', assertEvent('prompt')) // Emitted right before a hook is invoked // .on('hook', assertEvent('hook')) // Emitted on each generator method .on('createSomething', assertEvent('createSomething')) .on('createSomethingElse', assertEvent('createSomethingElse')); angular.run(); }); it('hoists up the series of event from specific generator to the generators handler', function (done) { var lifecycle = [ 'generators:start', 'angular:all:start', 'angular:all:createSomething', 'angular:all:createSomethingElse', 'angular:all:end', 'generators:end' ]; function assertEvent(ev) { return function () { assert.equal(ev, lifecycle.shift()); if (!lifecycle.length) { done(); } }; } generators() .registerStub(this.Generator, 'angular:all') // Series of events proxied from the resolved generator .on('generators:start', assertEvent('generators:start')) .on('generators:end', assertEvent('generators:end')) // .on('conflict', assertEvent('generators:conflict')) // .on('prompt', assertEvent('generators:prompt')) // .on('hook', assertEvent('generators:start')) // Emitted for each generator method invoked, prefix by the generator namespace .on('angular:all:createSomething', assertEvent('angular:all:createSomething')) .on('angular:all:createSomethingElse', assertEvent('angular:all:createSomethingElse')) // Additionally, for more specific events, same prefixing happens on // start, end, conflict, prompt and hook. .on('angular:all:start', assertEvent('angular:all:start')) .on('angular:all:end', assertEvent('angular:all:end')) .on('angular:all:conflict', assertEvent('angular:all:conflict')) .on('angular:all:prompt', assertEvent('angular:all:prompt')) // actual run .run('angular:all myapp'); }); it('only call the end event once (bug #402)', function (done) { function GeneratorOnce() { generators.Base.apply(this, arguments); this.sourceRoot(path.join(__dirname, 'fixtures')); this.destinationRoot(path.join(__dirname, 'temp')); } util.inherits(GeneratorOnce, generators.Base); GeneratorOnce.prototype.createDuplicate = function () { this.copy('foo-copy.js'); this.copy('foo-copy.js'); }; var generatorOnce = new GeneratorOnce([], { env: generators(), resolved: __filename }); var isFirstEndEvent = true; generatorOnce.on('end', function () { assert.ok(isFirstEndEvent); if (isFirstEndEvent) { done(); } isFirstEndEvent = false; }); generatorOnce.run(); }); }); describe('Store', function() { beforeEach(function() { this.store = new Store(); }); describe('#add / #get', function() { beforeEach(function() { this.module = function () {}; this.modulePath = path.join(__dirname, "fixtures/dummy-package"); }); describe('storing as module', function() { beforeEach(function() { this.store.add('foo:module', this.module); this.outcome = this.store.get('foo:module'); }); it('store and return the module', function() { assert.equal(this.outcome, this.module); }); it('assign meta data to the module', function() { assert.equal(this.outcome.namespace, 'foo:module'); }); it('assign dummy resolved value (can\'t determine the path of an instantiated)', function() { assert.ok(this.outcome.resolved.length > 0); }); }); describe('storing as module path', function() { beforeEach(function() { this.store.add('foo:path', this.modulePath); this.outcome = this.store.get('foo:path'); }); it('store and returns the required module', function() { assert.notEqual(this.outcome, this.modulePath); assert.equal(this.outcome.yeoman, 'Yo!'); }); it('assign meta data to the module', function() { assert.equal(this.outcome.resolved, this.modulePath); assert.equal(this.outcome.namespace, 'foo:path'); }); }); }); describe('#namespaces', function() { beforeEach(function() { this.store.add('foo', {}); this.store.add('lab', {}); }); it('return stored module namespaces', function() { assert.deepEqual(this.store.namespaces(), [ 'foo', 'lab' ]); }); }); }); });