diff --git a/.gitignore b/.gitignore index f9b1763a8..230257f1f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,8 @@ node_modules bower_components -test/temp +/generators/* +/test/* demo .idea .DS_Store -release.txt -test/fixtures/bower.json -test/fixtures/package.json -generators \ No newline at end of file +release.txt \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 7aeb1de2c..0f6dcdf23 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,29 +1,70 @@ 'use strict'; -var fs = require('fs'); -var path = require('path'); -var Promise = require('bluebird'); -var gulp = require('gulp'); -var babel = require('gulp-babel'); -var del = require('del'); -var runSequence = require('run-sequence'); +const fs = require('fs'); +const path = require('path'); +const exec = require('child_process').exec; +const _ = require('lodash'); +const Promise = require('bluebird'); +const gulp = require('gulp'); +const gutil = require('gulp-util'); +const babel = require('gulp-babel'); +const gulpMocha = require('gulp-mocha'); +const plumber = require('gulp-plumber'); +const gulpIf = require('gulp-if'); +const del = require('del'); +const lazypipe = require('lazypipe'); +const runSequence = require('run-sequence'); +const merge = require('merge-stream'); +const shell = require('shelljs'); + +var watching = false; + +const mocha = lazypipe() + .pipe(gulpMocha, { + reporter: 'spec', + timeout: 120000, + slow: 500, + globals: { + should: require('should') + }, + require: [ + './mocha.conf' + ] + }); + +const transpile = lazypipe() + .pipe(babel); gulp.task('clean', () => { return del(['generators/**/*']); }); gulp.task('babel', () => { - return gulp.src(['src/**/*.js']) - .pipe(babel()) + let generators = gulp.src(['src/generators/**/*.js']) + .pipe(gulpIf(watching, plumber())) + .pipe(transpile()) .pipe(gulp.dest('generators')); + + let test = gulp.src(['src/test/**/*.js']) + .pipe(gulpIf(watching, plumber())) + .pipe(transpile()) + .pipe(gulp.dest('test')); + + return merge(generators, test); }); gulp.task('watch', () => { + watching = true; return gulp.watch('src/**/*.js', ['babel']); }); gulp.task('copy', () => { - return gulp.src(['src/**/*', '!src/**/*.js']) + let nonJsGen = gulp.src(['src/generators/**/*', '!src/generators/**/*.js'], {dot: true}) .pipe(gulp.dest('generators')); + + let nonJsTest = gulp.src(['src/test/**/*', '!src/test/**/*.js'], {dot: true}) + .pipe(gulp.dest('test')); + + return merge(nonJsGen, nonJsTest); }); gulp.task('build', cb => { @@ -81,3 +122,49 @@ gulp.task('updateFixtures:test', () => { gulp.task('updateFixtures:deps', () => { return updateFixtures('deps'); }); + +function execAsync(cmd, opt) { + return new Promise((resolve, reject) => { + exec(cmd, opt, (err, stdout, stderr) => { + if(err) { + console.log(`stderr: ${stderr}`); + return reject(err); + } + + return resolve(stdout); + }) + }); +} + +gulp.task('installFixtures', function() { + gutil.log('installing npm & bower dependencies for generated app'); + let progress = setInterval(() => { + process.stdout.write('.'); + }, 1 * 1000); + shell.cd('test/fixtures'); + + return Promise.all([ + execAsync('npm install --quiet', {cwd: '../fixtures'}), + execAsync('bower install', {cwd: '../fixtures'}) + ]).then(() => { + process.stdout.write('\n'); + if(!process.env.SAUCE_USERNAME) { + gutil.log('running npm run-script update-webdriver'); + return execAsync('npm run-script update-webdriver').then(() => { + clearInterval(progress); + process.stdout.write('\n'); + shell.cd('../../'); + }); + } else { + clearInterval(progress); + process.stdout.write('\n'); + shell.cd('../../'); + return Promise.resolve(); + } + }); +}); + +gulp.task('test', () => { + return gulp.src(['test/pre.test.js', 'test/*.test.js']) + .pipe(mocha()); +}); diff --git a/mocha.conf.js b/mocha.conf.js new file mode 100644 index 000000000..4a47ecbec --- /dev/null +++ b/mocha.conf.js @@ -0,0 +1,7 @@ +'use strict'; + +global.DEBUG = !!process.env.DEBUG; + +var fs = require('fs'); +var Promise = require('bluebird'); +Promise.promisifyAll(fs); diff --git a/package.json b/package.json index 7cc2759bf..6d407e4c0 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "url": "git://github.com/angular-fullstack/generator-angular-fullstack.git" }, "scripts": { - "test": "gulp build && grunt test", + "test": "gulp updateFixtures:test && gulp installFixtures && gulp build && gulp test", "prepublish": "gulp build" }, "dependencies": { @@ -67,14 +67,23 @@ "grunt-mocha-test": "^0.12.7", "grunt-release": "^0.13.0", "gulp": "^3.9.1", + "gulp-if": "^2.0.0", + "gulp-mocha": "^2.2.0", + "gulp-plumber": "^1.1.0", + "gulp-util": "^3.0.7", "jit-grunt": "~0.10.0", + "jscs": "^3.0.3", + "lazypipe": "^1.0.1", + "merge-stream": "^1.0.0", + "minimatch": "^3.0.0", "mocha": "^2.2.5", "q": "^1.0.1", "recursive-readdir": "^2.0.0", "run-sequence": "^1.1.5", "shelljs": "^0.6.0", + "should": "^8.3.1", "yeoman-assert": "^2.0.0", - "yeoman-test": "^1.1.0" + "yeoman-test": "^1.3.0" }, "engines": { "node": "^5.10.1", diff --git a/src/app/USAGE b/src/generators/app/USAGE similarity index 100% rename from src/app/USAGE rename to src/generators/app/USAGE diff --git a/src/app/index.js b/src/generators/app/index.js similarity index 100% rename from src/app/index.js rename to src/generators/app/index.js diff --git a/src/controller/index.js b/src/generators/controller/index.js similarity index 100% rename from src/controller/index.js rename to src/generators/controller/index.js diff --git a/src/decorator/index.js b/src/generators/decorator/index.js similarity index 100% rename from src/decorator/index.js rename to src/generators/decorator/index.js diff --git a/src/directive/index.js b/src/generators/directive/index.js similarity index 100% rename from src/directive/index.js rename to src/generators/directive/index.js diff --git a/src/endpoint/index.js b/src/generators/endpoint/index.js similarity index 98% rename from src/endpoint/index.js rename to src/generators/endpoint/index.js index 675e61c07..4c05c8e97 100644 --- a/src/endpoint/index.js +++ b/src/generators/endpoint/index.js @@ -22,7 +22,7 @@ export class Generator extends Base { }); this.option('endpointDirectory', { - desc: 'Parent directory for enpoints', + desc: 'Parent directory for endpoints', type: String }); } diff --git a/src/factory/index.js b/src/generators/factory/index.js similarity index 100% rename from src/factory/index.js rename to src/generators/factory/index.js diff --git a/src/filter/index.js b/src/generators/filter/index.js similarity index 100% rename from src/filter/index.js rename to src/generators/filter/index.js diff --git a/src/generator-base.js b/src/generators/generator-base.js similarity index 100% rename from src/generator-base.js rename to src/generators/generator-base.js diff --git a/src/heroku/USAGE b/src/generators/heroku/USAGE similarity index 100% rename from src/heroku/USAGE rename to src/generators/heroku/USAGE diff --git a/src/heroku/index.js b/src/generators/heroku/index.js similarity index 100% rename from src/heroku/index.js rename to src/generators/heroku/index.js diff --git a/src/heroku/templates/Procfile b/src/generators/heroku/templates/Procfile similarity index 100% rename from src/heroku/templates/Procfile rename to src/generators/heroku/templates/Procfile diff --git a/src/insight-init.js b/src/generators/insight-init.js similarity index 100% rename from src/insight-init.js rename to src/generators/insight-init.js diff --git a/src/openshift/USAGE b/src/generators/openshift/USAGE similarity index 100% rename from src/openshift/USAGE rename to src/generators/openshift/USAGE diff --git a/src/openshift/index.js b/src/generators/openshift/index.js similarity index 100% rename from src/openshift/index.js rename to src/generators/openshift/index.js diff --git a/src/openshift/templates/hot_deploy b/src/generators/openshift/templates/hot_deploy similarity index 100% rename from src/openshift/templates/hot_deploy rename to src/generators/openshift/templates/hot_deploy diff --git a/src/provider/index.js b/src/generators/provider/index.js similarity index 100% rename from src/provider/index.js rename to src/generators/provider/index.js diff --git a/src/route/index.js b/src/generators/route/index.js similarity index 100% rename from src/route/index.js rename to src/generators/route/index.js diff --git a/src/service/index.js b/src/generators/service/index.js similarity index 100% rename from src/service/index.js rename to src/generators/service/index.js diff --git a/src/util.js b/src/generators/util.js similarity index 100% rename from src/util.js rename to src/generators/util.js diff --git a/src/test/endpoint.test.js b/src/test/endpoint.test.js new file mode 100644 index 000000000..16cb76c38 --- /dev/null +++ b/src/test/endpoint.test.js @@ -0,0 +1,253 @@ +'use strict'; +import path from 'path'; +import fs from 'fs'; +import _ from 'lodash'; +import Promise from 'bluebird'; +import helpers from 'yeoman-test'; +import assert from 'yeoman-assert'; +import minimatch from 'minimatch'; +import Checker from 'jscs'; +const jscs = new Checker(); +jscs.registerDefaultRules(); +import * as getExpectedFiles from './get-expected-files'; +import { + copyAsync, + runCmd, + assertOnlyFiles, + readJSON, + runGen +} from './test-helpers'; + +const TEST_DIR = __dirname; + +const defaultOptions = { + buildtool: 'grunt', + script: 'js', + transpiler: 'babel', + markup: 'html', + stylesheet: 'sass', + router: 'uirouter', + testing: 'mocha', + chai: 'expect', + bootstrap: true, + uibootstrap: true, + odms: ['mongoose'], + auth: true, + oauth: [], + socketio: true +}; + +function runEndpointGen(name, opt={}) { + let prompts = opt.prompts || {}; + let options = opt.options || {}; + let config = opt.config; + + return new Promise((resolve, reject) => { + let dir; + let gen = helpers + .run(require.resolve('../generators/endpoint')) + .inTmpDir(function(_dir) { + // this will create a new temporary directory for each new generator run + var done = this.async(); + if(DEBUG) console.log(`TEMP DIR: ${_dir}`); + dir = _dir; + + // symlink our dependency directories + return Promise.all([ + fs.mkdirAsync(dir + '/client').then(() => { + return fs.symlinkAsync(__dirname + '/fixtures/bower_components', dir + '/client/bower_components'); + }), + fs.symlinkAsync(__dirname + '/fixtures/node_modules', dir + '/node_modules') + ]).then(done); + }) + .withOptions(options) + .withArguments([name]) + .withPrompts(prompts); + + if(config) { + gen + .withLocalConfig(config); + } + + gen + .on('error', reject) + .on('end', () => resolve(dir)); + }); +} + +let jshintCmd = path.join(TEST_DIR, '/fixtures/node_modules/.bin/jshint'); +function testFile(command, _path) { + _path = path.normalize(_path); + return fs.accessAsync(_path, fs.R_OK).then(() => { + return runCmd(`${command} ${_path}`); + }); +} + +function jshintDir(dir, name, folder) { + if(!folder) folder = name; + let endpointDir = path.join(dir, 'server/api', folder); + + let regFiles = fs.readdirAsync(endpointDir) + .then(files => files.filter(file => minimatch(file, '**/!(*.spec|*.mock|*.integration).js', {dot: true}))) + .map(file => testFile(jshintCmd, path.join('./server/api/', folder, file))); + + let specFiles = fs.readdirAsync(endpointDir) + .then(files => files.filter(file => minimatch(file, '**/+(*.spec|*.mock|*.integration).js', {dot: true}))) + .map(file => testFile(`${jshintCmd} --config server/.jshintrc-spec`, path.join('./server/api/', folder, file))); + + return Promise.all([regFiles, specFiles]); +} +function jscsDir(dir, name, folder) { + if(!folder) folder = name; + let endpointDir = path.join(dir, 'server/api', folder); + + return fs.readdirAsync(endpointDir).then(files => { + return Promise.map(files, file => { + return fs.readFileAsync(path.join('server/api', folder, file), 'utf8').then(data => { + let results = jscs.checkString(data) + let errors = results.getErrorList(); + if(errors.length === 0) { + return Promise.resolve(); + } else { + errors.forEach(error => { + var colorizeOutput = true; + console.log(results.explainError(error, colorizeOutput) + '\n'); + }); + return Promise.reject(); + } + }); + }); + }); +} + +var config; +var genDir; + +before(function() { + return Promise.all([ + runGen(defaultOptions).then(_dir => { + genDir = _dir; + + return fs.readFileAsync(path.join(genDir, '.jscsrc'), 'utf8').then(data => { + jscs.configure(JSON.parse(data)); + }); + }), + readJSON(path.join(TEST_DIR, 'fixtures/.yo-rc.json')).then(_config => { + _config['generator-angular-fullstack'].insertRoutes = false; + _config['generator-angular-fullstack'].pluralizeRoutes = false; + _config['generator-angular-fullstack'].insertSockets = false; + _config['generator-angular-fullstack'].insertModels = false; + config = _config; + }) + ]); +}); + +describe('angular-fullstack:endpoint', function() { + describe(`with a generated endpont 'foo'`, function() { + var dir; + beforeEach(function() { + return runEndpointGen('foo', {config: config['generator-angular-fullstack']}).then(_dir => { + dir = _dir; + + return Promise.all([ + copyAsync(path.join(genDir, '/server/.jshintrc'), './server/.jshintrc'), + copyAsync(path.join(genDir, '/server/.jshintrc-spec'), './server/.jshintrc-spec'), + copyAsync(path.join(genDir, '/.jscsrc'), './.jscsrc') + ]); + }); + }); + + it('should generate the expected files', function() { + assert.file(getExpectedFiles.endpoint('foo')); + }); + + it('should pass jscs', function() { + return jscsDir(dir, 'foo').should.be.fulfilled(); + }); + + it('should pass lint', function() { + return jshintDir(dir, 'foo').should.be.fulfilled(); + }); + }); + + describe('with a generated capitalized endpont', function() { + var dir; + beforeEach(function() { + return runEndpointGen('Foo', {config: config['generator-angular-fullstack']}).then(_dir => { + dir = _dir; + + return Promise.all([ + copyAsync(path.join(genDir, '/server/.jshintrc'), './server/.jshintrc'), + copyAsync(path.join(genDir, '/server/.jshintrc-spec'), './server/.jshintrc-spec'), + copyAsync(path.join(genDir, '/.jscsrc'), './.jscsrc') + ]); + }); + }); + + it('should generate the expected files', function() { + assert.file(getExpectedFiles.endpoint('Foo')); + }); + + it('should pass jscs', function() { + return jscsDir(dir, 'Foo').should.be.fulfilled(); + }); + + it('should pass lint', function() { + return jshintDir(dir, 'Foo').should.be.fulfilled(); + }); + }); + + describe('with a generated path name endpont', function() { + var dir; + beforeEach(function() { + return runEndpointGen('foo/bar', {config: config['generator-angular-fullstack']}).then(_dir => { + dir = _dir; + + return Promise.all([ + copyAsync(path.join(genDir, '/server/.jshintrc'), './server/.jshintrc'), + copyAsync(path.join(genDir, '/server/.jshintrc-spec'), './server/.jshintrc-spec'), + copyAsync(path.join(genDir, '/.jscsrc'), './.jscsrc') + ]); + }); + }); + + it('should generate the expected files', function() { + assert.file(getExpectedFiles.endpoint('bar', 'foo/bar')); + }); + + it('should pass jscs', function() { + return jscsDir(dir, 'foo', 'foo/bar').should.be.fulfilled(); + }); + + it('should pass lint', function() { + return jshintDir(dir, 'foo', 'foo/bar').should.be.fulfilled(); + }); + }); + + describe('with a generated snake-case endpoint', function() { + var dir; + beforeEach(function() { + return runEndpointGen('foo-bar', {config: config['generator-angular-fullstack']}).then(_dir => { + dir = _dir; + + return Promise.all([ + copyAsync(path.join(genDir, '/server/.jshintrc'), './server/.jshintrc'), + copyAsync(path.join(genDir, '/server/.jshintrc-spec'), './server/.jshintrc-spec'), + copyAsync(path.join(genDir, '/.jscsrc'), './.jscsrc') + ]); + }); + }); + + it('should generate the expected files', function() { + assert.file(getExpectedFiles.endpoint('foo-bar')); + }); + + it('should pass jscs', function() { + return jscsDir(dir, 'foo-bar').should.be.fulfilled(); + }); + + it('should pass lint', function() { + return jshintDir(dir, 'foo-bar').should.be.fulfilled(); + }); + }); +}); \ No newline at end of file diff --git a/src/test/fixtures/.bowerrc b/src/test/fixtures/.bowerrc new file mode 100644 index 000000000..69fad3580 --- /dev/null +++ b/src/test/fixtures/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "bower_components" +} diff --git a/src/test/fixtures/.yo-rc.json b/src/test/fixtures/.yo-rc.json new file mode 100644 index 000000000..3f652f692 --- /dev/null +++ b/src/test/fixtures/.yo-rc.json @@ -0,0 +1,69 @@ +{ + "generator-angular-fullstack": { + "endpointDirectory": "server/api/", + "insertRoutes": true, + "registerRoutesFile": "server/routes.js", + "routesNeedle": "// Insert routes below", + "routesBase": "/api/", + "pluralizeRoutes": true, + "insertSockets": true, + "registerSocketsFile": "server/config/socketio.js", + "socketsNeedle": "// Insert sockets below", + "insertModels": true, + "registerModelsFile": "server/sqldb/index.js", + "modelsNeedle": "// Insert models below", + "filters": { + "js": true, + "babel": true, + "flow": false, + "html": true, + "sass": true, + "uirouter": true, + "bootstrap": true, + "uibootstrap": true, + "socketio": true, + "auth": true, + "models": true, + "mongooseModels": true, + "mongoose": true, + "grunt": true, + "mocha": true, + "jasmine": false, + "expect": true + } + }, + "generator-ng-component": { + "routeDirectory": "client/app/", + "directiveDirectory": "client/app/", + "componentDirectory": "app/components/", + "filterDirectory": "client/app/", + "serviceDirectory": "client/app/", + "basePath": "client", + "moduleName": "", + "modulePrompt": true, + "filters": [ + "uirouter", + "mocha", + "expect", + "should", + "uirouter", + "es6" + ], + "extensions": [ + "babel", + "js", + "html", + "scss" + ], + "directiveSimpleTemplates": "", + "directiveComplexTemplates": "", + "filterTemplates": "", + "serviceTemplates": "", + "factoryTemplates": "", + "controllerTemplates": "", + "componentTemplates": "", + "decoratorTemplates": "", + "providerTemplates": "", + "routeTemplates": "" + } +} diff --git a/src/test/get-expected-files.js b/src/test/get-expected-files.js new file mode 100644 index 000000000..c3ac9a338 --- /dev/null +++ b/src/test/get-expected-files.js @@ -0,0 +1,238 @@ +/** + * Generate an array of files to expect from a set of options + * + * @param {Object} options - generator options + * @return {Array} - array of files + * + */ +export function app(options) { + var mapping = { + stylesheet: { + sass: 'scss', + stylus: 'styl', + less: 'less', + css: 'css' + }, + markup: { + jade: 'jade', + html: 'html' + }, + script: { + js: 'js', + ts: 'ts' + } + }, + files = []; + + /** + * Generate an array of OAuth files based on type + * + * @param {String} type - type of oauth + * @return {Array} - array of files + * + */ + var oauthFiles = function(type) { + return [ + 'server/auth/' + type + '/index.js', + 'server/auth/' + type + '/passport.js', + ]; + }; + + + var script = mapping.script[options.transpiler === 'ts' ? 'ts' : 'js'], + markup = mapping.markup[options.markup], + stylesheet = mapping.stylesheet[options.stylesheet], + models = options.models ? options.models : options.odms[0]; + + /* Core Files */ + files = files.concat([ + 'client/.htaccess', + 'client/favicon.ico', + 'client/robots.txt', + 'client/index.html', + 'client/app/app.' + script, + 'client/app/app.' + stylesheet, + 'client/app/main/main.' + script, + 'client/app/main/main.' + markup, + 'client/app/main/main.' + stylesheet, + 'client/app/main/main.controller.' + script, + 'client/app/main/main.controller.spec.' + script, + 'client/assets/images/yeoman.png', + 'client/components/footer/footer.' + stylesheet, + 'client/components/footer/footer.' + markup, + 'client/components/footer/footer.directive.' + script, + 'client/components/navbar/navbar.' + markup, + 'client/components/navbar/navbar.controller.' + script, + 'client/components/navbar/navbar.directive.' + script, + 'client/components/util/util.module.' + script, + 'client/components/util/util.service.' + script, + 'server/.jshintrc', + 'server/.jshintrc-spec', + 'server/app.js', + 'server/index.js', + 'server/routes.js', + 'server/api/thing/index.js', + 'server/api/thing/index.spec.js', + 'server/api/thing/thing.controller.js', + 'server/api/thing/thing.integration.js', + 'server/components/errors/index.js', + 'server/config/local.env.js', + 'server/config/local.env.sample.js', + 'server/config/express.js', + 'server/config/environment/index.js', + 'server/config/environment/development.js', + 'server/config/environment/production.js', + 'server/config/environment/test.js', + 'server/config/environment/shared.js', + 'server/views/404.' + markup, + 'e2e/main/main.po.js', + 'e2e/main/main.spec.js', + 'e2e/components/navbar/navbar.po.js', + '.babelrc', + '.bowerrc', + '.buildignore', + '.editorconfig', + '.gitattributes', + '.gitignore', + '.travis.yml', + '.jscsrc', + '.yo-rc.json', + 'Gruntfile.js', + 'package.json', + 'bower.json', + 'karma.conf.js', + 'mocha.conf.js', + 'protractor.conf.js', + 'README.md' + ]); + + /* TypeScript */ + if (options.transpiler === 'ts') { + files = files.concat([ + 'tsconfig.client.test.json', + 'tsconfig.client.json', + 'tsd.json', + 'tsd_test.json', + 'client/tslint.json' + ]); + } else { + files = files.concat([ + 'client/.jshintrc' + ]); + } + + /* Ui-Router */ + if (options.router === 'uirouter') { + files = files.concat([ + 'client/components/ui-router/ui-router.mock.' + script + ]); + } + + /* Ui-Bootstrap */ + if (options.uibootstrap) { + files = files.concat([ + 'client/components/modal/modal.' + markup, + 'client/components/modal/modal.' + stylesheet, + 'client/components/modal/modal.service.' + script + ]); + } + + /* Models - Mongoose or Sequelize */ + if (models) { + files = files.concat([ + 'server/api/thing/thing.model.js', + 'server/api/thing/thing.events.js', + 'server/config/seed.js' + ]); + } + + /* Sequelize */ + if (options.odms.indexOf('sequelize') !== -1) { + files = files.concat([ + 'server/sqldb/index.js' + ]); + } + + /* Authentication */ + if (options.auth) { + files = files.concat([ + 'client/app/account/account.' + script, + 'client/app/account/login/login.' + markup, + 'client/app/account/login/login.controller.' + script, + 'client/app/account/settings/settings.' + markup, + 'client/app/account/settings/settings.controller.' + script, + 'client/app/account/signup/signup.' + markup, + 'client/app/account/signup/signup.controller.' + script, + 'client/app/admin/admin.' + markup, + 'client/app/admin/admin.' + stylesheet, + 'client/app/admin/admin.module.' + script, + 'client/app/admin/admin.router.' + script, + 'client/app/admin/admin.controller.' + script, + 'client/components/auth/auth.module.' + script, + 'client/components/auth/auth.service.' + script, + 'client/components/auth/interceptor.service.' + script, + 'client/components/auth/router.decorator.' + script, + 'client/components/auth/user.service.' + script, + 'client/components/mongoose-error/mongoose-error.directive.' + script, + 'server/api/user/index.js', + 'server/api/user/index.spec.js', + 'server/api/user/user.controller.js', + 'server/api/user/user.integration.js', + 'server/api/user/user.model.js', + 'server/api/user/user.model.spec.js', + 'server/api/user/user.events.js', + 'server/auth/index.js', + 'server/auth/auth.service.js', + 'server/auth/local/index.js', + 'server/auth/local/passport.js', + 'e2e/account/login/login.po.js', + 'e2e/account/login/login.spec.js', + 'e2e/account/logout/logout.spec.js', + 'e2e/account/signup/signup.po.js', + 'e2e/account/signup/signup.spec.js' + ]); + } + + if (options.oauth && options.oauth.length) { + /* OAuth (see oauthFiles function above) */ + options.oauth.forEach(function(type, i) { + files = files.concat(oauthFiles(type.replace('Auth', ''))); + }); + + + files = files.concat([ + 'client/components/oauth-buttons/oauth-buttons.' + stylesheet, + 'client/components/oauth-buttons/oauth-buttons.' + markup, + 'client/components/oauth-buttons/oauth-buttons.controller.' + script, + 'client/components/oauth-buttons/oauth-buttons.controller.spec.' + script, + 'client/components/oauth-buttons/oauth-buttons.directive.' + script, + 'client/components/oauth-buttons/oauth-buttons.directive.spec.' + script, + 'e2e/components/oauth-buttons/oauth-buttons.po.js' + ]); + } + + /* Socket.IO */ + if (options.socketio) { + files = files.concat([ + 'client/components/socket/socket.service.' + script, + 'client/components/socket/socket.mock.' + script, + 'server/api/thing/thing.socket.js', + 'server/config/socketio.js' + ]); + } + + return files; +} + +export function endpoint(name, path) { + if(!path) path = name; + return [ + `server/api/${path}/index.js`, + `server/api/${path}/index.spec.js`, + `server/api/${path}/${name}.controller.js`, + `server/api/${path}/${name}.events.js`, + `server/api/${path}/${name}.integration.js`, + `server/api/${path}/${name}.model.js`, + `server/api/${path}/${name}.socket.js` + ]; +} diff --git a/src/test/main.test.js b/src/test/main.test.js new file mode 100644 index 000000000..849b221b5 --- /dev/null +++ b/src/test/main.test.js @@ -0,0 +1,430 @@ +'use strict'; +import path from 'path'; +import fs from 'fs'; +import _ from 'lodash'; +import Promise from 'bluebird'; +import helpers from 'yeoman-test'; +import assert from 'yeoman-assert'; +import * as getExpectedFiles from './get-expected-files'; +import { + copyAsync, + runCmd, + assertOnlyFiles, + readJSON, + runGen +} from './test-helpers'; + +const defaultOptions = { + buildtool: 'grunt', + transpiler: 'babel', + markup: 'html', + stylesheet: 'sass', + router: 'uirouter', + testing: 'mocha', + chai: 'expect', + bootstrap: true, + uibootstrap: true, + odms: ['mongoose'], + auth: true, + oauth: [], + socketio: true +}; +const TEST_DIR = __dirname; + +function runEndpointGen(name, opt={}) { + let prompts = opt.prompts || {}; + let options = opt.options || {}; + let config = opt.config; + + return new Promise((resolve, reject) => { + let gen = helpers + .run(require.resolve('../generators/endpoint'), {tmpdir: false}) + .withOptions(options) + .withArguments([name]) + .withPrompts(prompts); + + if(config) { + gen + .withLocalConfig(config); + } + + gen + .on('error', reject) + .on('end', () => resolve()) + }); +} + +describe('angular-fullstack:app', function() { + describe('default settings', function() { + var dir; + + beforeEach(function() { + return runGen(defaultOptions).then(_dir => { + dir = _dir; + }); + }); + + it('generates the proper files', function() { + const expectedFiles = getExpectedFiles.app(defaultOptions); + assert.file(expectedFiles); + return assertOnlyFiles(expectedFiles, path.normalize(dir)).should.be.fulfilled(); + }); + + it('passes JSCS', function() { + return runCmd('grunt jscs').should.be.fulfilled(); + }); + + it('passes JSHint', function() { + return runCmd('grunt jshint').should.be.fulfilled(); + }); + + it('passes client tests', function() { + return runCmd('grunt test:client').should.be.fulfilled(); + }); + + it('passes server tests', function() { + return runCmd('grunt test:server').should.be.fulfilled(); + }); + + describe('with a generated endpoint', function() { + beforeEach(function() { + return readJSON(path.join(dir, '.yo-rc.json')).then(config => { + return runEndpointGen('foo', {config: config['generator-angular-fullstack']}); + }); + }); + + it('should run server tests successfully', function() { + return runCmd('grunt test:server').should.be.fulfilled(); + }); + }); + + describe('with a generated capitalized endpoint', function() { + beforeEach(function() { + return readJSON(path.join(dir, '.yo-rc.json')).then(config => { + return runEndpointGen('Foo', {config: config['generator-angular-fullstack']}); + }); + }); + + it('should run server tests successfully', function() { + return runCmd('grunt test:server').should.be.fulfilled(); + }); + }); + + describe('with a generated path name endpoint', function() { + beforeEach(function() { + return readJSON(path.join(dir, '.yo-rc.json')).then(config => { + return runEndpointGen('foo/bar', {config: config['generator-angular-fullstack']}); + }); + }); + + it('should run server tests successfully', function() { + return runCmd('grunt test:server').should.be.fulfilled(); + }); + }); + + describe('with a generated snake-case endpoint', function() { + beforeEach(function() { + return readJSON(path.join(dir, '.yo-rc.json')).then(config => { + return runEndpointGen('foo-bar', {config: config['generator-angular-fullstack']}); + }); + }); + + it('should run server tests successfully', function() { + return runCmd('grunt test:server').should.be.fulfilled(); + }); + }); + + if(!process.env.SKIP_E2E) { + it('should run e2e tests successfully', function() { + this.retries(2); + return runCmd('grunt test:e2e').should.be.fulfilled(); + }); + + it('should run e2e tests successfully for production app', function() { + this.retries(2); + return runCmd('grunt test:e2e:prod').should.be.fulfilled(); + }); + } + }); + + describe('default settings using existing `.yo-rc.json`', function() { + var dir; + var jscsResult; + var lintResult; + var clientTestResult; + var serverTestResult; + + before(function() { + return runGen(null, { + copyConfigFile: true, + options: { + skipInstall: true, + skipConfig: true + } + }).then(_dir => { + dir = _dir; + jscsResult = runCmd('grunt jscs'); + lintResult = runCmd('grunt jshint'); + clientTestResult = runCmd('grunt test:client'); + serverTestResult = runCmd('grunt test:server'); + }); + }); + + it('generates the proper files', function() { + const expectedFiles = getExpectedFiles.app(defaultOptions); + assert.file(expectedFiles); + return assertOnlyFiles(expectedFiles, path.normalize(dir)).should.be.fulfilled(); + }); + + it('passes JSCS', function() { + return jscsResult.should.be.fulfilled(); + }); + + it('passes JSHint', function() { + return lintResult.should.be.fulfilled(); + }); + + it('passes client tests', function() { + return clientTestResult.should.be.fulfilled(); + }); + + it('passes server tests', function() { + return serverTestResult.should.be.fulfilled(); + }); + }); + + describe('with TypeScript, Jade, Jasmine, LESS, & OAuth', function() { + var dir; + var jscsResult; + var lintResult; + var clientTestResult; + var serverTestResult; + var testOptions = { + buildtool: 'grunt', + transpiler: 'ts', + markup: 'jade', + stylesheet: 'less', + router: 'uirouter', + testing: 'jasmine', + odms: ['mongoose'], + auth: true, + oauth: ['twitterAuth', 'facebookAuth', 'googleAuth'], + socketio: true, + bootstrap: true, + uibootstrap: true + }; + + before(function() { + return runGen(testOptions).then(_dir => { + dir = _dir; + jscsResult = runCmd('grunt jscs'); + lintResult = runCmd('grunt tslint'); + clientTestResult = runCmd('grunt test:client'); + serverTestResult = runCmd('grunt test:server'); + }); + }); + + it('should generate the proper files', function() { + const expectedFiles = getExpectedFiles.app(testOptions); + assert.file(expectedFiles); + return assertOnlyFiles(expectedFiles, path.normalize(dir)).should.be.fulfilled(); + }); + + it('passes JSCS', function() { + return jscsResult.should.be.fulfilled(); + }); + + it('passes lint', function() { + return lintResult.should.be.fulfilled(); + }); + + it('should run client tests successfully', function() { + return clientTestResult.should.be.fulfilled(); + }); + + it('should run server tests successfully', function() { + return serverTestResult.should.be.fulfilled(); + }); + + describe('with a generated endpoint', function() { + beforeEach(function() { + return readJSON(path.join(dir, '.yo-rc.json')).then(config => { + return runEndpointGen('foo', {config: config['generator-angular-fullstack']}); + }); + }); + + it('should run server tests successfully', function() { + return runCmd('grunt test:server').should.be.fulfilled(); + }); + }); + + if(!process.env.SKIP_E2E) { + it('should run e2e tests successfully', function() { + this.retries(2); + return runCmd('grunt test:e2e').should.be.fulfilled(); + }); + + it('should run e2e tests successfully for production app', function() { + this.retries(2); + return runCmd('grunt test:e2e:prod').should.be.fulfilled(); + }); + } + }); + + describe('with sequelize models, auth', function() { + var dir; + var jscsResult; + var lintResult; + var clientTestResult; + var serverTestResult; + var testOptions = { + buildtool: 'grunt', + transpiler: 'babel', + markup: 'jade', + stylesheet: 'css', + router: 'uirouter', + testing: 'jasmine', + odms: ['sequelize'], + auth: true, + oauth: ['twitterAuth', 'facebookAuth', 'googleAuth'], + socketio: true, + bootstrap: true, + uibootstrap: true + }; + + beforeEach(function() { + return runGen(testOptions).then(_dir => { + dir = _dir; + jscsResult = runCmd('grunt jscs'); + lintResult = runCmd('grunt jshint'); + clientTestResult = runCmd('grunt test:client'); + serverTestResult = runCmd('grunt test:server'); + }); + }); + + it('should generate the proper files', function() { + const expectedFiles = getExpectedFiles.app(testOptions); + assert.file(expectedFiles); + return assertOnlyFiles(expectedFiles, path.normalize(dir)).should.be.fulfilled(); + }); + + it('passes JSCS', function() { + return jscsResult.should.be.fulfilled(); + }); + + it('passes lint', function() { + return lintResult.should.be.fulfilled(); + }); + + it('should run client tests successfully', function() { + return clientTestResult.should.be.fulfilled(); + }); + + it('should run server tests successfully', function() { + return serverTestResult.should.be.fulfilled(); + }); + + describe('with a generated endpoint', function() { + beforeEach(function() { + return readJSON(path.join(dir, '.yo-rc.json')).then(config => { + return runEndpointGen('foo', {config: config['generator-angular-fullstack']}); + }); + }); + + it('should run server tests successfully', function() { + return runCmd('grunt test:server').should.be.fulfilled(); + }); + }); + + if(!process.env.SKIP_E2E) { + it('should run e2e tests successfully', function() { + this.retries(2); + return runCmd('grunt test').should.be.fulfilled(); + }); + + it('should run e2e tests successfully for production app', function() { + this.retries(2); + return runCmd('grunt test:e2e:prod').should.be.fulfilled(); + }); + } + }); + + describe('with TypeScript, Mocha + Chai (should) and no server options', function() { + var dir; + var jscsResult; + var lintResult; + var clientTestResult; + var serverTestResult; + var testOptions = { + buildtool: 'grunt', + transpiler: 'ts', + markup: 'jade', + stylesheet: 'stylus', + router: 'uirouter', + testing: 'mocha', + chai: 'should', + odms: [], + auth: false, + oauth: [], + socketio: false, + bootstrap: false, + uibootstrap: false + }; + + beforeEach(function() { + return runGen(testOptions).then(_dir => { + dir = _dir; + jscsResult = runCmd('grunt jscs'); + lintResult = runCmd('grunt tslint'); + clientTestResult = runCmd('grunt test:client'); + serverTestResult = runCmd('grunt test:server'); + }); + }); + + it('should generate the proper files', function() { + const expectedFiles = getExpectedFiles.app(testOptions); + assert.file(expectedFiles); + return assertOnlyFiles(expectedFiles, path.normalize(dir)).should.be.fulfilled(); + }); + + it('passes JSCS', function() { + return jscsResult.should.be.fulfilled(); + }); + + it('passes lint', function() { + return lintResult.should.be.fulfilled(); + }); + + it('should run client tests successfully', function() { + return clientTestResult.should.be.fulfilled(); + }); + + it('should run server tests successfully', function() { + return serverTestResult.should.be.fulfilled(); + }); + + describe('with a generated endpoint', function() { + beforeEach(function() { + return readJSON(path.join(dir, '.yo-rc.json')).then(config => { + return runEndpointGen('foo', {config: config['generator-angular-fullstack']}); + }); + }); + + it('should run server tests successfully', function() { + return runCmd('grunt test:server').should.be.fulfilled(); + }); + }); + + if(!process.env.SKIP_E2E) { + it('should run e2e tests successfully', function() { + this.retries(2); + return runCmd('grunt test:e2e').should.be.fulfilled(); + }); + + it('should run e2e tests successfully for production app', function() { + this.retries(2); + return runCmd('grunt test:e2e:prod').should.be.fulfilled(); + }); + } + }); +}); \ No newline at end of file diff --git a/src/test/pre.test.js b/src/test/pre.test.js new file mode 100644 index 000000000..61c88de27 --- /dev/null +++ b/src/test/pre.test.js @@ -0,0 +1,37 @@ +'use strict'; +import path from 'path'; +import assert from 'yeoman-assert'; + +describe('test fixtures', function() { + it('should have package.json in fixtures', function() { + assert.file([path.join(__dirname, 'fixtures/package.json')]); + }); + + it('should have .bowerrc & bower.json in fixtures', function() { + assert.file([path.join(__dirname, 'fixtures/bower.json'), path.join(__dirname, 'fixtures/.bowerrc')]); + }); + + it('should have .yo-rc.json in fixtures', function() { + assert.file([path.join(__dirname, 'fixtures/.yo-rc.json')]); + }); + + it('should have all npm packages in fixtures/node_modules', function() { + var packageJson = require('./fixtures/package.json'); + var deps = Object.keys(packageJson.dependencies); + deps = deps.concat(Object.keys(packageJson.devDependencies)); + deps = deps.map(function(dep) { + return path.join(__dirname, 'fixtures', 'node_modules', dep); + }); + assert.file(deps); + }); + + it('should have all bower packages in fixtures/bower_components', function() { + var bowerJson = require('./fixtures/bower.json'); + var deps = Object.keys(bowerJson.dependencies); + deps = deps.concat(Object.keys(bowerJson.devDependencies)); + deps = deps.map(function(dep) { + return path.join(__dirname, 'fixtures', 'bower_components', dep); + }); + assert.file(deps); + }); +}); \ No newline at end of file diff --git a/src/test/test-helpers.js b/src/test/test-helpers.js new file mode 100644 index 000000000..5a192e936 --- /dev/null +++ b/src/test/test-helpers.js @@ -0,0 +1,131 @@ +'use strict'; +import path from 'path'; +import fs from 'fs'; +import _ from 'lodash'; +import Promise from 'bluebird'; +import {exec} from 'child_process'; +import helpers from 'yeoman-test'; +import assert from 'yeoman-assert'; +import recursiveReadDir from 'recursive-readdir'; + +const TEST_DIR = __dirname; + +/** + * Copy file from src to dest + * @param {string} src + * @param {string} dest + * @returns {Promise} + */ +export function copyAsync(src, dest) { + return fs.readFileAsync(src) + .then(data => fs.writeFileAsync(dest, data)); +} + +/** + * @callback doneCallback + * @param {null|Error} err + */ + +/** + * Run the given command in a child process + * @param {string} cmd - command to run + * @returns {Promise} + */ +export function runCmd(cmd) { + return new Promise((resolve, reject) => { + exec(cmd, {}, function(err, stdout, stderr) { + if(err) { + console.error(stdout); + return reject(err); + } else { + if(DEBUG) console.log(`${cmd} stdout: ${stdout}`); + return resolve(); + } + }); + }); +} + +/** + * Assert that only the expected files are present + * @param {string[]} expectedFiles - array of expected files + * @param {string} [topLevelPath='./'] - root dir of expected files to recursively search + * @param {string[]} [skip=['node_modules','bower_components']] - files/folders recursiveReadDir should skip + * @returns {Promise} + */ +export function assertOnlyFiles(expectedFiles, topLevelPath='./', skip=['node_modules', 'bower_components']) { + return new Promise((resolve, reject) => { + recursiveReadDir(topLevelPath, skip, function(err, actualFiles) { + if(err) return reject(err); + + actualFiles = _.map(actualFiles.concat(), file => path.normalize(file.replace(path.normalize(`${topLevelPath}/`), ''))); + expectedFiles = _.map(expectedFiles, file => path.normalize(file)); + + let extras = _.pullAll(actualFiles, expectedFiles); + + if(extras.length !== 0) { + return reject(extras); + } + resolve(); + }); + }); +} + +/** + * Read JSON from a file + * @param {string} path + * @returns {Promise} - parsed JSON + */ +export function readJSON(path) { + return fs.readFileAsync(path, 'utf8').then(data => { + return JSON.parse(data); + }); +} + +/** + * Run angular-fullstack:app + * @param {object} [prompts] + * @param {object} [opts={}] + * @param {boolean} [opts.copyConfigFile] - copy default .yo-rc.json + * @returns {Promise} + */ +export function runGen(prompts, opts={}) { + let options = opts.options || {skipInstall: true}; + + return new Promise((resolve, reject) => { + let dir; + let gen = helpers + .run(require.resolve('../generators/app')) + .inTmpDir(function(_dir) { + // this will create a new temporary directory for each new generator run + var done = this.async(); + if(DEBUG) console.log(`TEMP DIR: ${_dir}`); + dir = _dir; + + let promises = [ + fs.mkdirAsync(dir + '/client').then(() => { + return fs.symlinkAsync(__dirname + '/fixtures/bower_components', dir + '/client/bower_components'); + }), + fs.symlinkAsync(__dirname + '/fixtures/node_modules', dir + '/node_modules') + ]; + + if(opts.copyConfigFile) { + promises.push(copyAsync(path.join(TEST_DIR, 'fixtures/.yo-rc.json'), path.join(dir, '.yo-rc.json'))); + } + + // symlink our dependency directories + return Promise.all(promises).then(done); + }) + .withGenerators([ + require.resolve('../generators/endpoint'), + // [helpers.createDummyGenerator(), 'ng-component:app'] + ]) + // .withArguments(['upperCaseBug']) + .withOptions(options); + + if(prompts) gen.withPrompts(prompts); + + gen + .on('error', reject) + .on('end', () => resolve(dir)); + }); +} \ No newline at end of file diff --git a/templates/app/.jscsrc b/templates/app/.jscsrc index 8923ad53e..e05f83c6c 100644 --- a/templates/app/.jscsrc +++ b/templates/app/.jscsrc @@ -2,7 +2,6 @@ "excludeFiles": [ "client/app/app.constant.js" ], - "esnext": true, "maximumLineLength": { "value": 100, "allowComments": true, diff --git a/templates/app/Gruntfile(grunt).js b/templates/app/Gruntfile(grunt).js index a39fd5d18..7f5d4e83f 100644 --- a/templates/app/Gruntfile(grunt).js +++ b/templates/app/Gruntfile(grunt).js @@ -1,7 +1,7 @@ // Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= rootGeneratorName() %> <%= rootGeneratorVersion() %> 'use strict'; -module.exports = function (grunt) { +module.exports = function(grunt) { var localConfig; try { localConfig = require('./server/config/local.env'); @@ -26,7 +26,6 @@ module.exports = function (grunt) { // Define the configuration for all the tasks grunt.initConfig({ - // Project settings pkg: grunt.file.readJSON('package.json'), yeoman: { @@ -87,7 +86,7 @@ module.exports = function (grunt) { jsTest: { files: ['<%%= yeoman.client %>/{app,components}/**/*.{spec,mock}.<%= scriptExt %>'], tasks: [<% if(filters.babel) { %>'newer:jshint:all'<% } if(filters.ts) { %>'newer:tslint:all', 'newer:ts:client_test',<% } %>, 'wiredep:test', 'karma'] - },<% if (filters.stylus) { %> + },<% if(filters.stylus) { %> injectStylus: { files: ['<%%= yeoman.client %>/{app,components}/**/*.styl'], tasks: ['injector:stylus'] @@ -95,7 +94,7 @@ module.exports = function (grunt) { stylus: { files: ['<%%= yeoman.client %>/{app,components}/**/*.styl'], tasks: ['stylus', 'postcss'] - },<% } if (filters.sass) { %> + },<% } if(filters.sass) { %> injectSass: { files: ['<%%= yeoman.client %>/{app,components}/**/*.{scss,sass}'], tasks: ['injector:sass'] @@ -103,7 +102,7 @@ module.exports = function (grunt) { sass: { files: ['<%%= yeoman.client %>/{app,components}/**/*.{scss,sass}'], tasks: ['sass', 'postcss'] - },<% } if (filters.less) { %> + },<% } if(filters.less) { %> injectLess: { files: ['<%%= yeoman.client %>/{app,components}/**/*.less'], tasks: ['injector:less'] @@ -111,7 +110,7 @@ module.exports = function (grunt) { less: { files: ['<%%= yeoman.client %>/{app,components}/**/*.less'], tasks: ['less', 'postcss'] - },<% } if (filters.jade) { %> + },<% } if(filters.jade) { %> jade: { files: ['<%%= yeoman.client %>/{app,components}/**/*.jade'], tasks: ['jade'] @@ -142,6 +141,7 @@ module.exports = function (grunt) { tasks: ['wiredep'] }, }, + <%_ if(!filters.ts) { _%> // Make sure code styles are up to par and there are no obvious mistakes jshint: { @@ -165,7 +165,8 @@ module.exports = function (grunt) { test: { src: ['<%%= yeoman.client %>/{app,components}/**/*.{spec,mock}.js'] } - },<% if(filters.ts) { %> + },<% } %> + <%_ if(filters.ts) { _%> tslint: { options: { @@ -240,14 +241,14 @@ module.exports = function (grunt) { env: { PORT: process.env.PORT || 9000 }, - callback: function (nodemon) { - nodemon.on('log', function (event) { + callback: function(nodemon) { + nodemon.on('log', function(event) { console.log(event.colour); }); // opens browser on initial server start - nodemon.on('config:update', function () { - setTimeout(function () { + nodemon.on('config:update', function() { + setTimeout(function() { require('open')('http://localhost:8080/debug?port=5858'); }, 500); }); @@ -469,9 +470,9 @@ module.exports = function (grunt) { // Run some tasks in parallel to speed up the build process concurrent: { - pre: [<% if (filters.stylus) { %> - 'injector:stylus',<% } if (filters.less) { %> - 'injector:less',<% } if (filters.sass) { %> + pre: [<% if(filters.stylus) { %> + 'injector:stylus',<% } if(filters.less) { %> + 'injector:less',<% } if(filters.sass) { %> 'injector:sass',<% } %> 'ngconstant'<% if(filters.ts) { %>, 'copy:constant'<% } %> @@ -596,7 +597,7 @@ module.exports = function (grunt) { NODE_ENV: 'production' }, all: localConfig - },<% if (filters.jade) { %> + },<% if(filters.jade) { %> // Compiles Jade to html jade: { @@ -688,7 +689,7 @@ module.exports = function (grunt) { '.tmp/app/app.css' : '<%%= yeoman.client %>/app/app.styl' } } - },<% } if (filters.sass) { %> + },<% } if(filters.sass) { %> // Compiles Sass to CSS sass: { @@ -700,7 +701,7 @@ module.exports = function (grunt) { '.tmp/app/app.css' : '<%%= yeoman.client %>/app/app.scss' } } - },<% } if (filters.less) { %> + },<% } if(filters.less) { %> // Compiles Less to CSS less: { @@ -742,7 +743,7 @@ module.exports = function (grunt) { ] ] } - },<% if (filters.stylus) { %> + },<% if(filters.stylus) { %> // Inject component styl into app.styl stylus: { @@ -762,7 +763,7 @@ module.exports = function (grunt) { '!<%%= yeoman.client %>/app/app.styl' ] } - },<% } if (filters.sass) { %> + },<% } if(filters.sass) { %> // Inject component scss into app.scss sass: { @@ -782,7 +783,7 @@ module.exports = function (grunt) { '!<%%= yeoman.client %>/app/app.{scss,sass}' ] } - },<% } if (filters.less) { %> + },<% } if(filters.less) { %> // Inject component less into app.less less: { @@ -826,12 +827,12 @@ module.exports = function (grunt) { }); // Used for delaying livereload until after server has restarted - grunt.registerTask('wait', function () { + grunt.registerTask('wait', function() { grunt.log.ok('Waiting for server reload...'); var done = this.async(); - setTimeout(function () { + setTimeout(function() { grunt.log.writeln('Done waiting!'); done(); }, 1500); @@ -841,12 +842,12 @@ module.exports = function (grunt) { this.async(); }); - grunt.registerTask('serve', function (target) { - if (target === 'dist') { + grunt.registerTask('serve', function(target) { + if(target === 'dist') { return grunt.task.run(['build', 'env:all', 'env:prod', 'express:prod', 'wait', 'open', 'express-keepalive']); } - if (target === 'debug') { + if(target === 'debug') { return grunt.task.run([ 'clean:server', 'env:all', @@ -876,22 +877,20 @@ module.exports = function (grunt) { ]); }); - grunt.registerTask('server', function () { + grunt.registerTask('server', function() { grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); grunt.task.run(['serve']); }); grunt.registerTask('test', function(target, option) { - if (target === 'server') { + if(target === 'server') { return grunt.task.run([ 'env:all', 'env:test', 'mochaTest:unit', 'mochaTest:integration' ]); - } - - else if (target === 'client') { + } else if(target === 'client') { return grunt.task.run([ 'clean:server', 'env:all', @@ -905,11 +904,8 @@ module.exports = function (grunt) { 'wiredep:test', 'karma' ]); - } - - else if (target === 'e2e') { - - if (option === 'prod') { + } else if(target === 'e2e') { + if(option === 'prod') { return grunt.task.run([ 'build', 'env:all', @@ -917,9 +913,7 @@ module.exports = function (grunt) { 'express:prod', 'protractor' ]); - } - - else { + } else { return grunt.task.run([ 'clean:server', 'env:all', @@ -937,33 +931,24 @@ module.exports = function (grunt) { 'protractor' ]); } - } - - else if (target === 'coverage') { - - if (option === 'unit') { + } else if(target === 'coverage') { + if(option === 'unit') { return grunt.task.run([ 'env:all', 'env:test', 'mocha_istanbul:unit' ]); - } - - else if (option === 'integration') { + } else if(option === 'integration') { return grunt.task.run([ 'env:all', 'env:test', 'mocha_istanbul:integration' ]); - } - - else if (option === 'check') { + } else if(option === 'check') { return grunt.task.run([ 'istanbul_check_coverage' ]); - } - - else { + } else { return grunt.task.run([ 'env:all', 'env:test', @@ -971,13 +956,12 @@ module.exports = function (grunt) { 'istanbul_check_coverage' ]); } - + } else { + grunt.task.run([ + 'test:server', + 'test:client' + ]); } - - else grunt.task.run([ - 'test:server', - 'test:client' - ]); }); grunt.registerTask('build', [ diff --git a/templates/app/client/components/navbar/navbar.controller.js b/templates/app/client/components/navbar/navbar.controller.js index 958355f9f..12ae0b351 100644 --- a/templates/app/client/components/navbar/navbar.controller.js +++ b/templates/app/client/components/navbar/navbar.controller.js @@ -9,13 +9,19 @@ class NavbarController { isCollapsed = true; //end-non-standard + <%_ if(filters.ngroute || filters.auth) { _%> - constructor(<% if(!filters.uirouter) { %>$location<% } if(!filters.uirouter && filters.auth) { %>, <% } if (filters.auth) { %>Auth<% } %>) {<% if(!filters.uirouter) { %> - this.$location = $location;<% } %> - <% if (filters.auth) { %>this.isLoggedIn = Auth.isLoggedIn; + constructor(<% if(!filters.uirouter) { %>$location<% } if(!filters.uirouter && filters.auth) { %>, <% } if (filters.auth) { %>Auth<% } %>) { + <%_ if(!filters.uirouter) { _%> + this.$location = $location; + <%_ } _%> + <%_ if (filters.auth) { _%> + this.isLoggedIn = Auth.isLoggedIn; this.isAdmin = Auth.isAdmin; this.getCurrentUser = Auth.getCurrentUser; - <% } %>}<% if(!filters.uirouter) { %> + <%_ } _%> + }<% } %> + <%_ if(!filters.uirouter) { _%> isActive(route) { return route === this.$location.path(); diff --git a/templates/app/e2e/main/main.spec(jasmine).js b/templates/app/e2e/main/main.spec(jasmine).js index 57284495a..3d56cb5d3 100644 --- a/templates/app/e2e/main/main.spec(jasmine).js +++ b/templates/app/e2e/main/main.spec(jasmine).js @@ -12,7 +12,7 @@ describe('Main View', function() { it('should include jumbotron with correct data', function() { expect(page.h1El.getText()).toBe('\'Allo, \'Allo!'); - expect(page.imgEl.getAttribute('src')).toMatch(/yeoman.png$/); + expect(page.imgEl.getAttribute('src')).toMatch(/yeoman(\.[a-zA-Z0-9]*)?\.png$/); expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); }); }); diff --git a/templates/app/e2e/main/main.spec(mocha).js b/templates/app/e2e/main/main.spec(mocha).js index 798b58c41..12b0781db 100644 --- a/templates/app/e2e/main/main.spec(mocha).js +++ b/templates/app/e2e/main/main.spec(mocha).js @@ -13,7 +13,7 @@ describe('Main View', function() { it('should include jumbotron with correct data', function() { <%= expect() %>page.h1El.getText()<%= to() %>.eventually.equal('\'Allo, \'Allo!'); - <%= expect() %>page.imgEl.getAttribute('src')<%= to() %>.eventually.match(/yeoman.png$/); + <%= expect() %>page.imgEl.getAttribute('src')<%= to() %>.eventually.match(/yeoman(\.[a-zA-Z0-9]*)?\.png$/); <%= expect() %>page.imgEl.getAttribute('alt')<%= to() %>.eventually.equal('I\'m Yeoman'); }); }); diff --git a/templates/app/server/config/express.js b/templates/app/server/config/express.js index eeca74e6e..84f82043b 100644 --- a/templates/app/server/config/express.js +++ b/templates/app/server/config/express.js @@ -56,7 +56,7 @@ export default function(app) { * Lusca - express server security * https://github.com/krakenjs/lusca */ - if ('test' !== env) { + if (env !== 'test' && !process.env.SAUCE_USERNAME) { app.use(lusca({ csrf: { angular: true diff --git a/test/fixtures/.yo-rc.json b/test/fixtures/.yo-rc.json index 716e42b6c..3f652f692 100644 --- a/test/fixtures/.yo-rc.json +++ b/test/fixtures/.yo-rc.json @@ -13,24 +13,57 @@ "registerModelsFile": "server/sqldb/index.js", "modelsNeedle": "// Insert models below", "filters": { + "js": true, "babel": true, + "flow": false, "html": true, - "less": true, + "sass": true, "uirouter": true, - "bootstrap": false, - "uibootstrap": false, + "bootstrap": true, + "uibootstrap": true, "socketio": true, "auth": true, "models": true, "mongooseModels": true, "mongoose": true, - "oauth": true, - "googleAuth": true, "grunt": true, "mocha": true, "jasmine": false, - "should": true, - "expect": false + "expect": true } + }, + "generator-ng-component": { + "routeDirectory": "client/app/", + "directiveDirectory": "client/app/", + "componentDirectory": "app/components/", + "filterDirectory": "client/app/", + "serviceDirectory": "client/app/", + "basePath": "client", + "moduleName": "", + "modulePrompt": true, + "filters": [ + "uirouter", + "mocha", + "expect", + "should", + "uirouter", + "es6" + ], + "extensions": [ + "babel", + "js", + "html", + "scss" + ], + "directiveSimpleTemplates": "", + "directiveComplexTemplates": "", + "filterTemplates": "", + "serviceTemplates": "", + "factoryTemplates": "", + "controllerTemplates": "", + "componentTemplates": "", + "decoratorTemplates": "", + "providerTemplates": "", + "routeTemplates": "" } } diff --git a/test/test-file-creation.js b/test/test-file-creation.js deleted file mode 100644 index 5c4c29e5d..000000000 --- a/test/test-file-creation.js +++ /dev/null @@ -1,758 +0,0 @@ -/*global describe, beforeEach, it */ -'use strict'; -var path = require('path'); -var fs = require('fs'); -var exec = require('child_process').exec; -var helpers = require('yeoman-test'); -var assert = require('yeoman-assert'); -var chai = require('chai'); -var expect = chai.expect; -var recursiveReadDir = require('recursive-readdir'); - -describe('angular-fullstack generator', function () { - var gen, defaultOptions = { - buildtool: 'grunt', - script: 'js', - transpiler: 'babel', - markup: 'html', - stylesheet: 'sass', - router: 'uirouter', - testing: 'mocha', - chai: 'expect', - bootstrap: true, - uibootstrap: true, - odms: [ 'mongoose' ], - auth: true, - oauth: [], - socketio: true - }, dependenciesInstalled = false; - - function copySync(s, d) { fs.writeFileSync(d, fs.readFileSync(s)); } - - function generatorTest(generatorType, name, mockPrompt, callback) { - gen.run(function () { - var afGenerator; - var deps = [path.join('../../generators', generatorType)]; - afGenerator = helpers.createGenerator('angular-fullstack:' + generatorType, deps, [name], { - skipInstall: true - }); - - helpers.mockPrompt(afGenerator, mockPrompt); - afGenerator.run(function () { - callback(); - }); - }); - } - - /** - * Assert that only an array of files exist at a given path - * - * @param {Array} expectedFiles - array of files - * @param {Function} done - callback(error{Error}) - * @param {String} topLevelPath - top level path to assert files at (optional) - * @param {Array} skip - array of paths to skip/ignore (optional) - * - */ - function assertOnlyFiles(expectedFiles, done, topLevelPath, skip) { - topLevelPath = topLevelPath || './'; - skip = skip || ['node_modules', 'client/bower_components']; - - recursiveReadDir(topLevelPath, skip, function(err, actualFiles) { - if (err) { return done(err); } - var files = actualFiles.concat(); - - expectedFiles.forEach(function(file, i) { - var index = files.indexOf(path.normalize(file)); - if (index >= 0) { - files.splice(index, 1); - } - }); - - if (files.length !== 0) { - err = new Error('unexpected files found'); - err.expected = expectedFiles.join('\n'); - err.actual = files.join('\n'); - return done(err); - } - - done(); - }); - } - - /** - * Exec a command and run test assertion(s) based on command type - * - * @param {String} cmd - the command to exec - * @param {Object} self - context of the test - * @param {Function} cb - callback() - * @param {String} endpoint - endpoint to generate before exec (optional) - * @param {Number} timeout - timeout for the exec and test (optional) - * - */ - function runTest(cmd, self, cb) { - var args = Array.prototype.slice.call(arguments), - endpoint = (args[3] && typeof args[3] === 'string') ? args.splice(3, 1)[0] : null, - timeout = (args[3] && typeof args[3] === 'number') ? args.splice(3, 1)[0] : null; - - self.timeout(timeout || 60000); - - var execFn = function() { - var cmdCode; - var cp = exec(cmd, function(error, stdout, stderr) { - if(cmdCode !== 0) { - console.error(stdout); - throw new Error('Error running command: ' + cmd); - } - cb(); - }); - cp.on('exit', function (code) { - cmdCode = code; - }); - }; - - if (endpoint) { - generatorTest('endpoint', endpoint, {}, execFn); - } else { - gen.run(execFn); - } - } - - /** - * Generate an array of files to expect from a set of options - * - * @param {Object} ops - generator options - * @return {Array} - array of files - * - */ - function genFiles(ops) { - var mapping = { - stylesheet: { - sass: 'scss', - stylus: 'styl', - less: 'less', - css: 'css' - }, - markup: { - jade: 'jade', - html: 'html' - }, - script: { - js: 'js', - ts: 'ts' - } - }, - files = []; - - /** - * Generate an array of OAuth files based on type - * - * @param {String} type - type of oauth - * @return {Array} - array of files - * - */ - var oauthFiles = function(type) { - return [ - 'server/auth/' + type + '/index.js', - 'server/auth/' + type + '/passport.js', - ]; - }; - - - var script = mapping.script[ops.transpiler === 'ts' ? 'ts' : 'js'], - markup = mapping.markup[ops.markup], - stylesheet = mapping.stylesheet[ops.stylesheet], - models = ops.models ? ops.models : ops.odms[0]; - - /* Core Files */ - files = files.concat([ - 'client/.htaccess', - 'client/favicon.ico', - 'client/robots.txt', - 'client/index.html', - 'client/app/app.' + script, - 'client/app/app.' + stylesheet, - 'client/app/main/main.' + script, - 'client/app/main/main.' + markup, - 'client/app/main/main.' + stylesheet, - 'client/app/main/main.controller.' + script, - 'client/app/main/main.controller.spec.' + script, - 'client/assets/images/yeoman.png', - 'client/components/footer/footer.' + stylesheet, - 'client/components/footer/footer.' + markup, - 'client/components/footer/footer.directive.' + script, - 'client/components/navbar/navbar.' + markup, - 'client/components/navbar/navbar.controller.' + script, - 'client/components/navbar/navbar.directive.' + script, - 'client/components/util/util.module.' + script, - 'client/components/util/util.service.' + script, - 'server/.jshintrc', - 'server/.jshintrc-spec', - 'server/app.js', - 'server/index.js', - 'server/routes.js', - 'server/api/thing/index.js', - 'server/api/thing/index.spec.js', - 'server/api/thing/thing.controller.js', - 'server/api/thing/thing.integration.js', - 'server/components/errors/index.js', - 'server/config/local.env.js', - 'server/config/local.env.sample.js', - 'server/config/express.js', - 'server/config/environment/index.js', - 'server/config/environment/development.js', - 'server/config/environment/production.js', - 'server/config/environment/test.js', - 'server/config/environment/shared.js', - 'server/views/404.' + markup, - 'e2e/main/main.po.js', - 'e2e/main/main.spec.js', - 'e2e/components/navbar/navbar.po.js', - '.babelrc', - '.bowerrc', - '.buildignore', - '.editorconfig', - '.gitattributes', - '.gitignore', - '.travis.yml', - '.jscsrc', - '.yo-rc.json', - 'Gruntfile.js', - 'package.json', - 'bower.json', - 'karma.conf.js', - 'mocha.conf.js', - 'protractor.conf.js', - 'README.md' - ]); - - /* TypeScript */ - if (ops.transpiler === 'ts') { - files = files.concat([ - 'tsconfig.client.test.json', - 'tsconfig.client.json', - 'tsd.json', - 'tsd_test.json', - 'client/tslint.json' - ]); - } else { - files = files.concat([ - 'client/.jshintrc' - ]); - } - - /* Ui-Router */ - if (ops.router === 'uirouter') { - files = files.concat([ - 'client/components/ui-router/ui-router.mock.' + script - ]); - } - - /* Ui-Bootstrap */ - if (ops.uibootstrap) { - files = files.concat([ - 'client/components/modal/modal.' + markup, - 'client/components/modal/modal.' + stylesheet, - 'client/components/modal/modal.service.' + script - ]); - } - - /* Models - Mongoose or Sequelize */ - if (models) { - files = files.concat([ - 'server/api/thing/thing.model.js', - 'server/api/thing/thing.events.js', - 'server/config/seed.js' - ]); - } - - /* Sequelize */ - if (ops.odms.indexOf('sequelize') !== -1) { - files = files.concat([ - 'server/sqldb/index.js' - ]); - } - - /* Authentication */ - if (ops.auth) { - files = files.concat([ - 'client/app/account/account.' + script, - 'client/app/account/login/login.' + markup, - 'client/app/account/login/login.controller.' + script, - 'client/app/account/settings/settings.' + markup, - 'client/app/account/settings/settings.controller.' + script, - 'client/app/account/signup/signup.' + markup, - 'client/app/account/signup/signup.controller.' + script, - 'client/app/admin/admin.' + markup, - 'client/app/admin/admin.' + stylesheet, - 'client/app/admin/admin.module.' + script, - 'client/app/admin/admin.router.' + script, - 'client/app/admin/admin.controller.' + script, - 'client/components/auth/auth.module.' + script, - 'client/components/auth/auth.service.' + script, - 'client/components/auth/interceptor.service.' + script, - 'client/components/auth/router.decorator.' + script, - 'client/components/auth/user.service.' + script, - 'client/components/mongoose-error/mongoose-error.directive.' + script, - 'server/api/user/index.js', - 'server/api/user/index.spec.js', - 'server/api/user/user.controller.js', - 'server/api/user/user.integration.js', - 'server/api/user/user.model.js', - 'server/api/user/user.model.spec.js', - 'server/api/user/user.events.js', - 'server/auth/index.js', - 'server/auth/auth.service.js', - 'server/auth/local/index.js', - 'server/auth/local/passport.js', - 'e2e/account/login/login.po.js', - 'e2e/account/login/login.spec.js', - 'e2e/account/logout/logout.spec.js', - 'e2e/account/signup/signup.po.js', - 'e2e/account/signup/signup.spec.js' - ]); - } - - if (ops.oauth && ops.oauth.length) { - /* OAuth (see oauthFiles function above) */ - ops.oauth.forEach(function(type, i) { - files = files.concat(oauthFiles(type.replace('Auth', ''))); - }); - - - files = files.concat([ - 'client/components/oauth-buttons/oauth-buttons.' + stylesheet, - 'client/components/oauth-buttons/oauth-buttons.' + markup, - 'client/components/oauth-buttons/oauth-buttons.controller.' + script, - 'client/components/oauth-buttons/oauth-buttons.controller.spec.' + script, - 'client/components/oauth-buttons/oauth-buttons.directive.' + script, - 'client/components/oauth-buttons/oauth-buttons.directive.spec.' + script, - 'e2e/components/oauth-buttons/oauth-buttons.po.js' - ]); - } - - /* Socket.IO */ - if (ops.socketio) { - files = files.concat([ - 'client/components/socket/socket.service.' + script, - 'client/components/socket/socket.mock.' + script, - 'server/api/thing/thing.socket.js', - 'server/config/socketio.js' - ]); - } - - return files; - } - - - /** - * Generator tests - */ - - beforeEach(function (done) { - this.timeout(10000); - var deps = [ - '../../generators/app', - '../../generators/endpoint', - [ - helpers.createDummyGenerator(), - 'ng-component:app' - ] - ]; - - helpers.testDirectory(path.join(__dirname, 'temp'), function (err) { - if (err) { - return done(err); - } - - gen = helpers.createGenerator('angular-fullstack:app', deps, [], { - skipInstall: true - }); - gen.conflicter.force = true; - done(); - }.bind(this)); - }); - - describe('making sure test fixtures are present', function() { - - it('should have package.json in fixtures', function() { - assert.file([ - path.join(__dirname, 'fixtures', 'package.json') - ]); - }); - - it('should have bower.json in fixtures', function() { - assert.file([ - path.join(__dirname, 'fixtures', 'bower.json') - ]); - }); - - it('should have all npm packages in fixtures/node_modules', function() { - var packageJson = require('./fixtures/package.json'); - var deps = Object.keys(packageJson.dependencies); - deps = deps.concat(Object.keys(packageJson.devDependencies)); - deps = deps.map(function(dep) { - return path.join(__dirname, 'fixtures', 'node_modules', dep); - }); - assert.file(deps); - }); - - it('should have all bower packages in fixtures/bower_components', function() { - var bowerJson = require('./fixtures/bower.json'); - var deps = Object.keys(bowerJson.dependencies); - deps = deps.concat(Object.keys(bowerJson.devDependencies)); - deps = deps.map(function(dep) { - return path.join(__dirname, 'fixtures', 'bower_components', dep); - }); - assert.file(deps); - }); - }); - - describe('running app', function() { - - beforeEach(function() { - this.timeout(20000); - fs.mkdirSync(__dirname + '/temp/client'); - fs.symlinkSync(__dirname + '/fixtures/node_modules', __dirname + '/temp/node_modules'); - fs.symlinkSync(__dirname +'/fixtures/bower_components', __dirname +'/temp/client/bower_components'); - }); - - describe('with default options', function() { - beforeEach(function() { - helpers.mockPrompt(gen, defaultOptions); - }); - - it('should run client tests successfully', function(done) { - runTest('grunt test:client', this, done); - }); - - it('should pass jscs', function(done) { - runTest('grunt jscs', this, done); - }); - - it('should pass lint', function(done) { - runTest('grunt jshint', this, done); - }); - - it('should run server tests successfully', function(done) { - runTest('grunt test:server', this, done); - }); - - it('should pass jscs with generated endpoint', function(done) { - runTest('grunt jscs', this, done, 'foo'); - }); - - it('should pass lint with generated endpoint', function(done) { - runTest('grunt jshint', this, done, 'foo'); - }); - - it('should run server tests successfully with generated endpoint', function(done) { - runTest('grunt test:server', this, done, 'foo'); - }); - - it('should pass lint with generated capitalized endpoint', function(done) { - runTest('grunt jshint', this, done, 'Foo'); - }); - - it('should run server tests successfully with generated capitalized endpoint', function(done) { - runTest('grunt test:server', this, done, 'Foo'); - }); - - it('should pass lint with generated path name endpoint', function(done) { - runTest('grunt jshint', this, done, 'foo/bar'); - }); - - it('should run server tests successfully with generated path name endpoint', function(done) { - runTest('grunt test:server', this, done, 'foo/bar'); - }); - - it('should generate expected files with path name endpoint', function(done) { - runTest('(exit 0)', this, function() { - assert.file([ - 'server/api/foo/bar/index.js', - 'server/api/foo/bar/index.spec.js', - 'server/api/foo/bar/bar.controller.js', - 'server/api/foo/bar/bar.events.js', - 'server/api/foo/bar/bar.integration.js', - 'server/api/foo/bar/bar.model.js', - 'server/api/foo/bar/bar.socket.js' - ]); - done(); - }, 'foo/bar'); - }); - - it('should use existing config if available', function(done) { - this.timeout(60000); - copySync(__dirname + '/fixtures/.yo-rc.json', __dirname + '/temp/.yo-rc.json'); - var gen = helpers.createGenerator('angular-fullstack:app', [ - '../../generators/app', - '../../generators/endpoint', - [ - helpers.createDummyGenerator(), - 'ng-component:app' - ] - ], [], { - skipInstall: true - }); - helpers.mockPrompt(gen, { - skipConfig: true - }); - gen.run(function () { - assert.file([ - 'client/app/main/main.less', - 'server/auth/google/passport.js' - ]); - done(); - }); - }); - - it('should generate expected files', function (done) { - gen.run(function () { - assert.file(genFiles(defaultOptions)); - done(); - }); - }); - - it('should not generate unexpected files', function (done) { - gen.run(function () { - assertOnlyFiles(genFiles(defaultOptions), done); - }); - }); - - if(!process.env.SKIP_E2E) { - it('should run e2e tests successfully', function(done) { - runTest('grunt test:e2e', this, done, 240000); - }); - - //it('should run e2e tests successfully for production app', function(done) { - // runTest('grunt test:e2e:prod', this, done, 240000); - //}); - } - }); - - describe('with other preprocessors and oauth', function() { - var testOptions = { - buildtool: 'grunt', - script: 'js', - transpiler: 'ts', - markup: 'jade', - stylesheet: 'less', - router: 'uirouter', - testing: 'jasmine', - odms: [ 'mongoose' ], - auth: true, - oauth: ['twitterAuth', 'facebookAuth', 'googleAuth'], - socketio: true, - bootstrap: true, - uibootstrap: true - }; - - beforeEach(function() { - helpers.mockPrompt(gen, testOptions); - }); - - it('should run client tests successfully', function(done) { - runTest('grunt test:client', this, done); - }); - - it('should pass jscs', function(done) { - runTest('grunt jscs', this, done); - }); - - it('should pass lint', function(done) { - runTest('grunt tslint', this, done); - }); - - it('should run server tests successfully', function(done) { - runTest('grunt test:server', this, done); - }); - - it('should pass jscs with generated endpoint', function(done) { - runTest('grunt jscs', this, done, 'foo'); - }); - - // TODO: generator-ng-component needs TS support - // it('should pass lint with generated snake-case endpoint', function(done) { - // runTest('grunt jshint', this, done, 'foo-bar'); - // }); - - it('should run server tests successfully with generated snake-case endpoint', function(done) { - runTest('grunt test:server', this, done, 'foo-bar'); - }); - - it('should generate expected files', function (done) { - gen.run(function () { - assert.file(genFiles(testOptions)); - done(); - }); - }); - - it('should not generate unexpected files', function (done) { - gen.run(function () { - assertOnlyFiles(genFiles(testOptions), done); - }); - }); - - if(!process.env.SKIP_E2E) { - it('should run e2e tests successfully', function (done) { - runTest('grunt test:e2e', this, done, 240000); - }); - - //it('should run e2e tests successfully for production app', function (done) { - // runTest('grunt test:e2e:prod', this, done, 240000); - //}); - } - - }); - - describe('with sequelize models, auth', function() { - var testOptions = { - buildtool: 'grunt', - script: 'js', - transpiler: 'babel', - markup: 'jade', - stylesheet: 'stylus', - router: 'uirouter', - testing: 'jasmine', - odms: [ 'sequelize' ], - auth: true, - oauth: ['twitterAuth', 'facebookAuth', 'googleAuth'], - socketio: true, - bootstrap: true, - uibootstrap: true - }; - - beforeEach(function() { - helpers.mockPrompt(gen, testOptions); - }); - - it('should run client tests successfully', function(done) { - runTest('grunt test:client', this, done); - }); - - it('should pass jscs', function(done) { - runTest('grunt jscs', this, done); - }); - - it('should pass lint', function(done) { - runTest('grunt jshint', this, done); - }); - - it('should run server tests successfully', function(done) { - runTest('grunt test:server', this, done); - }); - - it('should pass jscs with generated endpoint', function(done) { - runTest('grunt jscs', this, done, 'foo'); - }); - - it('should pass lint with generated snake-case endpoint', function(done) { - runTest('grunt jshint', this, done, 'foo-bar'); - }); - - it('should run server tests successfully with generated snake-case endpoint', function(done) { - runTest('grunt test:server', this, done, 'foo-bar'); - }); - - it('should generate expected files', function (done) { - gen.run(function () { - assert.file(genFiles(testOptions)); - done(); - }); - }); - - it('should not generate unexpected files', function (done) { - gen.run(function () { - assertOnlyFiles(genFiles(testOptions), done); - }); - }); - - if(!process.env.SKIP_E2E) { - it('should run e2e tests successfully', function (done) { - runTest('grunt test:e2e', this, done, 240000); - }); - - //it('should run e2e tests successfully for production app', function (done) { - // runTest('grunt test:e2e:prod', this, done, 240000); - //}); - } - - }); - - describe('with other preprocessors and no server options', function() { - var testOptions = { - buildtool: 'grunt', - script: 'js', - transpiler: 'ts', - markup: 'jade', - stylesheet: 'stylus', - router: 'ngroute', - testing: 'mocha', - chai: 'should', - odms: [], - auth: false, - oauth: [], - socketio: false, - bootstrap: false, - uibootstrap: false - }; - - beforeEach(function(done) { - helpers.mockPrompt(gen, testOptions); - done(); - }); - - it('should run client tests successfully', function(done) { - runTest('grunt test:client', this, done); - }); - - it('should pass jscs', function(done) { - runTest('grunt jscs', this, done); - }); - - it('should pass lint', function(done) { - runTest('grunt tslint', this, done); - }); - - it('should run server tests successfully', function(done) { - runTest('grunt test:server', this, done); - }); - - it('should pass jscs with generated endpoint', function(done) { - runTest('grunt jscs', this, done, 'foo'); - }); - - // TODO: generator-ng-component needs TS support - // it('should pass lint with generated endpoint', function(done) { - // runTest('grunt jshint', this, done, 'foo'); - // }); - - it('should run server tests successfully with generated endpoint', function(done) { - runTest('grunt test:server', this, done, 'foo'); - }); - - it('should generate expected files', function (done) { - gen.run(function () { - assert.file(genFiles(testOptions)); - done(); - }); - }); - - it('should not generate unexpected files', function (done) { - gen.run(function () { - assertOnlyFiles(genFiles(testOptions), done); - }); - }); - - if(!process.env.SKIP_E2E) { - it('should run e2e tests successfully', function (done) { - runTest('grunt test:e2e', this, done, 240000); - }); - - //it('should run e2e tests successfully for production app', function (done) { - // runTest('grunt test:e2e:prod', this, done, 240000); - //}); - } - - }); - }); -});