diff --git a/lib/test/helpers.js b/lib/test/helpers.js index 7e57dc3e..16f3f384 100644 --- a/lib/test/helpers.js +++ b/lib/test/helpers.js @@ -11,41 +11,56 @@ var generators = require('../..'); // Mocha helpers var helpers = module.exports; -helpers.stubs = []; +helpers.decorated = []; -// cleanup the test dir, and cd into it -helpers.before = function before(dir) { - if (!dir) { - throw new Error('Missing directory'); - } - - dir = path.resolve(dir); +/** + * Create a function that will clean up the test directory, + * cd into it, and create a dummy gruntfile inside. Intended for use + * as a callback for the mocha `before` hook. + * + * @param {String} dir - path to the test directory + * @returns {Function} mocha callback + */ +helpers.setUpTestDirectory = function before(dir) { return function (done) { - rimraf(dir, function (err) { - if (err) { - return done(err); - } - mkdirp.sync(dir); - process.chdir(dir); + helpers.testDirectory(dir, function () { helpers.gruntfile({ dummy: true }, done); }); }; }; -// Wrap a method with custom functionality. -// -// - context - (object) context to find the original method -// - method - (string) name of the method to wrap -// - replacement - (function) executes before the original method -// - options - (opt) (object) config settings +/** + * Create a function that will clean up the test directory, + * cd into it, and create a dummy gruntfile inside. Intended for use + * as a callback for the mocha `before` hook. + * + * @deprecated + * @param {String} dir - path to the test directory + * @returns {Function} mocha callback + */ + +helpers.before = function (dir) { + console.log('before is deprecated. Use setUpTestDirectory instead'); + return helpers.setUpTestDirectory(dir); +}; + +/** + * Wrap a method with custom functionality. + * + * @param {Object} context - context to find the original method + * @param {String} method - name of the method to wrap + * @param {Function} replacement - executes before the original method + * @param {Object} options - config settings + */ + helpers.decorate = function decorate(context, method, replacement, options) { options = options || {}; replacement = replacement || function () {}; var naturalMethod = context[method]; - helpers.stubs.push({ + helpers.decorated.push({ context: context, method: method, naturalMethod: naturalMethod @@ -62,38 +77,41 @@ helpers.decorate = function decorate(context, method, replacement, options) { }; }; -// Override a method with custom functionality. -// -// - context - (object) context to find the original method -// - method - (string) name of the method to wrap -// - replacement - (function) executes before the original method +/** + * Override a method with custom functionality. + * @param {Object} context - context to find the original method + * @param {String} method - name of the method to wrap + * @param {Function} replacement - executes before the original method + */ helpers.stub = function stub(context, method, replacement) { helpers.decorate(context, method, replacement, { stub: true }); }; -// Restore all stubs with original behavior. +/** + * Restore the original behavior of all decorated and stubbed methods + */ helpers.restore = function restore() { - helpers.stubs.forEach(function (stub) { - stub.context[stub.method] = stub.naturalMethod; + helpers.decorated.forEach(function (dec) { + dec.context[dec.method] = dec.naturalMethod; }); }; -// Generates a new Gruntfile.js in the current working directory based on -// `options` hash passed in. Same as other helpers, meant to be use as a mocha -// handler. -// -// - options - Grunt configuration -// - done - callback to call on completion -// -// Example -// -// before(helpers.gruntfile({ -// foo: { -// bar: '' -// } -// })); -// -// Returns a function suitable to use with mocha hooks. +/** + * + * Generates a new Gruntfile.js in the current working directory based on + * options hash passed in. + * + * @param {Object} options - Grunt configuration + * @param {Function} done - callback to call on completion + * @example + * before(helpers.gruntfile({ + * foo: { + * bar: '' + * } + * })); + * + */ + helpers.gruntfile = function (options, done) { var config = 'grunt.initConfig(' + JSON.stringify(options, null, 2) + ');'; config = config.split('\n').map(function (line) { @@ -109,37 +127,167 @@ helpers.gruntfile = function (options, done) { fs.writeFile('Gruntfile.js', out.join('\n'), done); }; -helpers.assertFile = function (file, reg) { - var here = fs.existsSync(file); - assert.ok(here, file + ', no such file or directory'); +/** + * Assert that a file exists + * @param {String} file - path to a file + * @example + * assertFile('templates/user.hbs'); + * + * @also + * + * Assert that each of an array of files exists + * @param {Array} pairs - an array of paths to files + * @example + * assertFile(['templates/user.hbs', 'templates/user/edit.hbs']); + * + * @also + * + * Assert that a file's content matches a regex + * @deprecated + * @param {String} file - path to a file + * @param {Regex} reg - regex that will be used to search the file + * @example + * assertFileContent('models/user.js', /App\.User = DS\.Model\.extend/); + */ - if (!reg) { - return assert.ok(here); +helpers.assertFile = function () { + var args = _.toArray(arguments); + if (_.last(args) instanceof RegExp) { // DEPRECATED CASE + var depMsg = 'assertFile(String, RegExp) DEPRECATED; use '; + depMsg += 'assertFileContent(String, RegExp) instead.'; + console.log(depMsg); + helpers.assertFileContent(args[0], args[1]); + } else { + args = _.isString(args[0]) ? args : args[0]; + args.forEach(function (file) { + var here = fs.existsSync(file); + assert.ok(here, file + ', no such file or directory'); + }); } +}; - var body = fs.readFileSync(file, 'utf8'); - assert.ok(reg.test(body), file + ' did not match \'' + reg + '\'.'); +/** + * Assert that a file doesn't exist + * @param {String} file - path to a file + * @example + * assertNoFile('templates/user.hbs'); + * + * @also + * + * Assert that each of an array of files doesn't exist + * @param {Array} pairs - an array of paths to files + * @example + * assertNoFile(['templates/user.hbs', 'templates/user/edit.hbs']); + */ + +helpers.assertNoFile = function () { + var args = _.toArray(arguments); + args = _.isString(args[0]) ? args : args[0]; + args.forEach(function (file) { + var here = fs.existsSync(file); + assert.ok(!here, file + ' exists'); + }); }; -// Check all files present in the array are existing. -// If the item is an array first item is the file path, 2nd a regexp -// to check file content with -// -// helpers.assertFiles(['foo.js', 'bar.js', ['baz.js', /function baz/]]); -// +/** + * Assert that each of an array of files exists. If an item is an array with + * the first element a filepath and the second element a regex, check to see + * that the file content matches the regex + * @deprecated + * @param {Array} pairs - an array of paths to files or file/regex subarrays + * @example + * assertFile(['templates/user.hbs', 'templates/user/edit.hbs']); + * @example + * assertFiles(['foo.js', 'bar.js', ['baz.js', /function baz/]]); + */ + helpers.assertFiles = function (files) { + var depMsg = 'assertFiles deprecated. Use '; + depMsg += 'assertFile([String, String, ...]) or '; + depMsg += 'assertFile([[String, RegExp], [String, RegExp]...]) instead.'; + console.log(depMsg); files.forEach(function (item) { var file = item; var rx; if (item instanceof Array) { file = item[0]; rx = item[1]; + helpers.assertFileContent(file, rx); + } else { + helpers.assertFile(file); } + }); +}; + +/** + * Assert that a file's content matches a regex + * @param {String} file - path to a file + * @param {Regex} reg - regex that will be used to search the file + * @example + * assertFileContent('models/user.js', /App\.User = DS\.Model\.extend/); + * + * @also + * + * Assert that each file in an array of file-regex pairs matches its corresponding regex + * @param {Array} pairs - an array of arrays, where each subarray is a [String, RegExp] pair + * @example + * var arg = [ + * [ 'models/user.js', /App\.User \ DS\.Model\.extend/ ], + * [ 'controllers/user.js', /App\.UserController = Ember\.ObjectController\.extend/ ] + * ] + * assertFileContent(arg); + */ + +helpers.assertFileContent = function () { + var args = _.toArray(arguments); + var pairs = _.isString(args[0]) ? [args] : args[0]; + pairs.forEach(function (pair) { + var file = pair[0]; + var regex = pair[1]; + helpers.assertFile(file); + var body = fs.readFileSync(file, 'utf8'); + assert.ok(regex.test(body), file + ' did not match \'' + regex + '\'.'); + }); +}; + +/** + * Assert that a file's content does not match a regex + * @param {String} file - path to a file + * @param {Regex} reg - regex that will be used to search the file + * @example + * assertNoFileContent('models/user.js', /App\.User = DS\.Model\.extend/); + * + * @also + * + * Assert that each file in an array of file-regex pairs does not match its corresponding regex + * @param {Array} pairs - an array of arrays, where each subarray is a [String, RegExp] pair + * var arg = [ + * [ 'models/user.js', /App\.User \ DS\.Model\.extend/ ], + * [ 'controllers/user.js', /App\.UserController = Ember\.ObjectController\.extend/ ] + * ] + * assertNoFileContent(arg); + */ - helpers.assertFile(file, rx); +helpers.assertNoFileContent = function (file, reg) { + var args = _.toArray(arguments); + var pairs = _.isString(args[0]) ? [args] : args[0]; + pairs.forEach(function (pair) { + var file = pair[0]; + var regex = pair[1]; + helpers.assertFile(file); + var body = fs.readFileSync(file, 'utf8'); + assert.ok(!regex.test(body), file + ' did not match \'' + regex + '\'.'); }); }; +/** + * Assert that two strings are equal after standardization of newlines + * @param {String} value - a string + * @param {String} expected - the expected value of the string + * @example + * assertTextEqual('I have a yellow cat', 'I have a yellow cat'); + */ + helpers.assertTextEqual = function (value, expected) { function eol(str) { return str.replace(/\r\n/g, '\n'); @@ -149,7 +297,7 @@ helpers.assertTextEqual = function (value, expected) { }; /** - * Assert an Object implement an interface + * Assert an Object implements an interface * @param {Object} obj - subject implementing the façade * @param {Object|Array} methods - a façace, hash or array of keys to be implemented */ @@ -164,9 +312,17 @@ helpers.assertImplement = function (obj, methods) { assert.ok(pass); }; -// Clean-up the test directory and ce into it. -// Call given callback when you're there. -// +/** + * Clean-up the test directory and cd into it. + * Call given callback after entering the test directory. + * @param {String} dir - path to the test directory + * @param {Function} cb - callback executed after setting working directory to dir + * @example + * testDirectory(path.join(__dirname, './temp'), function () { + * fs.writeFileSync('testfile', 'Roses are red.'); + * ); + */ + helpers.testDirectory = function (dir, cb) { if (!dir) { throw new Error('Missing directory'); @@ -184,11 +340,16 @@ helpers.testDirectory = function (dir, cb) { }); }; -// Will answer to the questions for the furnished generator -// -// Example: -// mockPrompt(angular, {'bootstrap': 'Y', 'compassBoostrap': 'Y'}); -// +/** + * Answer prompt questions for the passed-in generator + * @param {Generator} generator - a Yeoman generator + * @param {Object} answers - an object where keys are the + * generators prompt names and values are the answers to + * the prompt questions + * @example + * mockPrompt(angular, {'bootstrap': 'Y', 'compassBoostrap': 'Y'}); + */ + helpers.mockPrompt = function (generator, answers) { var origPrompt = generator.prompt; generator.prompt = function (prompts, done) { @@ -197,9 +358,10 @@ helpers.mockPrompt = function (generator, answers) { generator.origPrompt = origPrompt; }; -// -// Create a simple, dummy generator -// +/** + * Create a simple, dummy generator + */ + helpers.createDummyGenerator = function () { return generators.Base.extend({ test: function () { @@ -208,18 +370,25 @@ helpers.createDummyGenerator = function () { }); }; -// Create a generator, using the given dependencies and controller arguments -// Dependecies can be path (autodiscovery) or an array [, ] -// -// Example: -// var deps = ['../../app', -// '../../common', -// '../../controller', -// '../../main', -// [createDummyGenerator(), 'testacular:app'] -// ]; -// var angular = createGenerator('angular:app', deps); -// +/** + * Create a generator, using the given dependencies and controller arguments + * Dependecies can be path (autodiscovery) or an array [, ] + * + * @param {String} name - the name of the generator + * @param {Array} dependencies - paths to the generators dependencies + * @param {Array|String} args - arguments to the generator; + * if String, will be split on spaces to create an Array + * @param {Object} options - configuration for the generator + * @example + * var deps = ['../../app', + * '../../common', + * '../../controller', + * '../../main', + * [createDummyGenerator(), 'testacular:app'] + * ]; + * var angular = createGenerator('angular:app', deps); + */ + helpers.createGenerator = function (name, dependencies, args, options) { var env = generators(); dependencies.forEach(function (d) { diff --git a/test/fixtures/testFile b/test/fixtures/testFile new file mode 100644 index 00000000..c6d77f90 --- /dev/null +++ b/test/fixtures/testFile @@ -0,0 +1 @@ +Roses are red. diff --git a/test/fixtures/testFile2 b/test/fixtures/testFile2 new file mode 100644 index 00000000..3f60df61 --- /dev/null +++ b/test/fixtures/testFile2 @@ -0,0 +1 @@ +Violets are blue. diff --git a/test/helpers.js b/test/helpers.js index 3fa151bc..aed1e68e 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -1,6 +1,8 @@ /*global it, describe, before, beforeEach */ var util = require('util'); +var path = require('path'); +var fs = require('fs'); var assert = require('assert'); var yeoman = require('..'); var helpers = yeoman.test; @@ -9,12 +11,12 @@ describe('yeoman.test', function () { 'use strict'; beforeEach(function () { + process.chdir(path.join(__dirname, './fixtures')); var self = this; this.StubGenerator = function (args, options) { self.args = args; self.options = options; }; - util.inherits(this.StubGenerator, yeoman.Base); }); @@ -40,4 +42,143 @@ describe('yeoman.test', function () { assert.equal(this.options.ui, 'tdd'); }); }); + + describe('#assertFile', function () { + + it('accept a file that exists', function () { + assert.doesNotThrow(helpers.assertFile.bind(helpers, 'testFile')); + }); + + it('accept an array of files all of which exist', function () { + assert.doesNotThrow(helpers.assertFile.bind(helpers, ['testFile', 'testFile2'])); + }); + + it('reject a file that does not exist', function () { + assert.throws(helpers.assertFile.bind(helpers, 'etherealTestFile')); + }); + + it('reject multiple files one of which does not exist', function () { + assert.throws(helpers.assertFile.bind(helpers, ['testFile', 'intangibleTestFile'])); + }); + + // DEPRECATED + + it('accept a file with content that matches reg', function () { + assert.doesNotThrow(helpers.assertFile.bind(helpers, 'testFile', /Roses are red/)); + }); + + it('reject a file with content does not match reg', function () { + assert.throws(helpers.assertFile.bind(helpers, 'testFile', /Roses are blue/)); + }); + + }); + + describe('#assertNoFile', function () { + + it('accept a file that does not exist', function () { + assert.doesNotThrow(helpers.assertNoFile.bind(helpers, 'etherealTestFile')); + }); + + it('accept an array of files all of which do not exist', function () { + assert.doesNotThrow( + helpers.assertNoFile.bind(helpers, ['etherealTestFile', 'intangibleTestFile'])); + }); + + it('reject a file that exists', function () { + assert.throws(helpers.assertNoFile.bind(helpers, 'testFile')); + }); + + it('reject an array of files one of which exists', function () { + assert.throws( + helpers.assertNoFile.bind(helpers, ['testFile', 'etherealTestFile'])); + }); + + }); + + describe('#assertFiles', function () { // DEPRECATED + + it('accept an array of files all of which exist', function () { + assert.doesNotThrow( + helpers.assertFiles.bind(helpers, ['testFile', 'testFile2'])); + }); + + it('reject an array of multiple files one of which exists', function () { + assert.throws( + helpers.assertFiles.bind(helpers, ['testFile', 'etherealTestFile'])); + }); + + it('accept an array of file/regex pairs when each file\'s content matches the corresponding regex', function () { + var arg = [ + ['testFile', /Roses are red/], + ['testFile2', /Violets are blue/] + ]; + assert.doesNotThrow(helpers.assertFiles.bind(helpers, arg)); + }); + + it('reject an array of file/regex pairs when one file\'s content does not matches the corresponding regex', function () { + var arg = [ + ['testFile', /Roses are red/], + ['testFile2', /Violets are orange/] + ]; + assert.throws(helpers.assertFiles.bind(helpers, arg)); + }); + + }); + + describe('#assertFileContent', function () { + + it('accept a file and regex when the file content matches the regex', function () { + assert.doesNotThrow(helpers.assertFileContent.bind(helpers, 'testFile', /Roses are red/)); + }); + + it('reject a file and regex when the file content does not match the regex', function () { + assert.throws(helpers.assertFileContent.bind(helpers, 'testFile', /Roses are blue/)); + }); + + it('accept an array of file/regex pairs when each file\'s content matches the corresponding regex', function () { + var arg = [ + ['testFile', /Roses are red/], + ['testFile2', /Violets are blue/] + ]; + assert.doesNotThrow(helpers.assertFileContent.bind(helpers, arg)); + }); + + it('reject an array of file/regex pairs when one file\'s content does not matches the corresponding regex', function () { + var arg = [ + ['testFile', /Roses are red/], + ['testFile2', /Violets are orange/] + ]; + assert.throws(helpers.assertFileContent.bind(helpers, arg)); + }); + + }); + + describe('#assertNoFileContent', function () { + + it('accept a file and regex when the file content does not match the regex', function () { + assert.doesNotThrow(helpers.assertNoFileContent.bind(helpers, 'testFile', /Roses are blue/)); + }); + + it('reject a file and regex when the file content matches the regex', function () { + assert.throws(helpers.assertNoFileContent.bind(helpers, 'testFile', /Roses are red/)); + }); + + it('accept an array of file/regex pairs when each file\'s content does not match its corresponding regex', function () { + var arg = [ + ['testFile', /Roses are green/], + ['testFile2', /Violets are orange/] + ]; + assert.doesNotThrow(helpers.assertNoFileContent.bind(helpers, arg)); + }); + + it('reject an array of file/regex pairs when one file\'s content does matches its corresponding regex', function () { + var arg = [ + ['testFile', /Roses are red/], + ['testFile2', /Violets are orange/] + ]; + assert.throws(helpers.assertNoFileContent.bind(helpers, arg)); + }); + + }); + });