From 102078dda98d2db9aacb9eb67d5d45ed0dbb1af9 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Mon, 25 Aug 2014 01:06:36 -0600 Subject: [PATCH 001/171] use more restrictive matching for serve-favicon version --- app/templates/_package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/_package.json b/app/templates/_package.json index 36256461c..e32849740 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -7,7 +7,7 @@ "morgan": "~1.0.0", "body-parser": "~1.5.0", "method-override": "~1.0.0", - "serve-favicon": "^2.0.1", + "serve-favicon": "~2.0.1", "cookie-parser": "~1.0.1", "express-session": "~1.0.2", "errorhandler": "~1.0.0", From 6aecdf7fc8929fe4853280d0e8ac878f3e2a20fd Mon Sep 17 00:00:00 2001 From: Patrick Baker Date: Tue, 26 Aug 2014 18:01:38 -0400 Subject: [PATCH 002/171] feat(auth): make crypto async - Update tests and add new test for changed password - User.authenticate() User.makeSalt() and User.encryptPassword() public API remains same - User.authenticate() User.makeSalt() and User.encryptPassword() callbacks are optional - Change User schema from hashedPassword to password - Remove unnecessary virtual attribute User.password, getters and setters are not async --- app/templates/server/.jshintrc | 1 + .../server/api/user(auth)/user.controller.js | 26 +++-- .../server/api/user(auth)/user.model.js | 104 +++++++++++++----- .../server/api/user(auth)/user.model.spec.js | 49 +++++++-- .../server/auth(auth)/local/passport.js | 14 ++- 5 files changed, 141 insertions(+), 53 deletions(-) diff --git a/app/templates/server/.jshintrc b/app/templates/server/.jshintrc index d7b958e7c..42fea558a 100644 --- a/app/templates/server/.jshintrc +++ b/app/templates/server/.jshintrc @@ -1,4 +1,5 @@ { + "expr": true, "node": true, "esnext": true, "bitwise": true, diff --git a/app/templates/server/api/user(auth)/user.controller.js b/app/templates/server/api/user(auth)/user.controller.js index f4cd10c29..17e6e0e04 100644 --- a/app/templates/server/api/user(auth)/user.controller.js +++ b/app/templates/server/api/user(auth)/user.controller.js @@ -14,7 +14,7 @@ var validationError = function(res, err) { * restriction: 'admin' */ exports.index = function(req, res) { - User.find({}, '-salt -hashedPassword', function (err, users) { + User.find({}, '-salt -password', function (err, users) { if(err) return res.send(500, err); res.json(200, users); }); @@ -67,15 +67,19 @@ exports.changePassword = function(req, res, next) { var newPass = String(req.body.newPassword); User.findById(userId, function (err, user) { - if(user.authenticate(oldPass)) { - user.password = newPass; - user.save(function(err) { - if (err) return validationError(res, err); - res.send(200); - }); - } else { - res.send(403); - } + user.authenticate(oldPass, function(authErr, authenticated) { + if (authErr) res.send(403); + + if (authenticated) { + user.password = newPass; + user.save(function(err) { + if (err) return validationError(res, err); + res.send(200); + }); + } else { + res.send(403); + } + }); }); }; @@ -86,7 +90,7 @@ exports.me = function(req, res, next) { var userId = req.user._id; User.findOne({ _id: userId - }, '-salt -hashedPassword', function(err, user) { // don't ever give out the password or salt + }, '-salt -password', function(err, user) { // don't ever give out the password or salt if (err) return next(err); if (!user) return res.json(401); res.json(user); diff --git a/app/templates/server/api/user(auth)/user.model.js b/app/templates/server/api/user(auth)/user.model.js index cc8d59263..b3497f859 100644 --- a/app/templates/server/api/user(auth)/user.model.js +++ b/app/templates/server/api/user(auth)/user.model.js @@ -12,7 +12,7 @@ var UserSchema = new Schema({ type: String, default: 'user' }, - hashedPassword: String, + password: String, provider: String, salt: String<% if (filters.oauth) { %>,<% if (filters.facebookAuth) { %> facebook: {},<% } %><% if (filters.twitterAuth) { %> @@ -24,16 +24,6 @@ var UserSchema = new Schema({ /** * Virtuals */ -UserSchema - .virtual('password') - .set(function(password) { - this._password = password; - this.salt = this.makeSalt(); - this.hashedPassword = this.encryptPassword(password); - }) - .get(function() { - return this._password; - }); // Public profile information UserSchema @@ -69,10 +59,10 @@ UserSchema // Validate empty password UserSchema - .path('hashedPassword') - .validate(function(hashedPassword) {<% if (filters.oauth) { %> + .path('password') + .validate(function(password) {<% if (filters.oauth) { %> if (authTypes.indexOf(this.provider) !== -1) return true;<% } %> - return hashedPassword.length; + return password.length; }, 'Password cannot be blank'); // Validate email is not taken @@ -99,12 +89,26 @@ var validatePresenceOf = function(value) { */ UserSchema .pre('save', function(next) { - if (!this.isNew) return next(); - - if (!validatePresenceOf(this.hashedPassword)<% if (filters.oauth) { %> && authTypes.indexOf(this.provider) === -1<% } %>) - next(new Error('Invalid password')); - else + // Handle new/update passwords + if (this.password) { + if (!validatePresenceOf(this.password)<% if (filters.oauth) { %> && authTypes.indexOf(this.provider) === -1<% } %>) + next(new Error('Invalid password')); + + // Make salt with a callback + var _this = this; + this.makeSalt(function(saltErr, salt) { + if (saltErr) next(saltErr); + _this.salt = salt; + // Async hash + _this.encryptPassword(_this.password, function(encryptErr, hashedPassword) { + if (encryptErr) next(encryptErr); + _this.password = hashedPassword; + next(); + }); + }); + } else { next(); + } }); /** @@ -115,34 +119,82 @@ UserSchema.methods = { * Authenticate - check if the passwords are the same * * @param {String} plainText + * @callback {callback} Optional callback * @return {Boolean} * @api public */ - authenticate: function(plainText) { - return this.encryptPassword(plainText) === this.hashedPassword; + authenticate: function(password, callback) { + if (!callback) + return this.password === this.encryptPassword(password); + + var _this = this; + this.encryptPassword(password, function(err, pwdGen) { + if (err) callback(err); + + if (_this.password === pwdGen) { + callback(null, true); + } else { + callback(null, false); + } + }); }, /** * Make salt * + * @param {Number} byteSize Optional salt byte size, default to 16 + * @callback {callback} Optional callback * @return {String} * @api public */ - makeSalt: function() { - return crypto.randomBytes(16).toString('base64'); + makeSalt: function(byteSize, callback) { + var defaultByteSize = 16; + + if (typeof arguments[0] === 'function') { + callback = arguments[0]; + byteSize = defaultByteSize; + } else if (typeof arguments[1] === 'function') { + callback = arguments[1]; + } + + if (!byteSize) { + byteSize = defaultByteSize; + } + + if (!callback) { + return crypto.randomBytes(byteSize).toString('base64'); + } + + return crypto.randomBytes(byteSize, function(err, salt) { + if (err) callback(err); + return callback(null, salt.toString('base64')); + }); }, /** * Encrypt password * * @param {String} password + * @callback {callback} Optional callback * @return {String} * @api public */ - encryptPassword: function(password) { - if (!password || !this.salt) return ''; + encryptPassword: function(password, callback) { + if (!password || !this.salt) { + return null; + } + + var defaultIterations = 10000; + var defaultKeyLength = 64; var salt = new Buffer(this.salt, 'base64'); - return crypto.pbkdf2Sync(password, salt, 10000, 64).toString('base64'); + + if (!callback) + return crypto.pbkdf2Sync(password, salt, defaultIterations, defaultKeyLength).toString('base64'); + + return crypto.pbkdf2(password, salt, defaultIterations, defaultKeyLength, function(err, key) { + if (err) callback(err); + return callback(null, key.toString('base64')); + }); } }; diff --git a/app/templates/server/api/user(auth)/user.model.spec.js b/app/templates/server/api/user(auth)/user.model.spec.js index 257c95b7c..95e8bfbd8 100644 --- a/app/templates/server/api/user(auth)/user.model.spec.js +++ b/app/templates/server/api/user(auth)/user.model.spec.js @@ -4,14 +4,9 @@ var should = require('should'); var app = require('../../app'); var User = require('./user.model'); -var user = new User({ - provider: 'local', - name: 'Fake User', - email: 'test@test.com', - password: 'password' -}); - describe('User Model', function() { + var user; + before(function(done) { // Clear users before testing User.remove().exec().then(function() { @@ -20,6 +15,15 @@ describe('User Model', function() { }); afterEach(function(done) { + // Start from scratch + user = new User({ + provider: 'local', + name: 'Fake User', + email: 'test@test.com', + password: 'password' + }); + + // Clear all users User.remove().exec().then(function() { done(); }); @@ -50,11 +54,34 @@ describe('User Model', function() { }); }); - it("should authenticate user if password is valid", function() { - return user.authenticate('password').should.be.true; + it("should authenticate user if password is valid", function(done) { + user.save(function(err, newUser) { + newUser.authenticate('password', function(authErr, authenticated) { + authenticated.should.be.true; + done(); + }); + }); + }); + + it("should not authenticate user if password is invalid", function(done) { + user.save(function(err, newUser) { + newUser.authenticate('invalidPassword', function(authErr, authenticated) { + authenticated.should.not.be.true; + done(); + }); + }); }); - it("should not authenticate user if password is invalid", function() { - return user.authenticate('blah').should.not.be.true; + it("should authenticate after updating password", function(done) { + user.save(function(err, newUser) { + newUser.password = 'newPassword'; + newUser.save(function() { + newUser.authenticate('newPassword', function(authErr, authenticated) { + authenticated.should.be.true; + done(); + }); + }); + }); }); + }); diff --git a/app/templates/server/auth(auth)/local/passport.js b/app/templates/server/auth(auth)/local/passport.js index ac82b42a2..4221f8786 100644 --- a/app/templates/server/auth(auth)/local/passport.js +++ b/app/templates/server/auth(auth)/local/passport.js @@ -15,11 +15,15 @@ exports.setup = function (User, config) { if (!user) { return done(null, false, { message: 'This email is not registered.' }); } - if (!user.authenticate(password)) { - return done(null, false, { message: 'This password is not correct.' }); - } - return done(null, user); + user.authenticate(password, function(authError, authenticated) { + if (authError) return done(authError); + if (!authenticated) { + return done(null, false, { message: 'This password is not correct.' }); + } else { + return done(null, user); + } + }); }); } )); -}; \ No newline at end of file +}; From de5c36ca3003ea9ef149eb26ca8393d6ab2d8a4a Mon Sep 17 00:00:00 2001 From: kingcody Date: Fri, 29 Aug 2014 11:05:05 -0400 Subject: [PATCH 003/171] refactor(server-tests): use sinon-chai and `mocha.conf.js` Changes: - add `mocha.conf.js` and use it as a `require` in `mochaTest` task - switch `should.js` for `mocha-chai` - change server-side test assertions to user chai assertions Breaking Changes: - should.js is no longer included, chai assertions should be used instead. --- app/templates/Gruntfile.js | 3 ++- app/templates/_package.json | 3 ++- app/templates/mocha.conf.js | 12 +++++++++++ app/templates/server/api/thing/thing.spec.js | 3 +-- .../server/api/user(auth)/user.model.spec.js | 20 ++++++++----------- endpoint/templates/name.spec.js | 5 ++--- test/fixtures/package.json | 3 ++- 7 files changed, 29 insertions(+), 20 deletions(-) create mode 100644 app/templates/mocha.conf.js diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index 998ca5620..d98992888 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -486,7 +486,8 @@ module.exports = function (grunt) { mochaTest: { options: { - reporter: 'spec' + reporter: 'spec', + require: 'mocha.conf.js' }, src: ['server/**/*.spec.js'] }, diff --git a/app/templates/_package.json b/app/templates/_package.json index e32849740..7b2cf48d5 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -30,6 +30,7 @@ "socketio-jwt": "^2.0.2"<% } %> }, "devDependencies": { + "chai-as-promised": "^4.1.1", "grunt": "~0.4.4", "grunt-autoprefixer": "~0.7.2", "grunt-wiredep": "~1.8.0", @@ -86,7 +87,7 @@ "karma": "~0.12.9", "karma-ng-html2js-preprocessor": "~0.1.0", "supertest": "~0.11.0", - "should": "~3.3.1" + "sinon-chai": "^2.5.0" }, "engines": { "node": ">=0.10.0" diff --git a/app/templates/mocha.conf.js b/app/templates/mocha.conf.js new file mode 100644 index 000000000..7bfaa573f --- /dev/null +++ b/app/templates/mocha.conf.js @@ -0,0 +1,12 @@ +'use strict'; + +var chai = require('chai'); +var sinon = require('sinon'); +var sinonChai = require('sinon-chai'); +var chaiAsPromised = require('chai-as-promised'); + +global.expect = chai.expect; +global.assert = chai.assert; +chai.should(); +chai.use(sinonChai); +chai.use(chaiAsPromised); diff --git a/app/templates/server/api/thing/thing.spec.js b/app/templates/server/api/thing/thing.spec.js index 17c8c6cd0..3f135445b 100644 --- a/app/templates/server/api/thing/thing.spec.js +++ b/app/templates/server/api/thing/thing.spec.js @@ -1,6 +1,5 @@ 'use strict'; -var should = require('should'); var app = require('../../app'); var request = require('supertest'); @@ -13,7 +12,7 @@ describe('GET /api/things', function() { .expect('Content-Type', /json/) .end(function(err, res) { if (err) return done(err); - res.body.should.be.instanceof(Array); + res.body.should.be.instanceOf(Array); done(); }); }); diff --git a/app/templates/server/api/user(auth)/user.model.spec.js b/app/templates/server/api/user(auth)/user.model.spec.js index 257c95b7c..8960ca859 100644 --- a/app/templates/server/api/user(auth)/user.model.spec.js +++ b/app/templates/server/api/user(auth)/user.model.spec.js @@ -1,6 +1,5 @@ 'use strict'; -var should = require('should'); var app = require('../../app'); var User = require('./user.model'); @@ -12,17 +11,14 @@ var user = new User({ }); describe('User Model', function() { - before(function(done) { - // Clear users before testing - User.remove().exec().then(function() { - done(); - }); + + // Clear users before testing + before(function() { + return User.remove().exec(); }); - afterEach(function(done) { - User.remove().exec().then(function() { - done(); - }); + afterEach(function() { + return User.remove().exec(); }); it('should begin with no users', function(done) { @@ -36,7 +32,7 @@ describe('User Model', function() { user.save(function() { var userDup = new User(user); userDup.save(function(err) { - should.exist(err); + err.should.be.instanceOf(Error); done(); }); }); @@ -45,7 +41,7 @@ describe('User Model', function() { it('should fail when saving without an email', function(done) { user.email = ''; user.save(function(err) { - should.exist(err); + err.should.be.instanceOf(Error); done(); }); }); diff --git a/endpoint/templates/name.spec.js b/endpoint/templates/name.spec.js index fcad73ebd..d287bafe5 100644 --- a/endpoint/templates/name.spec.js +++ b/endpoint/templates/name.spec.js @@ -1,6 +1,5 @@ 'use strict'; -var should = require('should'); var app = require('../../app'); var request = require('supertest'); @@ -13,8 +12,8 @@ describe('GET <%= route %>', function() { .expect('Content-Type', /json/) .end(function(err, res) { if (err) return done(err); - res.body.should.be.instanceof(Array); + res.body.should.be.instanceOf(Array); done(); }); }); -}); \ No newline at end of file +}); diff --git a/test/fixtures/package.json b/test/fixtures/package.json index ce4df22f9..9bdfd30ff 100644 --- a/test/fixtures/package.json +++ b/test/fixtures/package.json @@ -30,6 +30,7 @@ "socketio-jwt": "^2.0.2" }, "devDependencies": { + "chai-as-promised": "^4.1.1", "grunt": "~0.4.4", "grunt-autoprefixer": "~0.7.2", "grunt-wiredep": "~1.8.0", @@ -86,7 +87,7 @@ "karma": "~0.12.9", "karma-ng-html2js-preprocessor": "~0.1.0", "supertest": "~0.11.0", - "should": "~3.3.1" + "sinon-chai": "^2.5.0" }, "engines": { "node": ">=0.10.0" From dbbaa20b47629be60de8ce5f31a7c91abf9bdaef Mon Sep 17 00:00:00 2001 From: kingcody Date: Sat, 30 Aug 2014 07:53:03 -0400 Subject: [PATCH 004/171] feat(server-tests): code coverage and e2e Changes: - split server tests into `unit` and `e2e` type test - adjust `mochaTest` and `test:server` tasks to reflect the two types of tests - implement `grunt-mocha-istanbul` task for server-side code coverage - add `test:coverage` task - improve `mocha.conf.js` - add sinon, expect, and assert as globals to `server/.jshintrc-spec` - add `proxyquire` to the app for better stubbing in unit tests - improve test to reflect recent changes and to lay ground work for more coverage Grunt Task `test`: The grunt 'test' task now has a 'coverage' target. Used like so `grunt test:coverage`. This task will run istanbul for the server unit tests and e2e test separately. The resulting coverage reports will be placed in `coverage/(unit|e2e)`. There is also an option for the `test:coverage` task, possibilities for option are: - `unit` - `e2e` - `check` `test:coverage:check` will check the coverage reports for both `unit` and `e2e` and report whether or not they meet the required coverage. --- app/templates/Gruntfile.js | 94 +++++++++++- app/templates/_package.json | 4 +- app/templates/mocha.conf.js | 2 + app/templates/server/.jshintrc-spec | 5 +- app/templates/server/api/thing/index.js | 2 +- app/templates/server/api/thing/index.spec.js | 85 +++++++++++ .../server/api/thing/thing.controller.js | 2 +- app/templates/server/api/thing/thing.e2e.js | 135 ++++++++++++++++++ .../server/api/thing/thing.model(mongoose).js | 2 +- .../api/thing/thing.socket(socketio).js | 2 +- app/templates/server/api/thing/thing.spec.js | 19 --- app/templates/server/api/user(auth)/index.js | 1 - .../server/api/user(auth)/index.spec.js | 95 ++++++++++++ .../server/api/user(auth)/user.e2e.js | 68 +++++++++ .../server/api/user(auth)/user.model.spec.js | 66 +++++---- endpoint/templates/index.spec.js | 85 +++++++++++ endpoint/templates/name.e2e.js | 135 ++++++++++++++++++ endpoint/templates/name.spec.js | 19 --- test/fixtures/package.json | 2 + 19 files changed, 748 insertions(+), 75 deletions(-) create mode 100644 app/templates/server/api/thing/index.spec.js create mode 100644 app/templates/server/api/thing/thing.e2e.js delete mode 100644 app/templates/server/api/thing/thing.spec.js create mode 100644 app/templates/server/api/user(auth)/index.spec.js create mode 100644 app/templates/server/api/user(auth)/user.e2e.js create mode 100644 endpoint/templates/index.spec.js create mode 100644 endpoint/templates/name.e2e.js delete mode 100644 endpoint/templates/name.spec.js diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index d98992888..26866c21d 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -169,14 +169,14 @@ module.exports = function (grunt) { }, src: [ 'server/**/*.js', - '!server/**/*.spec.js' + '!server/**/*.{spec,e2e}.js' ] }, serverTest: { options: { jshintrc: 'server/.jshintrc-spec' }, - src: ['server/**/*.spec.js'] + src: ['server/**/*.{spec,e2e}.js'] }, all: [ '<%%= yeoman.client %>/{app,components}/**/*.js', @@ -489,7 +489,57 @@ module.exports = function (grunt) { reporter: 'spec', require: 'mocha.conf.js' }, - src: ['server/**/*.spec.js'] + unit: { + src: ['server/**/*.spec.js'] + }, + e2e: { + src: ['server/**/*.e2e.js'] + } + }, + + mocha_istanbul: { + unit: { + options: { + excludes: [ + '**/*.spec.js', + '**/*.mock.js', + '**/*.e2e.js' + ], + reporter: 'spec', + require: ['mocha.conf.js'], + mask: '**/*.spec.js', + coverageFolder: 'coverage/unit' + }, + src: 'server' + }, + e2e: { + options: { + excludes: [ + '**/*.spec.js', + '**/*.mock.js', + '**/*.e2e.js' + ], + reporter: 'spec', + require: ['mocha.conf.js'], + mask: '**/*.e2e.js', + coverageFolder: 'coverage/e2e' + }, + src: 'server' + } + }, + + istanbul_check_coverage: { + default: { + options: { + coverageFolder: 'coverage/*', + check: { + lines: 80, + statements: 80, + branches: 80, + functions: 80 + } + } + } }, protractor: { @@ -770,7 +820,8 @@ module.exports = function (grunt) { return grunt.task.run([ 'env:all', 'env:test', - 'mochaTest' + 'mochaTest:unit', + 'mochaTest:e2e' ]); } @@ -805,6 +856,41 @@ module.exports = function (grunt) { ]); } + else if (target === 'coverage') { + + if (option === 'unit') { + return grunt.task.run([ + 'env:all', + 'env:test', + 'mocha_istanbul:unit' + ]); + } + + else if (option === 'e2e') { + return grunt.task.run([ + 'env:all', + 'env:test', + 'mocha_istanbul:e2e' + ]); + } + + else if (option === 'check') { + return grunt.task.run([ + 'istanbul_check_coverage' + ]); + } + + else { + return grunt.task.run([ + 'env:all', + 'env:test', + 'mocha_istanbul', + 'istanbul_check_coverage' + ]); + } + + } + else grunt.task.run([ 'test:server', 'test:client' diff --git a/app/templates/_package.json b/app/templates/_package.json index 7b2cf48d5..9dfdadc5d 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -62,7 +62,8 @@ "grunt-asset-injector": "^0.1.0", "grunt-karma": "~0.8.2", "grunt-build-control": "DaftMonk/grunt-build-control", - "grunt-mocha-test": "~0.10.2",<% if(filters.sass) { %> + "grunt-mocha-test": "~0.10.2", + "grunt-mocha-istanbul": "^2.0.0",<% if(filters.sass) { %> "grunt-contrib-sass": "^0.7.3",<% } %><% if(filters.stylus) { %> "grunt-contrib-stylus": "latest",<% } %> "jit-grunt": "^0.5.0", @@ -86,6 +87,7 @@ "karma-phantomjs-launcher": "~0.1.4", "karma": "~0.12.9", "karma-ng-html2js-preprocessor": "~0.1.0", + "proxyquire": "^1.0.1", "supertest": "~0.11.0", "sinon-chai": "^2.5.0" }, diff --git a/app/templates/mocha.conf.js b/app/templates/mocha.conf.js index 7bfaa573f..497d43b2c 100644 --- a/app/templates/mocha.conf.js +++ b/app/templates/mocha.conf.js @@ -7,6 +7,8 @@ var chaiAsPromised = require('chai-as-promised'); global.expect = chai.expect; global.assert = chai.assert; +global.sinon = sinon; + chai.should(); chai.use(sinonChai); chai.use(chaiAsPromised); diff --git a/app/templates/server/.jshintrc-spec b/app/templates/server/.jshintrc-spec index b6b55cbf9..b9390c374 100644 --- a/app/templates/server/.jshintrc-spec +++ b/app/templates/server/.jshintrc-spec @@ -6,6 +6,9 @@ "before": true, "beforeEach": true, "after": true, - "afterEach": true + "afterEach": true, + "expect": true, + "assert": true, + "sinon": true } } diff --git a/app/templates/server/api/thing/index.js b/app/templates/server/api/thing/index.js index 242ed5901..e77e80c5b 100644 --- a/app/templates/server/api/thing/index.js +++ b/app/templates/server/api/thing/index.js @@ -12,4 +12,4 @@ router.put('/:id', controller.update); router.patch('/:id', controller.update); router.delete('/:id', controller.destroy);<% } %> -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/app/templates/server/api/thing/index.spec.js b/app/templates/server/api/thing/index.spec.js new file mode 100644 index 000000000..0fbfb1029 --- /dev/null +++ b/app/templates/server/api/thing/index.spec.js @@ -0,0 +1,85 @@ +'use strict'; + +var proxyquire = require('proxyquire').noPreserveCache(); + + /* thing.controller stub */ +var thingCtrl = { + index: 'thingCtrl.index'<% if(filters.mongoose) { %>, + show: 'thingCtrl.show', + create: 'thingCtrl.create', + update: 'thingCtrl.update', + destroy: 'thingCtrl.destroy'<% } %> + }, + /* express.Router().router stub */ + router = { + get: sinon.spy()<% if(filters.mongoose) { %>, + put: sinon.spy(), + patch: sinon.spy(), + post: sinon.spy(), + delete: sinon.spy()<% } %> + }, + /* stubbed thing router */ + index = proxyquire('./index.js', { + 'express': { + Router: function() { + return router; + } + }, + './thing.controller': thingCtrl + }); + +describe('Thing API Router:', function() { + + it('should return an express router instance', function() { + index.should.equal(router); + }); + + describe('GET /api/things', function() { + + it('should route to thing.controller.index', function() { + return router.get.withArgs('/', 'thingCtrl.index').should.have.been.calledOnce; + }); + + });<% if(filters.mongoose) { %> + + describe('GET /api/things/:id', function() { + + it('should route to thing.controller.show', function() { + return router.get.withArgs('/:id', 'thingCtrl.show').should.have.been.calledOnce; + }); + + }); + + describe('POST /api/things', function() { + + it('should route to thing.controller.create', function() { + return router.post.withArgs('/', 'thingCtrl.create').should.have.been.calledOnce; + }); + + }); + + describe('PUT /api/things/:id', function() { + + it('should route to thing.controller.update', function() { + return router.put.withArgs('/:id', 'thingCtrl.update').should.have.been.calledOnce; + }); + + }); + + describe('PATCH /api/things/:id', function() { + + it('should route to thing.controller.update', function() { + return router.patch.withArgs('/:id', 'thingCtrl.update').should.have.been.calledOnce; + }); + + }); + + describe('DELETE /api/things/:id', function() { + + it('should route to thing.controller.destroy', function() { + return router.delete.withArgs('/:id', 'thingCtrl.destroy').should.have.been.calledOnce; + }); + + });<% } %> + +}); diff --git a/app/templates/server/api/thing/thing.controller.js b/app/templates/server/api/thing/thing.controller.js index ba84d6fc9..72a678cf4 100644 --- a/app/templates/server/api/thing/thing.controller.js +++ b/app/templates/server/api/thing/thing.controller.js @@ -86,4 +86,4 @@ exports.destroy = function(req, res) { function handleError(res, err) { return res.send(500, err); -}<% } %> \ No newline at end of file +}<% } %> diff --git a/app/templates/server/api/thing/thing.e2e.js b/app/templates/server/api/thing/thing.e2e.js new file mode 100644 index 000000000..1140df7e9 --- /dev/null +++ b/app/templates/server/api/thing/thing.e2e.js @@ -0,0 +1,135 @@ +'use strict'; + +var app = require('../../app'); +var request = require('supertest');<% if(filters.mongoose) { %> + +var newThing;<% } %> + +describe('Thing API:', function() { + + describe('GET /api/things', function() { + var things; + + beforeEach(function(done) { + request(app) + .get('/api/things') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res) { + if (err) return done(err); + things = res.body; + done(); + }); + }); + + it('should respond with JSON array', function() { + things.should.be.instanceOf(Array); + }); + + });<% if(filters.mongoose) { %> + + describe('POST /api/things', function() { + beforeEach(function(done) { + request(app) + .post('/api/things') + .send({ + name: 'New Thing', + info: 'This is the brand new thing!!!' + }) + .expect(201) + .expect('Content-Type', /json/) + .end(function(err, res) { + if (err) return done(err); + newThing = res.body; + done(); + }); + }); + + it('should respond with the newly created thing', function() { + newThing.name.should.equal('New Thing'); + newThing.info.should.equal('This is the brand new thing!!!'); + }); + + }); + + describe('GET /api/things/:id', function() { + var thing; + + beforeEach(function(done) { + request(app) + .get('/api/things/' + newThing._id) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res) { + if (err) return done(err); + thing = res.body; + done(); + }); + }); + + afterEach(function() { + thing = {}; + }); + + it('should respond with the requested thing', function() { + thing.name.should.equal('New Thing'); + thing.info.should.equal('This is the brand new thing!!!'); + }); + + }); + + describe('PUT /api/things/:id', function() { + var updatedThing + + beforeEach(function(done) { + request(app) + .put('/api/things/' + newThing._id) + .send({ + name: 'Updated Thing', + info: 'This is the updated thing!!!' + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res) { + if (err) return done(err); + updatedThing = res.body; + done(); + }); + }); + + afterEach(function() { + updatedThing = {}; + }); + + it('should respond with the updated thing', function() { + updatedThing.name.should.equal('Updated Thing'); + updatedThing.info.should.equal('This is the updated thing!!!'); + }); + + }); + + describe('DELETE /api/things/:id', function() { + + it('should respond with 204 on successful removal', function(done) { + request(app) + .delete('/api/things/' + newThing._id) + .expect(204) + .end(function(err, res) { + if (err) return done(err); + done(); + }); + }); + + it('should respond with 404 when thing does not exsist', function(done) { + request(app) + .delete('/api/things/' + newThing._id) + .expect(404) + .end(function(err, res) { + if (err) return done(err); + done(); + }); + }); + + });<% } %> + +}); diff --git a/app/templates/server/api/thing/thing.model(mongoose).js b/app/templates/server/api/thing/thing.model(mongoose).js index ed857cd3b..92a791e70 100644 --- a/app/templates/server/api/thing/thing.model(mongoose).js +++ b/app/templates/server/api/thing/thing.model(mongoose).js @@ -9,4 +9,4 @@ var ThingSchema = new Schema({ active: Boolean }); -module.exports = mongoose.model('Thing', ThingSchema); \ No newline at end of file +module.exports = mongoose.model('Thing', ThingSchema); diff --git a/app/templates/server/api/thing/thing.socket(socketio).js b/app/templates/server/api/thing/thing.socket(socketio).js index 79d327695..dbf3e2fe7 100644 --- a/app/templates/server/api/thing/thing.socket(socketio).js +++ b/app/templates/server/api/thing/thing.socket(socketio).js @@ -21,4 +21,4 @@ function onSave(socket, doc, cb) { function onRemove(socket, doc, cb) { socket.emit('thing:remove', doc); -} \ No newline at end of file +} diff --git a/app/templates/server/api/thing/thing.spec.js b/app/templates/server/api/thing/thing.spec.js deleted file mode 100644 index 3f135445b..000000000 --- a/app/templates/server/api/thing/thing.spec.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -var app = require('../../app'); -var request = require('supertest'); - -describe('GET /api/things', function() { - - it('should respond with JSON array', function(done) { - request(app) - .get('/api/things') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) return done(err); - res.body.should.be.instanceOf(Array); - done(); - }); - }); -}); diff --git a/app/templates/server/api/user(auth)/index.js b/app/templates/server/api/user(auth)/index.js index 48567e485..be6fd3af3 100644 --- a/app/templates/server/api/user(auth)/index.js +++ b/app/templates/server/api/user(auth)/index.js @@ -2,7 +2,6 @@ var express = require('express'); var controller = require('./user.controller'); -var config = require('../../config/environment'); var auth = require('../../auth/auth.service'); var router = express.Router(); diff --git a/app/templates/server/api/user(auth)/index.spec.js b/app/templates/server/api/user(auth)/index.spec.js new file mode 100644 index 000000000..5bcd4c2c0 --- /dev/null +++ b/app/templates/server/api/user(auth)/index.spec.js @@ -0,0 +1,95 @@ +'use strict'; + +var proxyquire = require('proxyquire').noPreserveCache(); + + /* user.controller stub */ +var userCtrl = { + index: 'userCtrl.index', + destroy: 'userCtrl.destroy', + me: 'userCtrl.me', + changePassword: 'userCtrl.changePassword', + show: 'userCtrl.show', + create: 'userCtrl.create' + }, + /* auth.service stub */ + authService = { + isAuthenticated: function() { + return 'authService.isAuthenticated'; + }, + hasRole: function(role) { + return 'authService.hasRole.' + role; + } + }, + /* express.Router().router stub */ + router = { + get: sinon.spy(), + put: sinon.spy(), + post: sinon.spy(), + delete: sinon.spy() + }, + /* stubbed user router */ + index = proxyquire('./index', { + 'express': { + Router: function() { + return router; + } + }, + './user.controller': userCtrl, + '../../auth/auth.service': authService + }); + +describe('User API Router:', function() { + + it('should return an express router instance', function() { + index.should.equal(router); + }); + + describe('GET /api/users', function() { + + it('should verify admin role and route to user.controller.index', function() { + return router.get.withArgs('/', 'authService.hasRole.admin', 'userCtrl.index').should.have.been.calledOnce; + }); + + }); + + describe('DELETE /api/users/:id', function() { + + it('should verify admin role and route to user.controller.destroy', function() { + return router.delete.withArgs('/:id', 'authService.hasRole.admin', 'userCtrl.destroy').should.have.been.calledOnce; + }); + + }); + + describe('GET /api/users/me', function() { + + it('should be authenticated and route to user.controller.me', function() { + return router.get.withArgs('/me', 'authService.isAuthenticated', 'userCtrl.me').should.have.been.calledOnce; + }); + + }); + + describe('PUT /api/users/:id/password', function() { + + it('should be authenticated and route to user.controller.changePassword', function() { + return router.put.withArgs('/:id/password', 'authService.isAuthenticated', 'userCtrl.changePassword').should.have.been.calledOnce; + }); + + }); + + describe('GET /api/users/:id', function() { + + it('should be authenticated and route to user.controller.show', function() { + return router.get.withArgs('/:id', 'authService.isAuthenticated', 'userCtrl.show').should.have.been.calledOnce; + }); + + }); + + describe('POST /api/users', function() { + + it('should route to user.controller.create', function() { + return router.post.withArgs('/', 'userCtrl.create').should.have.been.calledOnce; + }); + + }); + +}); diff --git a/app/templates/server/api/user(auth)/user.e2e.js b/app/templates/server/api/user(auth)/user.e2e.js new file mode 100644 index 000000000..917acedd9 --- /dev/null +++ b/app/templates/server/api/user(auth)/user.e2e.js @@ -0,0 +1,68 @@ +'use strict'; + +var app = require('../../app'); +var User = require('./user.model'); +var request = require('supertest'); + +describe('User API:', function() { + var user; + + // Clear users before testing + before(function(done) { + User.remove(function() { + user = new User({ + name: 'Fake User', + email: 'test@test.com', + password: 'password' + }); + + user.save(function(err) { + if (err) return done(err); + done(); + }); + }); + }); + + // Clear users after testing + after(function() { + return User.remove().exec(); + }); + + describe('GET /api/users/me', function() { + var token; + + before(function(done) { + request(app) + .post('/auth/local') + .send({ + email: 'test@test.com', + password: 'password' + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res) { + token = res.body.token; + done(); + }); + }); + + it('should respond with a user profile when authenticated', function(done) { + request(app) + .get('/api/users/me') + .set('authorization', 'Bearer ' + token) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res) { + res.body._id.should.equal(user._id.toString()); + done(); + }); + }); + + it('should respond with a 401 when not authenticated', function(done) { + request(app) + .get('/api/users/me') + .expect(401) + .end(done); + }); + }); +}); diff --git a/app/templates/server/api/user(auth)/user.model.spec.js b/app/templates/server/api/user(auth)/user.model.spec.js index 8960ca859..0df9a2d5b 100644 --- a/app/templates/server/api/user(auth)/user.model.spec.js +++ b/app/templates/server/api/user(auth)/user.model.spec.js @@ -10,47 +10,61 @@ var user = new User({ password: 'password' }); -describe('User Model', function() { +describe('User Model:', function() { // Clear users before testing before(function() { return User.remove().exec(); }); - afterEach(function() { - return User.remove().exec(); - }); + describe('User (schema)', function() { - it('should begin with no users', function(done) { - User.find({}, function(err, users) { - users.should.have.length(0); - done(); + it('should begin with no users', function() { + return User.find({}).exec().should.eventually.have.length(0); }); + }); - it('should fail when saving a duplicate user', function(done) { - user.save(function() { - var userDup = new User(user); - userDup.save(function(err) { - err.should.be.instanceOf(Error); - done(); + describe('user (instance)', function() { + + describe('.save()', function() { + // Clear users after tests + afterEach(function() { + return User.remove().exec(); }); + + it('should fail when saving a duplicate user', function(done) { + user.save(function() { + var userDup = new User(user); + userDup.save(function(err) { + err.should.be.instanceOf(Error); + done(); + }); + }); + }); + + it('should fail when saving without an email', function(done) { + user.email = ''; + user.save(function(err) { + err.should.be.instanceOf(Error); + done(); + }); + }); + }); - }); - it('should fail when saving without an email', function(done) { - user.email = ''; - user.save(function(err) { - err.should.be.instanceOf(Error); - done(); + describe('.authenticate()', function() { + + it("should authenticate user if password is valid", function() { + return user.authenticate('password').should.be.true; + }); + + it("should not authenticate user if password is invalid", function() { + return user.authenticate('blah').should.not.be.true; + }); + }); - }); - it("should authenticate user if password is valid", function() { - return user.authenticate('password').should.be.true; }); - it("should not authenticate user if password is invalid", function() { - return user.authenticate('blah').should.not.be.true; - }); }); diff --git a/endpoint/templates/index.spec.js b/endpoint/templates/index.spec.js new file mode 100644 index 000000000..62caed5dc --- /dev/null +++ b/endpoint/templates/index.spec.js @@ -0,0 +1,85 @@ +'use strict'; + +var proxyquire = require('proxyquire').noPreserveCache(); + + /* <%= name %>.controller stub */ +var <%= name %>Ctrl = { + index: '<%= name %>Ctrl.index'<% if(filters.mongoose) { %>, + show: '<%= name %>Ctrl.show', + create: '<%= name %>Ctrl.create', + update: '<%= name %>Ctrl.update', + destroy: '<%= name %>Ctrl.destroy'<% } %> + }, + /* express.Router().router stub */ + router = { + get: sinon.spy()<% if(filters.mongoose) { %>, + put: sinon.spy(), + patch: sinon.spy(), + post: sinon.spy(), + delete: sinon.spy()<% } %> + }, + /* stubbed <%= name %> router */ + index = proxyquire('./index.js', { + 'express': { + Router: function() { + return router; + } + }, + './<%= name %>.controller': <%= name %>Ctrl + }); + +describe('<%= classedName %> API Router:', function() { + + it('should return an express router instance', function() { + index.should.equal(router); + }); + + describe('GET <%= route %>', function() { + + it('should route to <%= name %>.controller.index', function() { + return router.get.withArgs('/', '<%= name %>Ctrl.index').should.have.been.calledOnce; + }); + + });<% if(filters.mongoose) { %> + + describe('GET <%= route %>/:id', function() { + + it('should route to <%= name %>.controller.show', function() { + return router.get.withArgs('/:id', '<%= name %>Ctrl.show').should.have.been.calledOnce; + }); + + }); + + describe('POST <%= route %>', function() { + + it('should route to <%= name %>.controller.create', function() { + return router.post.withArgs('/', '<%= name %>Ctrl.create').should.have.been.calledOnce; + }); + + }); + + describe('PUT <%= route %>/:id', function() { + + it('should route to <%= name %>.controller.update', function() { + return router.put.withArgs('/:id', '<%= name %>Ctrl.update').should.have.been.calledOnce; + }); + + }); + + describe('PATCH <%= route %>/:id', function() { + + it('should route to <%= name %>.controller.update', function() { + return router.patch.withArgs('/:id', '<%= name %>Ctrl.update').should.have.been.calledOnce; + }); + + }); + + describe('DELETE <%= route %>/:id', function() { + + it('should route to <%= name %>.controller.destroy', function() { + return router.delete.withArgs('/:id', '<%= name %>Ctrl.destroy').should.have.been.calledOnce; + }); + + });<% } %> + +}); diff --git a/endpoint/templates/name.e2e.js b/endpoint/templates/name.e2e.js new file mode 100644 index 000000000..5960b40a2 --- /dev/null +++ b/endpoint/templates/name.e2e.js @@ -0,0 +1,135 @@ +'use strict'; + +var app = require('../../app'); +var request = require('supertest');<% if(filters.mongoose) { %> + +var new<%= classedName %>;<% } %> + +describe('<%= classedName %> API:', function() { + + describe('GET <%= route %>', function() { + var <%= name %>s; + + beforeEach(function(done) { + request(app) + .get('<%= route %>') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res) { + if (err) return done(err); + <%= name %>s = res.body; + done(); + }); + }); + + it('should respond with JSON array', function() { + <%= name %>s.should.be.instanceOf(Array); + }); + + });<% if(filters.mongoose) { %> + + describe('POST <%= route %>', function() { + beforeEach(function(done) { + request(app) + .post('<%= route %>') + .send({ + name: 'New <%= classedName %>', + info: 'This is the brand new <%= name %>!!!' + }) + .expect(201) + .expect('Content-Type', /json/) + .end(function(err, res) { + if (err) return done(err); + new<%= classedName %> = res.body; + done(); + }); + }); + + it('should respond with the newly created <%= name %>', function() { + new<%= classedName %>.name.should.equal('New <%= classedName %>'); + new<%= classedName %>.info.should.equal('This is the brand new <%= name %>!!!'); + }); + + }); + + describe('GET <%= route %>/:id', function() { + var <%= name %>; + + beforeEach(function(done) { + request(app) + .get('<%= route %>/' + new<%= classedName %>._id) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res) { + if (err) return done(err); + <%= name %> = res.body; + done(); + }); + }); + + afterEach(function() { + <%= name %> = {}; + }); + + it('should respond with the requested <%= name %>', function() { + <%= name %>.name.should.equal('New <%= classedName %>'); + <%= name %>.info.should.equal('This is the brand new <%= name %>!!!'); + }); + + }); + + describe('PUT <%= route %>/:id', function() { + var updated<%= classedName %> + + beforeEach(function(done) { + request(app) + .put('<%= route %>/' + new<%= classedName %>._id) + .send({ + name: 'Updated <%= classedName %>', + info: 'This is the updated <%= name %>!!!' + }) + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res) { + if (err) return done(err); + updated<%= classedName %> = res.body; + done(); + }); + }); + + afterEach(function() { + updated<%= classedName %> = {}; + }); + + it('should respond with the updated <%= name %>', function() { + updated<%= classedName %>.name.should.equal('Updated <%= classedName %>'); + updated<%= classedName %>.info.should.equal('This is the updated <%= name %>!!!'); + }); + + }); + + describe('DELETE <%= route %>/:id', function() { + + it('should respond with 204 on successful removal', function(done) { + request(app) + .delete('<%= route %>/' + new<%= classedName %>._id) + .expect(204) + .end(function(err, res) { + if (err) return done(err); + done(); + }); + }); + + it('should respond with 404 when <%= name %> does not exsist', function(done) { + request(app) + .delete('<%= route %>/' + new<%= classedName %>._id) + .expect(404) + .end(function(err, res) { + if (err) return done(err); + done(); + }); + }); + + });<% } %> + +}); diff --git a/endpoint/templates/name.spec.js b/endpoint/templates/name.spec.js deleted file mode 100644 index d287bafe5..000000000 --- a/endpoint/templates/name.spec.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -var app = require('../../app'); -var request = require('supertest'); - -describe('GET <%= route %>', function() { - - it('should respond with JSON array', function(done) { - request(app) - .get('<%= route %>') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) return done(err); - res.body.should.be.instanceOf(Array); - done(); - }); - }); -}); diff --git a/test/fixtures/package.json b/test/fixtures/package.json index 9bdfd30ff..2304c008e 100644 --- a/test/fixtures/package.json +++ b/test/fixtures/package.json @@ -63,6 +63,7 @@ "grunt-karma": "~0.8.2", "grunt-build-control": "DaftMonk/grunt-build-control", "grunt-mocha-test": "~0.10.2", + "grunt-mocha-istanbul": "^2.0.0", "grunt-contrib-sass": "^0.7.3", "grunt-contrib-stylus": "latest", "jit-grunt": "^0.5.0", @@ -86,6 +87,7 @@ "karma-phantomjs-launcher": "~0.1.4", "karma": "~0.12.9", "karma-ng-html2js-preprocessor": "~0.1.0", + "proxyquire": "^1.0.1", "supertest": "~0.11.0", "sinon-chai": "^2.5.0" }, From 5198685150001a138230c888691d52fcec3e7a6c Mon Sep 17 00:00:00 2001 From: kingcody Date: Sat, 30 Aug 2014 15:26:37 -0400 Subject: [PATCH 005/171] fix(server-tests): `test:coverage` task Changes: - add missing `option` argument from `test` task - define `istanbul_check_coverage` with `jit-grunt` - place server coverage reports in their own folder: `coverage/server` --- app/templates/Gruntfile.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index 26866c21d..a72f42dae 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -17,7 +17,8 @@ module.exports = function (grunt) { cdnify: 'grunt-google-cdn', protractor: 'grunt-protractor-runner', injector: 'grunt-asset-injector', - buildcontrol: 'grunt-build-control' + buildcontrol: 'grunt-build-control', + istanbul_check_coverage: 'grunt-mocha-istanbul' }); // Time how long tasks take. Can help when optimizing build times @@ -508,7 +509,7 @@ module.exports = function (grunt) { reporter: 'spec', require: ['mocha.conf.js'], mask: '**/*.spec.js', - coverageFolder: 'coverage/unit' + coverageFolder: 'coverage/server/unit' }, src: 'server' }, @@ -522,7 +523,7 @@ module.exports = function (grunt) { reporter: 'spec', require: ['mocha.conf.js'], mask: '**/*.e2e.js', - coverageFolder: 'coverage/e2e' + coverageFolder: 'coverage/server/e2e' }, src: 'server' } @@ -531,7 +532,7 @@ module.exports = function (grunt) { istanbul_check_coverage: { default: { options: { - coverageFolder: 'coverage/*', + coverageFolder: 'coverage/**', check: { lines: 80, statements: 80, @@ -815,7 +816,7 @@ module.exports = function (grunt) { grunt.task.run(['serve']); }); - grunt.registerTask('test', function(target) { + grunt.registerTask('test', function(target, option) { if (target === 'server') { return grunt.task.run([ 'env:all', From c0b0c6f41672986c66a4c9e729b97f1ceae9121c Mon Sep 17 00:00:00 2001 From: kingcody Date: Sat, 30 Aug 2014 14:49:38 -0400 Subject: [PATCH 006/171] docs(server-tests): update readme.md for new `test:coverage` task Changes: - add documentation for `grunt test:coverage` --- readme.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/readme.md b/readme.md index da6a851d1..a9997f528 100644 --- a/readme.md +++ b/readme.md @@ -338,6 +338,19 @@ To setup protractor e2e tests, you must first run Use `grunt test:e2e` to have protractor go through tests located in the `e2e` folder. +**Code Coverage** + +Use `grunt test:coverage` to run mocha-istanbul and generate code coverage reports. + +`coverage/server` will be populated with `e2e` and `unit` folders containing the `lcov` reports. + +The coverage taget has 3 available options: +- `test:coverage:unit` generate server unit test coverage +- `test:coverage:e2e` generate server e2e test coverage +- `test:coverage:check` combine the coverage reports and check against predefined thresholds + +* *when no option is given `test:coverage` runs all options in the above order* + ## Environment Variables Keeping your app secrets and other sensitive information in source control isn't a good idea. To have grunt launch your app with specific environment variables, add them to the git ignored environment config file: `server/config/local.env.js`. From 7045907c849427a16c3aacaf5fd70d14430be09d Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 31 Aug 2014 20:48:46 -0600 Subject: [PATCH 007/171] fix user model test --- app/templates/server/api/user(auth)/user.model.spec.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/templates/server/api/user(auth)/user.model.spec.js b/app/templates/server/api/user(auth)/user.model.spec.js index 952fc5461..afe8af882 100644 --- a/app/templates/server/api/user(auth)/user.model.spec.js +++ b/app/templates/server/api/user(auth)/user.model.spec.js @@ -64,12 +64,5 @@ describe('User Model:', function() { }); }); - - it("should authenticate user if password is valid", function() { - return user.authenticate('password').should.be.true; - }); - - it("should not authenticate user if password is invalid", function() { - return user.authenticate('blah').should.not.be.true; }); }); From 1ca7448fc05c369180c667077918cc2156c308a9 Mon Sep 17 00:00:00 2001 From: Rafael Almeida Date: Mon, 25 Aug 2014 17:40:12 +1200 Subject: [PATCH 008/171] feat (mongoose): use mongoose-bird to promisify the mongoose API --- app/templates/_package.json | 6 +- app/templates/server/api/thing/index.spec.js | 12 +- .../server/api/thing/thing.controller.js | 114 ++++++++++++------ .../server/api/thing/thing.model(mongoose).js | 2 +- .../server/api/user(auth)/index.spec.js | 12 +- .../server/api/user(auth)/user.controller.js | 95 +++++++++------ .../server/api/user(auth)/user.model.js | 31 +++-- .../server/api/user(auth)/user.model.spec.js | 68 ++++------- app/templates/server/app.js | 4 +- .../server/auth(auth)/auth.service.js | 30 +++-- app/templates/server/config/express.js | 4 +- app/templates/server/config/seed(mongoose).js | 83 +++++++------ endpoint/templates/index.spec.js | 12 +- endpoint/templates/name.controller.js | 114 ++++++++++++------ endpoint/templates/name.model(mongoose).js | 4 +- 15 files changed, 342 insertions(+), 249 deletions(-) diff --git a/app/templates/_package.json b/app/templates/_package.json index 9dfdadc5d..2d8337b71 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -15,7 +15,9 @@ "lodash": "~2.4.1",<% if(filters.jade) { %> "jade": "~1.2.0",<% } %><% if(filters.html) { %> "ejs": "~0.8.4",<% } %><% if(filters.mongoose) { %> - "mongoose": "~3.8.8",<% } %><% if(filters.auth) { %> + "mongoose": "~3.8.8", + "mongoose-bird": "~0.0.1", + <% } %><% if(filters.auth) { %> "jsonwebtoken": "^0.3.0", "express-jwt": "^0.1.3", "passport": "~0.2.0", @@ -46,7 +48,7 @@ "grunt-contrib-watch": "~0.6.1",<% if(filters.coffee) { %> "grunt-contrib-coffee": "^0.10.1",<% } %><% if(filters.jade) { %> "grunt-contrib-jade": "^0.11.0",<% } %><% if(filters.less) { %> - "grunt-contrib-less": "^0.11.0",<% } %> + "grunt-contrib-less": "^0.11.4",<% } %> "grunt-google-cdn": "~0.4.0", "grunt-newer": "~0.7.0", "grunt-ng-annotate": "^0.2.3", diff --git a/app/templates/server/api/thing/index.spec.js b/app/templates/server/api/thing/index.spec.js index 0fbfb1029..e62feff60 100644 --- a/app/templates/server/api/thing/index.spec.js +++ b/app/templates/server/api/thing/index.spec.js @@ -37,7 +37,7 @@ describe('Thing API Router:', function() { describe('GET /api/things', function() { it('should route to thing.controller.index', function() { - return router.get.withArgs('/', 'thingCtrl.index').should.have.been.calledOnce; + router.get.withArgs('/', 'thingCtrl.index').should.have.been.calledOnce; }); });<% if(filters.mongoose) { %> @@ -45,7 +45,7 @@ describe('Thing API Router:', function() { describe('GET /api/things/:id', function() { it('should route to thing.controller.show', function() { - return router.get.withArgs('/:id', 'thingCtrl.show').should.have.been.calledOnce; + router.get.withArgs('/:id', 'thingCtrl.show').should.have.been.calledOnce; }); }); @@ -53,7 +53,7 @@ describe('Thing API Router:', function() { describe('POST /api/things', function() { it('should route to thing.controller.create', function() { - return router.post.withArgs('/', 'thingCtrl.create').should.have.been.calledOnce; + router.post.withArgs('/', 'thingCtrl.create').should.have.been.calledOnce; }); }); @@ -61,7 +61,7 @@ describe('Thing API Router:', function() { describe('PUT /api/things/:id', function() { it('should route to thing.controller.update', function() { - return router.put.withArgs('/:id', 'thingCtrl.update').should.have.been.calledOnce; + router.put.withArgs('/:id', 'thingCtrl.update').should.have.been.calledOnce; }); }); @@ -69,7 +69,7 @@ describe('Thing API Router:', function() { describe('PATCH /api/things/:id', function() { it('should route to thing.controller.update', function() { - return router.patch.withArgs('/:id', 'thingCtrl.update').should.have.been.calledOnce; + router.patch.withArgs('/:id', 'thingCtrl.update').should.have.been.calledOnce; }); }); @@ -77,7 +77,7 @@ describe('Thing API Router:', function() { describe('DELETE /api/things/:id', function() { it('should route to thing.controller.destroy', function() { - return router.delete.withArgs('/:id', 'thingCtrl.destroy').should.have.been.calledOnce; + router.delete.withArgs('/:id', 'thingCtrl.destroy').should.have.been.calledOnce; }); });<% } %> diff --git a/app/templates/server/api/thing/thing.controller.js b/app/templates/server/api/thing/thing.controller.js index 72a678cf4..4d5abca05 100644 --- a/app/templates/server/api/thing/thing.controller.js +++ b/app/templates/server/api/thing/thing.controller.js @@ -7,10 +7,57 @@ * DELETE /things/:id -> destroy */ -'use strict'; +'use strict';<% if (filters.mongoose) { %> -var _ = require('lodash');<% if (filters.mongoose) { %> -var Thing = require('./thing.model');<% } %> +var _ = require('lodash'); +var Thing = require('./thing.model'); + +function handleError(res, statusCode){ + statusCode = statusCode || 500; + return function(err){ + res.send(statusCode, err); + }; +} + +function responseWithResult(res, statusCode){ + statusCode = statusCode || 200; + return function(entity){ + if(entity){ + return res.json(statusCode, entity); + } + }; +} + +function handleEntityNotFound(res){ + return function(entity){ + if(!entity){ + res.send(404); + return null; + } + return entity; + }; +} + +function saveUpdates(updates){ + return function(entity){ + var updated = _.merge(entity, updates); + return updated.saveAsync() + .then(function () { + return updated; + }); + }; +} + +function removeEntity(res){ + return function (entity) { + if(entity){ + return entity.removeAsync() + .then(function() { + return res.send(204); + }); + } + }; +}<% } %> // Get list of things exports.index = function(req, res) {<% if (!filters.mongoose) { %> @@ -34,56 +81,43 @@ exports.index = function(req, res) {<% if (!filters.mongoose) { %> name : 'Deployment Ready', info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' } - ]);<% } %><% if (filters.mongoose) { %> - Thing.find(function (err, things) { - if(err) { return handleError(res, err); } - return res.json(200, things); - });<% } %> + ]);<% } if (filters.mongoose) { %> + Thing.findAsync() + .then(responseWithResult(res)) + .catch(handleError(res));<% } %> };<% if (filters.mongoose) { %> // Get a single thing exports.show = function(req, res) { - Thing.findById(req.params.id, function (err, thing) { - if(err) { return handleError(res, err); } - if(!thing) { return res.send(404); } - return res.json(thing); - }); + Thing.findByIdAsync(req.params.id) + .then(handleEntityNotFound(res)) + .then(responseWithResult(res)) + .catch(handleError(res)); }; // Creates a new thing in the DB. exports.create = function(req, res) { - Thing.create(req.body, function(err, thing) { - if(err) { return handleError(res, err); } - return res.json(201, thing); - }); + Thing.createAsync(req.body) + .then(responseWithResult(res, 201)) + .catch(handleError(res)); }; // Updates an existing thing in the DB. exports.update = function(req, res) { - if(req.body._id) { delete req.body._id; } - Thing.findById(req.params.id, function (err, thing) { - if (err) { return handleError(res, err); } - if(!thing) { return res.send(404); } - var updated = _.merge(thing, req.body); - updated.save(function (err) { - if (err) { return handleError(res, err); } - return res.json(200, thing); - }); - }); + if(req.body._id) { + delete req.body._id; + } + Thing.findByIdAsync(req.params.id) + .then(handleEntityNotFound(res)) + .then(saveUpdates(req.body)) + .then(responseWithResult(res)) + .catch(handleError(res)); }; // Deletes a thing from the DB. exports.destroy = function(req, res) { - Thing.findById(req.params.id, function (err, thing) { - if(err) { return handleError(res, err); } - if(!thing) { return res.send(404); } - thing.remove(function(err) { - if(err) { return handleError(res, err); } - return res.send(204); - }); - }); -}; - -function handleError(res, err) { - return res.send(500, err); -}<% } %> + Thing.findByIdAsync(req.params.id) + .then(handleEntityNotFound(res)) + .then(removeEntity(res)) + .catch(handleError(res)); +};<% } %> diff --git a/app/templates/server/api/thing/thing.model(mongoose).js b/app/templates/server/api/thing/thing.model(mongoose).js index 92a791e70..a44bc710e 100644 --- a/app/templates/server/api/thing/thing.model(mongoose).js +++ b/app/templates/server/api/thing/thing.model(mongoose).js @@ -1,6 +1,6 @@ 'use strict'; -var mongoose = require('mongoose'), +var mongoose = require('mongoose-bird')(), Schema = mongoose.Schema; var ThingSchema = new Schema({ diff --git a/app/templates/server/api/user(auth)/index.spec.js b/app/templates/server/api/user(auth)/index.spec.js index 5bcd4c2c0..30b786fb3 100644 --- a/app/templates/server/api/user(auth)/index.spec.js +++ b/app/templates/server/api/user(auth)/index.spec.js @@ -47,7 +47,7 @@ describe('User API Router:', function() { describe('GET /api/users', function() { it('should verify admin role and route to user.controller.index', function() { - return router.get.withArgs('/', 'authService.hasRole.admin', 'userCtrl.index').should.have.been.calledOnce; + router.get.withArgs('/', 'authService.hasRole.admin', 'userCtrl.index').should.have.been.calledOnce; }); }); @@ -55,7 +55,7 @@ describe('User API Router:', function() { describe('DELETE /api/users/:id', function() { it('should verify admin role and route to user.controller.destroy', function() { - return router.delete.withArgs('/:id', 'authService.hasRole.admin', 'userCtrl.destroy').should.have.been.calledOnce; + router.delete.withArgs('/:id', 'authService.hasRole.admin', 'userCtrl.destroy').should.have.been.calledOnce; }); }); @@ -63,7 +63,7 @@ describe('User API Router:', function() { describe('GET /api/users/me', function() { it('should be authenticated and route to user.controller.me', function() { - return router.get.withArgs('/me', 'authService.isAuthenticated', 'userCtrl.me').should.have.been.calledOnce; + router.get.withArgs('/me', 'authService.isAuthenticated', 'userCtrl.me').should.have.been.calledOnce; }); }); @@ -71,7 +71,7 @@ describe('User API Router:', function() { describe('PUT /api/users/:id/password', function() { it('should be authenticated and route to user.controller.changePassword', function() { - return router.put.withArgs('/:id/password', 'authService.isAuthenticated', 'userCtrl.changePassword').should.have.been.calledOnce; + router.put.withArgs('/:id/password', 'authService.isAuthenticated', 'userCtrl.changePassword').should.have.been.calledOnce; }); }); @@ -79,7 +79,7 @@ describe('User API Router:', function() { describe('GET /api/users/:id', function() { it('should be authenticated and route to user.controller.show', function() { - return router.get.withArgs('/:id', 'authService.isAuthenticated', 'userCtrl.show').should.have.been.calledOnce; + router.get.withArgs('/:id', 'authService.isAuthenticated', 'userCtrl.show').should.have.been.calledOnce; }); }); @@ -87,7 +87,7 @@ describe('User API Router:', function() { describe('POST /api/users', function() { it('should route to user.controller.create', function() { - return router.post.withArgs('/', 'userCtrl.create').should.have.been.calledOnce; + router.post.withArgs('/', 'userCtrl.create').should.have.been.calledOnce; }); }); diff --git a/app/templates/server/api/user(auth)/user.controller.js b/app/templates/server/api/user(auth)/user.controller.js index 17e6e0e04..28e30199a 100644 --- a/app/templates/server/api/user(auth)/user.controller.js +++ b/app/templates/server/api/user(auth)/user.controller.js @@ -5,19 +5,37 @@ var passport = require('passport'); var config = require('../../config/environment'); var jwt = require('jsonwebtoken'); -var validationError = function(res, err) { - return res.json(422, err); +var validationError = function(res, statusCode) { + statusCode = statusCode || 422; + return function(err){ + res.json(statusCode, err); + }; }; +function handleError(res, statusCode){ + statusCode = statusCode || 500; + return function(err){ + res.send(statusCode, err); + }; +} + +function respondWith(res, statusCode){ + statusCode = statusCode || 200; + return function(){ + res.send(statusCode); + }; +} + /** * Get list of users * restriction: 'admin' */ exports.index = function(req, res) { - User.find({}, '-salt -password', function (err, users) { - if(err) return res.send(500, err); - res.json(200, users); - }); + User.findAsync({}, '-salt -hashedPassword') + .then(function (users) { + res.json(200, users); + }) + .catch(handleError(res)); }; /** @@ -27,11 +45,12 @@ exports.create = function (req, res, next) { var newUser = new User(req.body); newUser.provider = 'local'; newUser.role = 'user'; - newUser.save(function(err, user) { - if (err) return validationError(res, err); - var token = jwt.sign({_id: user._id }, config.secrets.session, { expiresInMinutes: 60*5 }); - res.json({ token: token }); - }); + newUser.saveAsync() + .then(function(user) { + var token = jwt.sign({_id: user._id }, config.secrets.session, { expiresInMinutes: 60*5 }); + res.json({ token: token }); + }) + .catch(validationError(res)); }; /** @@ -40,11 +59,16 @@ exports.create = function (req, res, next) { exports.show = function (req, res, next) { var userId = req.params.id; - User.findById(userId, function (err, user) { - if (err) return next(err); - if (!user) return res.send(401); - res.json(user.profile); - }); + User.findByIdAsync(userId) + .then(function (user) { + if(!user) { + return res.send(401); + } + res.json(user.profile); + }) + .catch(function(err){ + return next(err); + }); }; /** @@ -52,10 +76,9 @@ exports.show = function (req, res, next) { * restriction: 'admin' */ exports.destroy = function(req, res) { - User.findByIdAndRemove(req.params.id, function(err, user) { - if(err) return res.send(500, err); - return res.send(204); - }); + User.findByIdAndRemoveAsync(req.params.id) + .then(respondWith(res, 204)) + .catch(handleError(res)); }; /** @@ -66,21 +89,17 @@ exports.changePassword = function(req, res, next) { var oldPass = String(req.body.oldPassword); var newPass = String(req.body.newPassword); - User.findById(userId, function (err, user) { - user.authenticate(oldPass, function(authErr, authenticated) { - if (authErr) res.send(403); - - if (authenticated) { + User.findByIdAsync(userId) + .then(function(user) { + if(user.authenticate(oldPass)) { user.password = newPass; - user.save(function(err) { - if (err) return validationError(res, err); - res.send(200); - }); + return user.saveAsync() + .then(respondWith(res, 200)) + .catch(validationError(res)); } else { - res.send(403); + return res.send(403); } }); - }); }; /** @@ -88,13 +107,13 @@ exports.changePassword = function(req, res, next) { */ exports.me = function(req, res, next) { var userId = req.user._id; - User.findOne({ - _id: userId - }, '-salt -password', function(err, user) { // don't ever give out the password or salt - if (err) return next(err); - if (!user) return res.json(401); - res.json(user); - }); + + User.findOneAsync({ _id: userId }, '-salt -hashedPassword') + .then(function(user) { // don't ever give out the password or salt + if (!user) { return res.json(401); } + res.json(user); + }) + .catch(function(err){ return next(err); }); }; /** diff --git a/app/templates/server/api/user(auth)/user.model.js b/app/templates/server/api/user(auth)/user.model.js index b3497f859..b90386886 100644 --- a/app/templates/server/api/user(auth)/user.model.js +++ b/app/templates/server/api/user(auth)/user.model.js @@ -1,6 +1,6 @@ 'use strict'; -var mongoose = require('mongoose'); +var mongoose = require('mongoose-bird')(); var Schema = mongoose.Schema; var crypto = require('crypto');<% if(filters.oauth) { %> var authTypes = ['github', 'twitter', 'facebook', 'google'];<% } %> @@ -53,7 +53,9 @@ UserSchema UserSchema .path('email') .validate(function(email) {<% if (filters.oauth) { %> - if (authTypes.indexOf(this.provider) !== -1) return true;<% } %> + if (authTypes.indexOf(this.provider) !== -1){ + return true; + } <% } %> return email.length; }, 'Email cannot be blank'); @@ -61,7 +63,9 @@ UserSchema UserSchema .path('password') .validate(function(password) {<% if (filters.oauth) { %> - if (authTypes.indexOf(this.provider) !== -1) return true;<% } %> + if (authTypes.indexOf(this.provider) !== -1){ + return true; + } <% } %> return password.length; }, 'Password cannot be blank'); @@ -70,14 +74,19 @@ UserSchema .path('email') .validate(function(value, respond) { var self = this; - this.constructor.findOne({email: value}, function(err, user) { - if(err) throw err; - if(user) { - if(self.id === user.id) return respond(true); - return respond(false); - } - respond(true); - }); + return this.constructor.findOneAsync({email: value}) + .then(function(user) { + if(user) { + if(self.id === user.id) { + return respond(true); + } + return respond(false); + } + return respond(true); + }) + .catch(function(err){ + throw err; + }); }, 'The specified email address is already in use.'); var validatePresenceOf = function(value) { diff --git a/app/templates/server/api/user(auth)/user.model.spec.js b/app/templates/server/api/user(auth)/user.model.spec.js index afe8af882..fe51ea2ea 100644 --- a/app/templates/server/api/user(auth)/user.model.spec.js +++ b/app/templates/server/api/user(auth)/user.model.spec.js @@ -10,59 +10,39 @@ var user = new User({ password: 'password' }); -describe('User Model:', function() { - - // Clear users before testing +describe('User Model', function() { before(function() { + // Clear users before testing return User.remove().exec(); }); - describe('User (schema)', function() { - - it('should begin with no users', function() { - return User.find({}).exec().should.eventually.have.length(0); - }); - + afterEach(function() { + return User.remove().exec(); }); - describe('user (instance)', function() { - - describe('.save()', function() { - // Clear users after tests - afterEach(function() { - return User.remove().exec(); - }); - - it('should fail when saving a duplicate user', function(done) { - user.save(function() { - var userDup = new User(user); - userDup.save(function(err) { - err.should.be.instanceOf(Error); - done(); - }); - }); - }); - - it('should fail when saving without an email', function(done) { - user.email = ''; - user.save(function(err) { - err.should.be.instanceOf(Error); - done(); - }); - }); - - }); + it('should begin with no users', function() { + return User.findAsync({}) + .should.eventually.have.length(0); + }); - describe('.authenticate()', function() { + it('should fail when saving a duplicate user', function() { + return user.saveAsync() + .then(function() { + var userDup = new User(user); + return userDup.saveAsync(); + }).should.be.rejected; + }); - it("should authenticate user if password is valid", function() { - return user.authenticate('password').should.be.true; - }); + it('should fail when saving without an email', function() { + user.email = ''; + return user.saveAsync().should.be.rejected; + }); - it("should not authenticate user if password is invalid", function() { - return user.authenticate('blah').should.not.be.true; - }); + it("should authenticate user if password is valid", function() { + user.authenticate('password').should.be.true; + }); - }); + it("should not authenticate user if password is invalid", function() { + user.authenticate('blah').should.not.be.true; }); }); diff --git a/app/templates/server/app.js b/app/templates/server/app.js index 08b942f43..5369e94ef 100644 --- a/app/templates/server/app.js +++ b/app/templates/server/app.js @@ -8,7 +8,7 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development'; var express = require('express');<% if (filters.mongoose) { %> -var mongoose = require('mongoose');<% } %> +var mongoose = require('mongoose-bird')();<% } %> var config = require('./config/environment'); <% if (filters.mongoose) { %> // Connect to database @@ -34,4 +34,4 @@ server.listen(config.port, config.ip, function () { }); // Expose app -exports = module.exports = app; \ No newline at end of file +exports = module.exports = app; diff --git a/app/templates/server/auth(auth)/auth.service.js b/app/templates/server/auth(auth)/auth.service.js index 38ec34302..101dcc5c5 100644 --- a/app/templates/server/auth(auth)/auth.service.js +++ b/app/templates/server/auth(auth)/auth.service.js @@ -1,6 +1,6 @@ 'use strict'; -var mongoose = require('mongoose'); +var mongoose = require('mongoose-bird')(); var passport = require('passport'); var config = require('../config/environment'); var jwt = require('jsonwebtoken'); @@ -25,13 +25,17 @@ function isAuthenticated() { }) // Attach user to request .use(function(req, res, next) { - User.findById(req.user._id, function (err, user) { - if (err) return next(err); - if (!user) return res.send(401); - - req.user = user; - next(); - }); + User.findByIdAsync(req.user._id) + .then(function (user) { + if (!user) { + return res.send(401); + } + req.user = user; + next(); + }) + .catch(function(err){ + return next(err); + }); }); } @@ -39,7 +43,9 @@ function isAuthenticated() { * Checks if the user role meets the minimum requirements of the route */ function hasRole(roleRequired) { - if (!roleRequired) throw new Error('Required role needs to be set'); + if (!roleRequired) { + throw new Error('Required role needs to be set'); + } return compose() .use(isAuthenticated()) @@ -64,7 +70,9 @@ function signToken(id) { * Set token cookie directly for oAuth strategies */ function setTokenCookie(req, res) { - if (!req.user) return res.json(404, { message: 'Something went wrong, please try again.'}); + if (!req.user) { + return res.json(404, { message: 'Something went wrong, please try again.'}); + } var token = signToken(req.user._id, req.user.role); res.cookie('token', JSON.stringify(token)); res.redirect('/'); @@ -73,4 +81,4 @@ function setTokenCookie(req, res) { exports.isAuthenticated = isAuthenticated; exports.hasRole = hasRole; exports.signToken = signToken; -exports.setTokenCookie = setTokenCookie; \ No newline at end of file +exports.setTokenCookie = setTokenCookie; diff --git a/app/templates/server/config/express.js b/app/templates/server/config/express.js index 2243a7779..2403780f1 100644 --- a/app/templates/server/config/express.js +++ b/app/templates/server/config/express.js @@ -17,7 +17,7 @@ var config = require('./environment');<% if (filters.auth) { %> var passport = require('passport');<% } %><% if (filters.twitterAuth) { %> var session = require('express-session'); var mongoStore = require('connect-mongo')(session); -var mongoose = require('mongoose');<% } %> +var mongoose = require('mongoose-bird')();<% } %> module.exports = function(app) { var env = app.get('env'); @@ -57,4 +57,4 @@ module.exports = function(app) { app.use(morgan('dev')); app.use(errorHandler()); // Error handler - has to be last } -}; \ No newline at end of file +}; diff --git a/app/templates/server/config/seed(mongoose).js b/app/templates/server/config/seed(mongoose).js index 27ab19417..6d56010b6 100644 --- a/app/templates/server/config/seed(mongoose).js +++ b/app/templates/server/config/seed(mongoose).js @@ -6,44 +6,51 @@ 'use strict'; var Thing = require('../api/thing/thing.model'); -<% if (filters.auth) { %>var User = require('../api/user/user.model');<% } %> +<% if (filters.auth) { %> +var User = require('../api/user/user.model'); +<% } %> -Thing.find({}).remove(function() { - Thing.create({ - name : 'Development Tools', - info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' - }, { - name : 'Server and Client integration', - info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' - }, { - name : 'Smart Build System', - info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of scripts and styles into your index.html' - }, { - name : 'Modular Structure', - info : 'Best practice client and server structures allow for more code reusability and maximum scalability' - }, { - name : 'Optimized Build', - info : 'Build process packs up your templates as a single JavaScript payload, minifies your scripts/css/images, and rewrites asset names for caching.' - },{ - name : 'Deployment Ready', - info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' +Thing.find({}).removeAsync() + .then(function() { + Thing.create({ + name : 'Development Tools', + info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' + }, { + name : 'Server and Client integration', + info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' + }, { + name : 'Smart Build System', + info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of scripts and styles into your index.html' + }, { + name : 'Modular Structure', + info : 'Best practice client and server structures allow for more code reusability and maximum scalability' + }, { + name : 'Optimized Build', + info : 'Build process packs up your templates as a single JavaScript payload, minifies your scripts/css/images, and rewrites asset names for caching.' + },{ + name : 'Deployment Ready', + info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' + }); }); -});<% if (filters.auth) { %> -User.find({}).remove(function() { - User.create({ - provider: 'local', - name: 'Test User', - email: 'test@test.com', - password: 'test' - }, { - provider: 'local', - role: 'admin', - name: 'Admin', - email: 'admin@admin.com', - password: 'admin' - }, function() { - console.log('finished populating users'); - } - ); -});<% } %> \ No newline at end of file +<% if (filters.auth) { %> + +User.find({}).removeAsync() + .then(function() { + User.create({ + provider: 'local', + name: 'Test User', + email: 'test@test.com', + password: 'test' + }, { + provider: 'local', + role: 'admin', + name: 'Admin', + email: 'admin@admin.com', + password: 'admin' + }, function() { + console.log('finished populating users'); + } + ); + }); +<% } %> diff --git a/endpoint/templates/index.spec.js b/endpoint/templates/index.spec.js index 62caed5dc..b5bfb3a41 100644 --- a/endpoint/templates/index.spec.js +++ b/endpoint/templates/index.spec.js @@ -37,7 +37,7 @@ describe('<%= classedName %> API Router:', function() { describe('GET <%= route %>', function() { it('should route to <%= name %>.controller.index', function() { - return router.get.withArgs('/', '<%= name %>Ctrl.index').should.have.been.calledOnce; + router.get.withArgs('/', '<%= name %>Ctrl.index').should.have.been.calledOnce; }); });<% if(filters.mongoose) { %> @@ -45,7 +45,7 @@ describe('<%= classedName %> API Router:', function() { describe('GET <%= route %>/:id', function() { it('should route to <%= name %>.controller.show', function() { - return router.get.withArgs('/:id', '<%= name %>Ctrl.show').should.have.been.calledOnce; + router.get.withArgs('/:id', '<%= name %>Ctrl.show').should.have.been.calledOnce; }); }); @@ -53,7 +53,7 @@ describe('<%= classedName %> API Router:', function() { describe('POST <%= route %>', function() { it('should route to <%= name %>.controller.create', function() { - return router.post.withArgs('/', '<%= name %>Ctrl.create').should.have.been.calledOnce; + router.post.withArgs('/', '<%= name %>Ctrl.create').should.have.been.calledOnce; }); }); @@ -61,7 +61,7 @@ describe('<%= classedName %> API Router:', function() { describe('PUT <%= route %>/:id', function() { it('should route to <%= name %>.controller.update', function() { - return router.put.withArgs('/:id', '<%= name %>Ctrl.update').should.have.been.calledOnce; + router.put.withArgs('/:id', '<%= name %>Ctrl.update').should.have.been.calledOnce; }); }); @@ -69,7 +69,7 @@ describe('<%= classedName %> API Router:', function() { describe('PATCH <%= route %>/:id', function() { it('should route to <%= name %>.controller.update', function() { - return router.patch.withArgs('/:id', '<%= name %>Ctrl.update').should.have.been.calledOnce; + router.patch.withArgs('/:id', '<%= name %>Ctrl.update').should.have.been.calledOnce; }); }); @@ -77,7 +77,7 @@ describe('<%= classedName %> API Router:', function() { describe('DELETE <%= route %>/:id', function() { it('should route to <%= name %>.controller.destroy', function() { - return router.delete.withArgs('/:id', '<%= name %>Ctrl.destroy').should.have.been.calledOnce; + router.delete.withArgs('/:id', '<%= name %>Ctrl.destroy').should.have.been.calledOnce; }); });<% } %> diff --git a/endpoint/templates/name.controller.js b/endpoint/templates/name.controller.js index 1d9da544e..5851a1338 100644 --- a/endpoint/templates/name.controller.js +++ b/endpoint/templates/name.controller.js @@ -1,60 +1,94 @@ -'use strict'; +'use strict';<% if (filters.mongoose) { %> -var _ = require('lodash');<% if (filters.mongoose) { %> -var <%= classedName %> = require('./<%= name %>.model');<% } %> +var _ = require('lodash'); +var <%= classedName %> = require('./<%= name %>.model'); + +function handleError(res, statusCode){ + statusCode = statusCode || 500; + return function(err){ + res.send(statusCode, err); + }; +} + +function responseWithResult(res, statusCode){ + statusCode = statusCode || 200; + return function(entity){ + if(entity){ + return res.json(statusCode, entity); + } + }; +} + +function handleEntityNotFound(res){ + return function(entity){ + if(!entity){ + res.send(404); + return null; + } + return entity; + }; +} + +function saveUpdates(updates){ + return function(entity){ + var updated = _.merge(entity, updates); + return updated.saveAsync() + .then(function () { + return updated; + }); + }; +} + +function removeEntity(res){ + return function (entity) { + if(entity){ + return entity.removeAsync() + .then(function() { + return res.send(204); + }); + } + }; +}<% } %> // Get list of <%= name %>s exports.index = function(req, res) {<% if (!filters.mongoose) { %> - res.json([]);<% } %><% if (filters.mongoose) { %> - <%= classedName %>.find(function (err, <%= name %>s) { - if(err) { return handleError(res, err); } - return res.json(200, <%= name %>s); - });<% } %> + res.json([]);<% } if (filters.mongoose) { %> + <%= classedName %>.findAsync() + .then(responseWithResult(res)) + .catch(handleError(res));<% } %> };<% if (filters.mongoose) { %> // Get a single <%= name %> exports.show = function(req, res) { - <%= classedName %>.findById(req.params.id, function (err, <%= name %>) { - if(err) { return handleError(res, err); } - if(!<%= name %>) { return res.send(404); } - return res.json(<%= name %>); - }); + <%= classedName %>.findByIdAsync(req.params.id) + .then(handleEntityNotFound(res)) + .then(responseWithResult(res)) + .catch(handleError(res)); }; // Creates a new <%= name %> in the DB. exports.create = function(req, res) { - <%= classedName %>.create(req.body, function(err, <%= name %>) { - if(err) { return handleError(res, err); } - return res.json(201, <%= name %>); - }); + <%= classedName %>.createAsync(req.body) + .then(responseWithResult(res, 201)) + .catch(handleError(res)); }; // Updates an existing <%= name %> in the DB. exports.update = function(req, res) { - if(req.body._id) { delete req.body._id; } - <%= classedName %>.findById(req.params.id, function (err, <%= name %>) { - if (err) { return handleError(res, err); } - if(!<%= name %>) { return res.send(404); } - var updated = _.merge(<%= name %>, req.body); - updated.save(function (err) { - if (err) { return handleError(res, err); } - return res.json(200, <%= name %>); - }); - }); + if(req.body._id) { + delete req.body._id; + } + <%= classedName %>.findByIdAsync(req.params.id) + .then(handleEntityNotFound(res)) + .then(saveUpdates(req.body)) + .then(responseWithResult(res)) + .catch(handleError(res)); }; // Deletes a <%= name %> from the DB. exports.destroy = function(req, res) { - <%= classedName %>.findById(req.params.id, function (err, <%= name %>) { - if(err) { return handleError(res, err); } - if(!<%= name %>) { return res.send(404); } - <%= name %>.remove(function(err) { - if(err) { return handleError(res, err); } - return res.send(204); - }); - }); -}; - -function handleError(res, err) { - return res.send(500, err); -}<% } %> \ No newline at end of file + <%= classedName %>.findByIdAsync(req.params.id) + .then(handleEntityNotFound(res)) + .then(removeEntity(res)) + .catch(handleError(res)); +};<% } %> diff --git a/endpoint/templates/name.model(mongoose).js b/endpoint/templates/name.model(mongoose).js index 89e0dfaa7..9b23d9d41 100644 --- a/endpoint/templates/name.model(mongoose).js +++ b/endpoint/templates/name.model(mongoose).js @@ -1,6 +1,6 @@ 'use strict'; -var mongoose = require('mongoose'), +var mongoose = require('mongoose-bird')(), Schema = mongoose.Schema; var <%= classedName %>Schema = new Schema({ @@ -9,4 +9,4 @@ var <%= classedName %>Schema = new Schema({ active: Boolean }); -module.exports = mongoose.model('<%= classedName %>', <%= classedName %>Schema); \ No newline at end of file +module.exports = mongoose.model('<%= classedName %>', <%= classedName %>Schema); From 49d5bbdf5c8a59812e8f72ad5b95309894bea5ca Mon Sep 17 00:00:00 2001 From: kingcody Date: Sat, 6 Sep 2014 22:11:53 -0400 Subject: [PATCH 009/171] fix(gen): filter `client/components/socket` js files Changes: - add `(js)` filter to the js files in `client/components/socket` Closes #530 --- .../socket(socketio)/{socket.mock.js => socket.mock(js).js} | 0 .../socket(socketio)/{socket.service.js => socket.service(js).js} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename app/templates/client/components/socket(socketio)/{socket.mock.js => socket.mock(js).js} (100%) rename app/templates/client/components/socket(socketio)/{socket.service.js => socket.service(js).js} (100%) diff --git a/app/templates/client/components/socket(socketio)/socket.mock.js b/app/templates/client/components/socket(socketio)/socket.mock(js).js similarity index 100% rename from app/templates/client/components/socket(socketio)/socket.mock.js rename to app/templates/client/components/socket(socketio)/socket.mock(js).js diff --git a/app/templates/client/components/socket(socketio)/socket.service.js b/app/templates/client/components/socket(socketio)/socket.service(js).js similarity index 100% rename from app/templates/client/components/socket(socketio)/socket.service.js rename to app/templates/client/components/socket(socketio)/socket.service(js).js From c2ab8ae922ec0f2e377e560e937d09091e596b07 Mon Sep 17 00:00:00 2001 From: kingcody Date: Sat, 6 Sep 2014 21:53:07 -0400 Subject: [PATCH 010/171] test(gen): make tests more strict and DRY Changes: - `genFiles(ops)` creates an array of expected files based on options given - test for generated files on every set of tests - test for unexpected files - use testExec to be more DRY Exposes a bug with `client/components/socket`: `socket.service.js` and `socket.mock.js` are not filtered. See #530 --- package.json | 1 + test/test-file-creation.js | 416 +++++++++++++++++++++++++------------ 2 files changed, 287 insertions(+), 130 deletions(-) diff --git a/package.json b/package.json index cab669526..394f5e6d1 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "marked": "~0.2.8", "mocha": "~1.21.0", "q": "^1.0.1", + "recursive-readdir": "^1.2.0", "semver": "~2.2.1", "shelljs": "^0.3.0", "underscore.string": "^2.3.3" diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 2dc0ee548..2ea172b2e 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -1,11 +1,12 @@ /*global describe, beforeEach, it */ 'use strict'; var path = require('path'); +var fs = require('fs-extra'); +var exec = require('child_process').exec; var helpers = require('yeoman-generator').test; var chai = require('chai'); var expect = chai.expect; -var fs = require('fs-extra'); -var exec = require('child_process').exec; +var recursiveReadDir = require('recursive-readdir'); describe('angular-fullstack generator', function () { var gen, defaultOptions = { @@ -32,6 +33,201 @@ describe('angular-fullstack generator', function () { }); } + function assertOnlyFiles(expectedFiles, done, path, skip) { + path = path || './'; + skip = skip || ['e2e', 'node_modules', 'client/bower_components']; + + recursiveReadDir(path, skip, function(err, files) { + if (err) { return done(err); } + + for (var i = 0, expectedFilesLength = expectedFiles.length; i < expectedFilesLength; i++) { + var index = files.indexOf(expectedFiles[i]); + files.splice(index, 1); + } + + if (files.length !== 0) { + err = new Error('unexpected files found'); + err.expected = ''; + err.actual = files.join('\n'); + return done(err); + } + + done(); + }); + } + + function runTest(type, self, cb, timeout) { + self.timeout(timeout || 60000); + gen.run({}, function() { + exec(type, function(error, stdout, stderr) { + switch(type) { + case 'grunt test:client': + expect(stdout, 'Client tests failed \n' + stdout ).to.contain('Executed 1 of 1\u001b[32m SUCCESS\u001b'); + break; + case 'grunt jshint': + expect(stdout).to.contain('Done, without errors.'); + break; + case 'grunt test:server': + expect(stdout, 'Server tests failed (do you have mongoDB running?) \n' + stdout).to.contain('Done, without errors.'); + break; + default: + expect(stderr).to.be.empty; + } + + cb(); + }); + }); + } + + function genFiles(ops) { + var mapping = { + stylesheet: { + sass: 'scss', + stylus: 'styl', + less: 'less', + css: 'css' + }, + markup: { + jade: 'jade', + html: 'html' + }, + script: { + js: 'js', + coffee: 'coffee' + } + }, + files = []; + + var oauthFiles = function(type) { + return [ + 'server/auth/' + type + '/index.js', + 'server/auth/' + type + '/passport.js', + ]; + }; + + + var script = mapping.script[ops.script], + markup = mapping.markup[ops.markup], + stylesheet = mapping.stylesheet[ops.stylesheet]; + + files = files.concat([ + 'client/.htaccess', + 'client/.jshintrc', + '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/navbar/navbar.' + markup, + 'client/components/navbar/navbar.controller.' + script, + 'server/.jshintrc', + 'server/.jshintrc-spec', + 'server/app.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.e2e.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/views/404.' + markup, + '.bowerrc', + '.buildignore', + '.editorconfig', + '.gitattributes', + '.gitignore', + '.travis.yml', + '.yo-rc.json', + 'Gruntfile.js', + 'package.json', + 'bower.json', + 'karma.conf.js', + 'mocha.conf.js', + 'protractor.conf.js' + ]); + + if (ops.uibootstrap) { + files = files.concat([ + 'client/components/modal/modal.' + markup, + 'client/components/modal/modal.' + stylesheet, + 'client/components/modal/modal.service.' + script, + ]); + } + + if (ops.mongoose) { + files = files.concat([ + 'server/api/thing/thing.model.js', + 'server/config/seed.js' + ]); + } + + if (ops.auth) { + files = files.concat([ + 'client/app/account/account.' + script, + 'client/app/account/login/login.' + markup, + 'client/app/account/login/login.' + stylesheet, + '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.' + script, + 'client/app/admin/admin.controller.' + script, + 'client/components/auth/auth.service.' + 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.e2e.js', + 'server/api/user/user.model.js', + 'server/api/user/user.model.spec.js', + 'server/auth/index.js', + 'server/auth/auth.service.js', + 'server/auth/local/index.js', + 'server/auth/local/passport.js' + ]); + } + + if (ops.oauth) { + var oauth = ops.oauth; + for (var i = 0, oauthLength = oauth.length; i < oauthLength; i++) { + files = files.concat(oauthFiles(oauth[i].replace('Auth', ''))); + } + } + + 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; + } + + function everyFile(files, ops) { + ops = ops || { + skip: ['node_modules', 'client/bower_components', 'e2e'] + } + } + beforeEach(function (done) { this.timeout(10000); var deps = [ @@ -68,33 +264,15 @@ describe('angular-fullstack generator', function () { }); it('should run client tests successfully', function(done) { - this.timeout(60000); - gen.run({}, function () { - exec('grunt test:client', function (error, stdout, stderr) { - expect(stdout, 'Client tests failed \n' + stdout ).to.contain('Executed 1 of 1\u001b[32m SUCCESS\u001b'); - done(); - }); - }); + runTest('grunt test:client', this, done); }); it('should pass jshint', function(done) { - this.timeout(60000); - gen.run({}, function () { - exec('grunt jshint', function (error, stdout, stderr) { - expect(stdout).to.contain('Done, without errors.'); - done(); - }); - }); + runTest('grunt jshint', this, done); }); it('should run server tests successfully', function(done) { - this.timeout(60000); - gen.run({}, function () { - exec('grunt test:server', function (error, stdout, stderr) { - expect(stdout, 'Server tests failed (do you have mongoDB running?) \n' + stdout).to.contain('Done, without errors.'); - done(); - }); - }); + runTest('grunt test:server', this, done); }); it('should run server tests successfully with generated endpoint', function(done) { @@ -130,6 +308,19 @@ describe('angular-fullstack generator', function () { }); }); + it('should generate expected files', function (done) { + gen.run({}, function () { + helpers.assertFile(genFiles(defaultOptions)); + done(); + }); + }); + + it('should not generate unexpected files', function (done) { + gen.run({}, function () { + assertOnlyFiles(genFiles(defaultOptions), done); + }); + }); + // it('should run e2e tests successfully', function(done) { // this.timeout(80000); // gen.run({}, function () { @@ -144,166 +335,131 @@ describe('angular-fullstack generator', function () { }); describe('with other preprocessors and oauth', function() { + var testOptions = { + script: 'coffee', + markup: 'jade', + stylesheet: 'less', + router: 'uirouter', + mongoose: true, + auth: true, + oauth: ['twitterAuth', 'facebookAuth', 'googleAuth'], + socketio: true + }; + beforeEach(function() { - helpers.mockPrompt(gen, { - script: 'coffee', - markup: 'jade', - stylesheet: 'less', - router: 'uirouter', - mongoose: true, - auth: true, - oauth: ['twitterAuth', 'facebookAuth', 'googleAuth'], - socketio: true - }); + helpers.mockPrompt(gen, testOptions); }); it('should run client tests successfully', function(done) { - this.timeout(60000); - gen.run({}, function () { - exec('grunt test:client', function (error, stdout, stderr) { - expect(stdout, 'Client tests failed \n' + stdout ).to.contain('Executed 1 of 1\u001b[32m SUCCESS\u001b'); - done(); - }); - }); + runTest('grunt test:client', this, done); }); it('should pass jshint', function(done) { - this.timeout(60000); + runTest('grunt jshint', this, done); + }); + + it('should run server tests successfully', function(done) { + runTest('grunt test:server', this, done); + }); + + it('should generate expected files', function (done) { gen.run({}, function () { - exec('grunt jshint', function (error, stdout, stderr) { - expect(stdout).to.contain('Done, without errors.'); - done(); - }); + helpers.assertFile(genFiles(testOptions)); + done(); }); }); - it('should run server tests successfully', function(done) { - this.timeout(60000); + it('should not generate unexpected files', function (done) { gen.run({}, function () { - exec('grunt test:server', function (error, stdout, stderr) { - expect(stdout, 'Server tests failed (do you have mongoDB running?) \n' + stdout).to.contain('Done, without errors.'); - done(); - }); + assertOnlyFiles(genFiles(testOptions), done); }); }); }); describe('with other preprocessors and no server options', function() { + var testOptions = { + script: 'coffee', + markup: 'jade', + stylesheet: 'stylus', + router: 'ngroute', + mongoose: false, + auth: false, + oauth: [], + socketio: false + }; + beforeEach(function(done) { - helpers.mockPrompt(gen, { - script: 'coffee', - markup: 'jade', - stylesheet: 'stylus', - router: 'ngroute', - mongoose: false, - auth: false, - oauth: [], - socketio: false - }); + helpers.mockPrompt(gen, testOptions); done(); }); it('should run client tests successfully', function(done) { - this.timeout(60000); - gen.run({}, function () { - exec('grunt test:client', function (error, stdout, stderr) { - expect(stdout, 'Client tests failed \n' + stdout ).to.contain('Executed 1 of 1\u001b[32m SUCCESS\u001b'); - done(); - }); - }); + runTest('grunt test:client', this, done); }); it('should pass jshint', function(done) { - this.timeout(60000); + runTest('grunt jshint', this, done); + }); + + it('should run server tests successfully', function(done) { + runTest('grunt test:server', this, done); + }); + + it('should generate expected files', function (done) { gen.run({}, function () { - exec('grunt jshint', function (error, stdout, stderr) { - expect(stdout).to.contain('Done, without errors.'); - done(); - }); + helpers.assertFile(genFiles(testOptions)); + done(); }); }); - it('should run server tests successfully', function(done) { - this.timeout(60000); + it('should not generate unexpected files', function (done) { gen.run({}, function () { - exec('grunt test:server', function (error, stdout, stderr) { - expect(stdout, 'Server tests failed (do you have mongoDB running?) \n' + stdout).to.contain('Done, without errors.'); - done(); - }); + assertOnlyFiles(genFiles(testOptions), done); }); }); }); describe('with no preprocessors and no server options', function() { + var testOptions = { + script: 'js', + markup: 'html', + stylesheet: 'css', + router: 'ngroute', + mongoose: false, + auth: false, + oauth: [], + socketio: false + }; + beforeEach(function(done) { - helpers.mockPrompt(gen, { - script: 'js', - markup: 'html', - stylesheet: 'css', - router: 'ngroute', - mongoose: false, - auth: false, - oauth: [], - socketio: false - }); + helpers.mockPrompt(gen, testOptions); done(); }); it('should run client tests successfully', function(done) { - this.timeout(60000); - gen.run({}, function () { - exec('grunt test:client', function (error, stdout, stderr) { - expect(stdout, 'Client tests failed \n' + stdout ).to.contain('Executed 1 of 1\u001b[32m SUCCESS\u001b'); - done(); - }); - }); + runTest('grunt test:client', this, done); }); it('should pass jshint', function(done) { - this.timeout(60000); - gen.run({}, function () { - exec('grunt jshint', function (error, stdout, stderr) { - expect(stdout).to.contain('Done, without errors.'); - done(); - }); - }); + runTest('grunt jshint', this, done); }); it('should run server tests successfully', function(done) { - this.timeout(60000); - gen.run({}, function () { - exec('grunt test:server', function (error, stdout, stderr) { - expect(stdout, 'Server tests failed (do you have mongoDB running?) \n' + stdout).to.contain('Done, without errors.'); - done(); - }); - }); + runTest('grunt test:server', this, done); }); it('should generate expected files', function (done) { - helpers.mockPrompt(gen, defaultOptions); - gen.run({}, function () { - helpers.assertFile([ - 'client/.htaccess', - 'client/favicon.ico', - 'client/robots.txt', - 'client/app/main/main.scss', - 'client/app/main/main.html', - 'client/index.html', - 'client/.jshintrc', - 'client/assets/images/yeoman.png', - '.bowerrc', - '.editorconfig', - '.gitignore', - 'Gruntfile.js', - 'package.json', - 'bower.json', - 'server/app.js', - 'server/config/express.js', - 'server/api/thing/index.js']); + helpers.assertFile(genFiles(testOptions)); done(); }); }); + + it('should not generate unexpected files', function (done) { + gen.run({}, function () { + assertOnlyFiles(genFiles(testOptions), done); + }); + }); }); }); }); From 3536b4577821734dff86cf6d8c47e501abb28da4 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Tue, 9 Sep 2014 22:33:21 -0600 Subject: [PATCH 011/171] fix unexpected files created in tests --- test/test-file-creation.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/test-file-creation.js b/test/test-file-creation.js index c8d26917b..f5a753375 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -345,7 +345,9 @@ describe('angular-fullstack generator', function () { mongoose: true, auth: true, oauth: ['twitterAuth', 'facebookAuth', 'googleAuth'], - socketio: true + socketio: true, + bootstrap: true, + uibootstrap: true }; beforeEach(function() { @@ -387,7 +389,9 @@ describe('angular-fullstack generator', function () { mongoose: false, auth: false, oauth: [], - socketio: false + socketio: false, + bootstrap: false, + uibootstrap: false }; beforeEach(function(done) { @@ -430,7 +434,9 @@ describe('angular-fullstack generator', function () { mongoose: false, auth: false, oauth: [], - socketio: false + socketio: false, + bootstrap: true, + uibootstrap: true }; beforeEach(function(done) { From cf6ae006f5dfd318f8234910365fa13c69336509 Mon Sep 17 00:00:00 2001 From: kingcody Date: Wed, 10 Sep 2014 08:52:09 -0400 Subject: [PATCH 012/171] test(gen): clean up code and test more endpoint combinations Changes: - remove unneeded code - use runTest for endpoints as well - test endpoints with different options - test endpoints with various cased named Exposes Bug: See #540 --- test/test-file-creation.js | 94 ++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 30 deletions(-) diff --git a/test/test-file-creation.js b/test/test-file-creation.js index f5a753375..b6171fabd 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -39,13 +39,16 @@ describe('angular-fullstack generator', function () { path = path || './'; skip = skip || ['e2e', 'node_modules', 'client/bower_components']; - recursiveReadDir(path, skip, function(err, files) { + recursiveReadDir(path, skip, function(err, actualFiles) { if (err) { return done(err); } + var files = actualFiles.concat(); - for (var i = 0, expectedFilesLength = expectedFiles.length; i < expectedFilesLength; i++) { - var index = files.indexOf(expectedFiles[i]); - files.splice(index, 1); - } + expectedFiles.forEach(function(file, i) { + var index = files.indexOf(file); + if (index >= 0) { + files.splice(index, 1); + } + }); if (files.length !== 0) { err = new Error('unexpected files found'); @@ -58,11 +61,16 @@ describe('angular-fullstack generator', function () { }); } - function runTest(type, self, cb, timeout) { + 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); - gen.run({}, function() { - exec(type, function(error, stdout, stderr) { - switch(type) { + + var execFn = function() { + exec(cmd, function(error, stdout, stderr) { + switch(cmd) { case 'grunt test:client': expect(stdout, 'Client tests failed \n' + stdout ).to.contain('Executed 1 of 1\u001b[32m SUCCESS\u001b'); break; @@ -78,7 +86,13 @@ describe('angular-fullstack generator', function () { cb(); }); - }); + }; + + if (endpoint) { + generatorTest('endpoint', endpoint, {}, execFn); + } else { + gen.run({}, execFn); + } } function genFiles(ops) { @@ -206,10 +220,9 @@ describe('angular-fullstack generator', function () { } if (ops.oauth) { - var oauth = ops.oauth; - for (var i = 0, oauthLength = oauth.length; i < oauthLength; i++) { - files = files.concat(oauthFiles(oauth[i].replace('Auth', ''))); - } + ops.oauth.forEach(function(type, i) { + files = files.concat(oauthFiles(type.replace('Auth', ''))); + }); } if (ops.socketio) { @@ -224,11 +237,10 @@ describe('angular-fullstack generator', function () { return files; } - function everyFile(files, ops) { - ops = ops || { - skip: ['node_modules', 'client/bower_components', 'e2e'] - } - } + + /** + * Generator tests + */ beforeEach(function (done) { this.timeout(10000); @@ -269,7 +281,7 @@ describe('angular-fullstack generator', function () { runTest('grunt test:client', this, done); }); - it('should pass jshint', function(done) { + it('should pass lint', function(done) { runTest('grunt jshint', this, done); }); @@ -277,14 +289,20 @@ describe('angular-fullstack generator', function () { runTest('grunt test:server', this, done); }); + 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) { - this.timeout(60000); - generatorTest('endpoint', 'foo', {}, function() { - exec('grunt test:server', function (error, stdout, stderr) { - expect(stdout, 'Server tests failed (do you have mongoDB running?) \n' + stdout).to.contain('Done, without errors.'); - 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 use existing config if available', function(done) { @@ -358,7 +376,7 @@ describe('angular-fullstack generator', function () { runTest('grunt test:client', this, done); }); - it('should pass jshint', function(done) { + it('should pass lint', function(done) { runTest('grunt jshint', this, done); }); @@ -366,6 +384,14 @@ describe('angular-fullstack generator', function () { runTest('grunt test:server', this, done); }); + 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 () { helpers.assertFile(genFiles(testOptions)); @@ -403,7 +429,7 @@ describe('angular-fullstack generator', function () { runTest('grunt test:client', this, done); }); - it('should pass jshint', function(done) { + it('should pass lint', function(done) { runTest('grunt jshint', this, done); }); @@ -411,6 +437,14 @@ describe('angular-fullstack generator', function () { runTest('grunt test:server', this, done); }); + 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 () { helpers.assertFile(genFiles(testOptions)); @@ -448,7 +482,7 @@ describe('angular-fullstack generator', function () { runTest('grunt test:client', this, done); }); - it('should pass jshint', function(done) { + it('should pass lint', function(done) { runTest('grunt jshint', this, done); }); From 73620809a83c2a7ab5c3d6a81f67f8bb56f8fdb7 Mon Sep 17 00:00:00 2001 From: kingcody Date: Wed, 10 Sep 2014 09:32:07 -0400 Subject: [PATCH 013/171] fix(gen): camelCase endpoint name when used in variable name Closes #540 --- endpoint/templates/index.spec.js | 4 ++-- endpoint/templates/name.controller.js | 30 +++++++++++++-------------- endpoint/templates/name.e2e.js | 16 +++++++------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/endpoint/templates/index.spec.js b/endpoint/templates/index.spec.js index 62caed5dc..74c17d539 100644 --- a/endpoint/templates/index.spec.js +++ b/endpoint/templates/index.spec.js @@ -3,7 +3,7 @@ var proxyquire = require('proxyquire').noPreserveCache(); /* <%= name %>.controller stub */ -var <%= name %>Ctrl = { +var <%= cameledName %>Ctrl = { index: '<%= name %>Ctrl.index'<% if(filters.mongoose) { %>, show: '<%= name %>Ctrl.show', create: '<%= name %>Ctrl.create', @@ -25,7 +25,7 @@ var <%= name %>Ctrl = { return router; } }, - './<%= name %>.controller': <%= name %>Ctrl + './<%= name %>.controller': <%= cameledName %>Ctrl }); describe('<%= classedName %> API Router:', function() { diff --git a/endpoint/templates/name.controller.js b/endpoint/templates/name.controller.js index 1d9da544e..3b2bbc918 100644 --- a/endpoint/templates/name.controller.js +++ b/endpoint/templates/name.controller.js @@ -6,49 +6,49 @@ var <%= classedName %> = require('./<%= name %>.model');<% } %> // Get list of <%= name %>s exports.index = function(req, res) {<% if (!filters.mongoose) { %> res.json([]);<% } %><% if (filters.mongoose) { %> - <%= classedName %>.find(function (err, <%= name %>s) { + <%= classedName %>.find(function (err, <%= cameledName %>s) { if(err) { return handleError(res, err); } - return res.json(200, <%= name %>s); + return res.json(200, <%= cameledName %>s); });<% } %> };<% if (filters.mongoose) { %> // Get a single <%= name %> exports.show = function(req, res) { - <%= classedName %>.findById(req.params.id, function (err, <%= name %>) { + <%= classedName %>.findById(req.params.id, function (err, <%= cameledName %>) { if(err) { return handleError(res, err); } - if(!<%= name %>) { return res.send(404); } - return res.json(<%= name %>); + if(!<%= cameledName %>) { return res.send(404); } + return res.json(<%= cameledName %>); }); }; // Creates a new <%= name %> in the DB. exports.create = function(req, res) { - <%= classedName %>.create(req.body, function(err, <%= name %>) { + <%= classedName %>.create(req.body, function(err, <%= cameledName %>) { if(err) { return handleError(res, err); } - return res.json(201, <%= name %>); + return res.json(201, <%= cameledName %>); }); }; // Updates an existing <%= name %> in the DB. exports.update = function(req, res) { if(req.body._id) { delete req.body._id; } - <%= classedName %>.findById(req.params.id, function (err, <%= name %>) { + <%= classedName %>.findById(req.params.id, function (err, <%= cameledName %>) { if (err) { return handleError(res, err); } - if(!<%= name %>) { return res.send(404); } - var updated = _.merge(<%= name %>, req.body); + if(!<%= cameledName %>) { return res.send(404); } + var updated = _.merge(<%= cameledName %>, req.body); updated.save(function (err) { if (err) { return handleError(res, err); } - return res.json(200, <%= name %>); + return res.json(200, <%= cameledName %>); }); }); }; // Deletes a <%= name %> from the DB. exports.destroy = function(req, res) { - <%= classedName %>.findById(req.params.id, function (err, <%= name %>) { + <%= classedName %>.findById(req.params.id, function (err, <%= cameledName %>) { if(err) { return handleError(res, err); } - if(!<%= name %>) { return res.send(404); } - <%= name %>.remove(function(err) { + if(!<%= cameledName %>) { return res.send(404); } + <%= cameledName %>.remove(function(err) { if(err) { return handleError(res, err); } return res.send(204); }); @@ -57,4 +57,4 @@ exports.destroy = function(req, res) { function handleError(res, err) { return res.send(500, err); -}<% } %> \ No newline at end of file +}<% } %> diff --git a/endpoint/templates/name.e2e.js b/endpoint/templates/name.e2e.js index 5960b40a2..f168f1ca9 100644 --- a/endpoint/templates/name.e2e.js +++ b/endpoint/templates/name.e2e.js @@ -8,7 +8,7 @@ var new<%= classedName %>;<% } %> describe('<%= classedName %> API:', function() { describe('GET <%= route %>', function() { - var <%= name %>s; + var <%= cameledName %>s; beforeEach(function(done) { request(app) @@ -17,13 +17,13 @@ describe('<%= classedName %> API:', function() { .expect('Content-Type', /json/) .end(function(err, res) { if (err) return done(err); - <%= name %>s = res.body; + <%= cameledName %>s = res.body; done(); }); }); it('should respond with JSON array', function() { - <%= name %>s.should.be.instanceOf(Array); + <%= cameledName %>s.should.be.instanceOf(Array); }); });<% if(filters.mongoose) { %> @@ -53,7 +53,7 @@ describe('<%= classedName %> API:', function() { }); describe('GET <%= route %>/:id', function() { - var <%= name %>; + var <%= cameledName %>; beforeEach(function(done) { request(app) @@ -62,18 +62,18 @@ describe('<%= classedName %> API:', function() { .expect('Content-Type', /json/) .end(function(err, res) { if (err) return done(err); - <%= name %> = res.body; + <%= cameledName %> = res.body; done(); }); }); afterEach(function() { - <%= name %> = {}; + <%= cameledName %> = {}; }); it('should respond with the requested <%= name %>', function() { - <%= name %>.name.should.equal('New <%= classedName %>'); - <%= name %>.info.should.equal('This is the brand new <%= name %>!!!'); + <%= cameledName %>.name.should.equal('New <%= classedName %>'); + <%= cameledName %>.info.should.equal('This is the brand new <%= name %>!!!'); }); }); From ac8f335922bd77aa0781ebd4281b8b1b9186f18d Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 14 Sep 2014 18:23:01 -0600 Subject: [PATCH 014/171] style(package): remove unneeded line break --- app/templates/_package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/templates/_package.json b/app/templates/_package.json index 2d8337b71..1912903fa 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -16,8 +16,7 @@ "jade": "~1.2.0",<% } %><% if(filters.html) { %> "ejs": "~0.8.4",<% } %><% if(filters.mongoose) { %> "mongoose": "~3.8.8", - "mongoose-bird": "~0.0.1", - <% } %><% if(filters.auth) { %> + "mongoose-bird": "~0.0.1",<% } %><% if(filters.auth) { %> "jsonwebtoken": "^0.3.0", "express-jwt": "^0.1.3", "passport": "~0.2.0", From 502be54d092ff1312a7d065da47dbe3d5ec3490e Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 14 Sep 2014 18:25:38 -0600 Subject: [PATCH 015/171] remove fixtures that should not be git ignored --- .gitignore | 4 ++-- test/fixtures/package.json | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 9e436b36f..78a6835b7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,5 @@ demo .idea .DS_Store release.txt -fixtures/bower.json -fixtures/package.json \ No newline at end of file +test/fixtures/bower.json +test/fixtures/package.json \ No newline at end of file diff --git a/test/fixtures/package.json b/test/fixtures/package.json index 2304c008e..48d48607e 100644 --- a/test/fixtures/package.json +++ b/test/fixtures/package.json @@ -16,6 +16,7 @@ "jade": "~1.2.0", "ejs": "~0.8.4", "mongoose": "~3.8.8", + "mongoose-bird": "~0.0.1", "jsonwebtoken": "^0.3.0", "express-jwt": "^0.1.3", "passport": "~0.2.0", @@ -46,7 +47,7 @@ "grunt-contrib-watch": "~0.6.1", "grunt-contrib-coffee": "^0.10.1", "grunt-contrib-jade": "^0.11.0", - "grunt-contrib-less": "^0.11.0", + "grunt-contrib-less": "^0.11.4", "grunt-google-cdn": "~0.4.0", "grunt-newer": "~0.7.0", "grunt-ng-annotate": "^0.2.3", From 65d03fc8a39590e09ef36740707fdc883739e135 Mon Sep 17 00:00:00 2001 From: kingcody Date: Sun, 17 Aug 2014 03:48:29 -0400 Subject: [PATCH 016/171] feat(app-auth): Improve client-side Auth service Changes: - getCurrentUser, isLoggedIn, and isAdmin are now sync if no arg and async with an arg - Use Error first callback signature where applicable - Remove unused arguments from Auth service - Remove isLoggedInAsync - Switch use of isLoggedInAsync to isLoggedIn - Add/Improve comments - Fix client/app/account(auth)/settings/settings.controller(js).js Breaking Changes: - Callbacks that return Errors, use 'Error first' signature Closes #456 --- app/templates/client/app/app(coffee).coffee | 4 +- app/templates/client/app/app(js).js | 4 +- .../auth(auth)/auth.service(coffee).coffee | 93 +++++++----- .../components/auth(auth)/auth.service(js).js | 140 ++++++++++-------- 4 files changed, 136 insertions(+), 105 deletions(-) diff --git a/app/templates/client/app/app(coffee).coffee b/app/templates/client/app/app(coffee).coffee index ea9ae3c95..9c0a87a0e 100644 --- a/app/templates/client/app/app(coffee).coffee +++ b/app/templates/client/app/app(coffee).coffee @@ -34,6 +34,6 @@ angular.module '<%= scriptAppName %>', [<%= angularModules %>] .run ($rootScope, $location, Auth) -> # Redirect to login if route requires auth and you're not logged in $rootScope.$on <% if(filters.ngroute) { %>'$routeChangeStart'<% } %><% if(filters.uirouter) { %>'$stateChangeStart'<% } %>, (event, next) -> - Auth.isLoggedInAsync (loggedIn) -> + Auth.isLoggedIn (loggedIn) -> $location.path "/login" if next.authenticate and not loggedIn -<% } %> \ No newline at end of file +<% } %> diff --git a/app/templates/client/app/app(js).js b/app/templates/client/app/app(js).js index eef485d7c..4e6adea0c 100644 --- a/app/templates/client/app/app(js).js +++ b/app/templates/client/app/app(js).js @@ -46,10 +46,10 @@ angular.module('<%= scriptAppName %>', [<%= angularModules %>]) .run(function ($rootScope, $location, Auth) { // Redirect to login if route requires auth and you're not logged in $rootScope.$on(<% if(filters.ngroute) { %>'$routeChangeStart'<% } %><% if(filters.uirouter) { %>'$stateChangeStart'<% } %>, function (event, next) { - Auth.isLoggedInAsync(function(loggedIn) { + Auth.isLoggedIn(function(loggedIn) { if (next.authenticate && !loggedIn) { $location.path('/login'); } }); }); - })<% } %>; \ No newline at end of file + })<% } %>; diff --git a/app/templates/client/components/auth(auth)/auth.service(coffee).coffee b/app/templates/client/components/auth(auth)/auth.service(coffee).coffee index ac503ed0b..4131efe53 100644 --- a/app/templates/client/components/auth(auth)/auth.service(coffee).coffee +++ b/app/templates/client/components/auth(auth)/auth.service(coffee).coffee @@ -1,40 +1,35 @@ 'use strict' angular.module '<%= scriptAppName %>' -.factory 'Auth', ($location, $rootScope, $http, User, $cookieStore, $q) -> +.factory 'Auth', ($http, User, $cookieStore, $q) -> currentUser = if $cookieStore.get 'token' then User.get() else {} ### Authenticate user and save token @param {Object} user - login info - @param {Function} callback - optional + @param {Function} callback - optional, function(error) @return {Promise} ### login: (user, callback) -> - deferred = $q.defer() $http.post '/auth/local', email: user.email password: user.password - .success (data) -> - $cookieStore.put 'token', data.token + .then (res) -> + $cookieStore.put 'token', res.data.token currentUser = User.get() - deferred.resolve data callback?() + res.data - .error (err) => + , (err) => @logout() - deferred.reject err - callback? err - - deferred.promise + callback? err.data + $q.reject err.data ### Delete access token and user info - - @param {Function} ### logout: -> $cookieStore.remove 'token' @@ -46,7 +41,7 @@ angular.module '<%= scriptAppName %>' Create a new user @param {Object} user - user info - @param {Function} callback - optional + @param {Function} callback - optional, function(error, user) @return {Promise} ### createUser: (user, callback) -> @@ -54,7 +49,7 @@ angular.module '<%= scriptAppName %>' (data) -> $cookieStore.put 'token', data.token currentUser = User.get() - callback? user + callback? null, user , (err) => @logout() @@ -68,7 +63,7 @@ angular.module '<%= scriptAppName %>' @param {String} oldPassword @param {String} newPassword - @param {Function} callback - optional + @param {Function} callback - optional, function(error, user) @return {Promise} ### changePassword: (oldPassword, newPassword, callback) -> @@ -79,7 +74,7 @@ angular.module '<%= scriptAppName %>' newPassword: newPassword , (user) -> - callback? user + callback? null, user , (err) -> callback? err @@ -88,45 +83,61 @@ angular.module '<%= scriptAppName %>' ### - Gets all available info on authenticated user + Gets all available info on a user + (synchronous|asynchronous) - @return {Object} user + @param {Function|*} callback - optional, funciton(user) + @return {Object|Promise} ### - getCurrentUser: -> - currentUser + getCurrentUser: (callback) -> + return currentUser if arguments.length is 0 + value = if (currentUser.hasOwnProperty("$promise")) then currentUser.$promise else currentUser + $q.when value - ### - Check if a user is logged in synchronously + .then (user) -> + callback? user + user - @return {Boolean} - ### - isLoggedIn: -> - currentUser.hasOwnProperty 'role' + , -> + callback? {} + {} ### - Waits for currentUser to resolve before checking if user is logged in + Check if a user is logged in + (synchronous|asynchronous) + + @param {Function|*} callback - optional, function(is) + @return {Bool|Promise} ### - isLoggedInAsync: (callback) -> - if currentUser.hasOwnProperty '$promise' - currentUser.$promise.then -> - callback? true - return - .catch -> - callback? false - return + isLoggedIn: (callback) -> + return currentUser.hasOwnProperty("role") if arguments.length is 0 + + @getCurrentUser null + + .then (user) -> + is_ = user.hasOwnProperty("role") + callback? is_ + is_ - else - callback? currentUser.hasOwnProperty 'role' ### Check if a user is an admin + (synchronous|asynchronous) - @return {Boolean} + @param {Function|*} callback - optional, function(is) + @return {Bool|Promise} ### - isAdmin: -> - currentUser.role is 'admin' + isAdmin: (callback) -> + return currentUser.role is "admin" if arguments_.length is 0 + + @getCurrentUser null + + .then (user) -> + is_ = user.role is "admin" + callback? is_ + is_ ### diff --git a/app/templates/client/components/auth(auth)/auth.service(js).js b/app/templates/client/components/auth(auth)/auth.service(js).js index 9afb12da9..5baf0e0b4 100644 --- a/app/templates/client/components/auth(auth)/auth.service(js).js +++ b/app/templates/client/components/auth(auth)/auth.service(js).js @@ -1,9 +1,20 @@ 'use strict'; angular.module('<%= scriptAppName %>') - .factory('Auth', function Auth($location, $rootScope, $http, User, $cookieStore, $q) { - var currentUser = {}; - if($cookieStore.get('token')) { + .factory('Auth', function Auth($http, User, $cookieStore, $q) { + /** + * Return a callback or noop function + * + * @param {Function|*} cb - a 'potential' function + * @return {Function} + */ + var safeCb = function(cb) { + return (angular.isFunction(cb)) ? cb : angular.noop; + }, + + currentUser = {}; + + if ($cookieStore.get('token')) { currentUser = User.get(); } @@ -13,36 +24,28 @@ angular.module('<%= scriptAppName %>') * Authenticate user and save token * * @param {Object} user - login info - * @param {Function} callback - optional + * @param {Function} callback - optional, function(error) * @return {Promise} */ login: function(user, callback) { - var cb = callback || angular.noop; - var deferred = $q.defer(); - - $http.post('/auth/local', { + return $http.post('/auth/local', { email: user.email, password: user.password - }). - success(function(data) { - $cookieStore.put('token', data.token); + }) + .then(function(res) { + $cookieStore.put('token', res.data.token); currentUser = User.get(); - deferred.resolve(data); - return cb(); - }). - error(function(err) { + safeCb(callback)(); + return res.data; + }, function(err) { this.logout(); - deferred.reject(err); - return cb(err); + safeCb(callback)(err.data); + return $q.reject(err.data); }.bind(this)); - - return deferred.promise; }, /** * Delete access token and user info - * - * @param {Function} */ logout: function() { $cookieStore.remove('token'); @@ -53,21 +56,19 @@ angular.module('<%= scriptAppName %>') * Create a new user * * @param {Object} user - user info - * @param {Function} callback - optional + * @param {Function} callback - optional, function(error, user) * @return {Promise} */ createUser: function(user, callback) { - var cb = callback || angular.noop; - return User.save(user, function(data) { $cookieStore.put('token', data.token); currentUser = User.get(); - return cb(user); + return safeCb(callback)(null, user); }, function(err) { this.logout(); - return cb(err); + return safeCb(callback)(err); }.bind(this)).$promise; }, @@ -76,68 +77,87 @@ angular.module('<%= scriptAppName %>') * * @param {String} oldPassword * @param {String} newPassword - * @param {Function} callback - optional + * @param {Function} callback - optional, function(error, user) * @return {Promise} */ changePassword: function(oldPassword, newPassword, callback) { - var cb = callback || angular.noop; - return User.changePassword({ id: currentUser._id }, { oldPassword: oldPassword, newPassword: newPassword }, function(user) { - return cb(user); + return safeCb(callback)(null, user); }, function(err) { - return cb(err); + return safeCb(callback)(err); }).$promise; }, /** - * Gets all available info on authenticated user + * Gets all available info on a user + * (synchronous|asynchronous) * - * @return {Object} user + * @param {Function|*} callback - optional, funciton(user) + * @return {Object|Promise} */ - getCurrentUser: function() { - return currentUser; + getCurrentUser: function(callback) { + if (arguments.length === 0) { + return currentUser; + } + + var value = (currentUser.hasOwnProperty('$promise')) ? currentUser.$promise : currentUser; + return $q.when(value) + .then(function(user) { + safeCb(callback)(user); + return user; + }, function() { + safeCb(callback)({}); + return {}; + }); }, /** * Check if a user is logged in + * (synchronous|asynchronous) * - * @return {Boolean} + * @param {Function|*} callback - optional, function(is) + * @return {Bool|Promise} */ - isLoggedIn: function() { - return currentUser.hasOwnProperty('role'); - }, + isLoggedIn: function(callback) { + if (arguments.length === 0) { + return currentUser.hasOwnProperty('role'); + } - /** - * Waits for currentUser to resolve before checking if user is logged in - */ - isLoggedInAsync: function(cb) { - if(currentUser.hasOwnProperty('$promise')) { - currentUser.$promise.then(function() { - cb(true); - }).catch(function() { - cb(false); + return this.getCurrentUser(null) + .then(function(user) { + var is = user.hasOwnProperty('role'); + safeCb(callback)(is); + return is; }); - } else if(currentUser.hasOwnProperty('role')) { - cb(true); - } else { - cb(false); - } }, - /** - * Check if a user is an admin - * - * @return {Boolean} - */ - isAdmin: function() { - return currentUser.role === 'admin'; + /** + * Check if a user is an admin + * (synchronous|asynchronous) + * + * @param {Function|*} callback - optional, function(is) + * @return {Bool|Promise} + */ + isAdmin: function(callback) { + if (arguments.length === 0) { + return currentUser.role === 'admin'; + } + + return this.getCurrentUser(null) + .then(function(user) { + var is = user.role === 'admin'; + safeCb(callback)(is); + return is; + }); }, /** * Get auth token + * + * @return {String} - a token string used for authenticating */ getToken: function() { return $cookieStore.get('token'); From 2aaff12d3559770ee2e9bdd8a856ef808e713010 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Tue, 16 Sep 2014 23:41:46 -0600 Subject: [PATCH 017/171] fix typo --- .../client/components/auth(auth)/auth.service(coffee).coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/client/components/auth(auth)/auth.service(coffee).coffee b/app/templates/client/components/auth(auth)/auth.service(coffee).coffee index 4131efe53..1d4b29544 100644 --- a/app/templates/client/components/auth(auth)/auth.service(coffee).coffee +++ b/app/templates/client/components/auth(auth)/auth.service(coffee).coffee @@ -130,7 +130,7 @@ angular.module '<%= scriptAppName %>' @return {Bool|Promise} ### isAdmin: (callback) -> - return currentUser.role is "admin" if arguments_.length is 0 + return currentUser.role is "admin" if arguments.length is 0 @getCurrentUser null From 6aadee6d956cefb9a787eb0851d287654dfca111 Mon Sep 17 00:00:00 2001 From: kingcody Date: Fri, 15 Aug 2014 02:50:09 -0400 Subject: [PATCH 018/171] feat(app-routing): improve app routing Changes: - Use `ui-sref` instead of `href` or `ng-href` when ui-router is chosen - Use `ui-sref-active` instead of `ng-class='{active: isActive()}'` when ui-router is chosen - Use `$state.go()` where applicable, when ui-router is chosen - Use `$scope.menu[n].state` instead of `$scope.menu[n].link` when ui-router is chosen (attempt to remove possible confusion) - Omit `$scope.isActive` when ui-router is chosen - Simplify `navbar(jade).jade` templating (remove extra `<% if (filters.auth) %>` tag) - Add `/logout` route for both ng-route and ui-router - Use `$routeChangeStart` or `$stateChangeStart` to pass refering route or state to `/logout` - Add `stateMock` for testing `$state` transitions - Use `stateMock` in main.controller for expecting template requests from state transistions Closes #331 --- .../app/account(auth)/account(coffee).coffee | 26 ++++++++++++- .../client/app/account(auth)/account(js).js | 38 ++++++++++++++++++- .../app/account(auth)/login/login(html).html | 2 +- .../app/account(auth)/login/login(jade).jade | 2 +- .../login/login.controller(coffee).coffee | 4 +- .../login/login.controller(js).js | 4 +- .../account(auth)/signup/signup(html).html | 2 +- .../account(auth)/signup/signup(jade).jade | 2 +- .../signup/signup.controller(coffee).coffee | 4 +- .../signup/signup.controller(js).js | 4 +- app/templates/client/app/app(coffee).coffee | 11 +++--- app/templates/client/app/app(js).js | 15 ++++---- .../main/main.controller.spec(coffee).coffee | 13 ++++--- .../app/main/main.controller.spec(js).js | 11 ++++-- .../components/navbar/navbar(html).html | 14 +++---- .../components/navbar/navbar(jade).jade | 24 ++++++------ .../navbar/navbar.controller(coffee).coffee | 12 ++---- .../navbar/navbar.controller(js).js | 15 +++----- .../ui-router.mock(coffee).coffee | 26 +++++++++++++ .../ui-router(uirouter)/ui-router.mock(js).js | 34 +++++++++++++++++ 20 files changed, 191 insertions(+), 72 deletions(-) create mode 100644 app/templates/client/components/ui-router(uirouter)/ui-router.mock(coffee).coffee create mode 100644 app/templates/client/components/ui-router(uirouter)/ui-router.mock(js).js diff --git a/app/templates/client/app/account(auth)/account(coffee).coffee b/app/templates/client/app/account(auth)/account(coffee).coffee index 2b7b8b23b..088fb6840 100644 --- a/app/templates/client/app/account(auth)/account(coffee).coffee +++ b/app/templates/client/app/account(auth)/account(coffee).coffee @@ -7,6 +7,14 @@ angular.module '<%= scriptAppName %>' templateUrl: 'app/account/login/login.html' controller: 'LoginCtrl' + .when '/logout', + name: 'logout' + referrer: '/' + controller: ($location, $route, Auth) -> + referrer = $route.current.params.referrer or $route.current.referrer or "/" + Auth.logout() + $location.path referrer + .when '/signup', templateUrl: 'app/account/signup/signup.html' controller: 'SignupCtrl' @@ -15,6 +23,10 @@ angular.module '<%= scriptAppName %>' templateUrl: 'app/account/settings/settings.html' controller: 'SettingsCtrl' authenticate: true + +.run ($rootScope) -> + $rootScope.$on '$routeChangeStart', (event, next, current) -> + next.referrer = current.originalPath if next.name is "logout" and current and current.originalPath and not current.authenticate <% } %><% if(filters.uirouter) { %>.config ($stateProvider) -> $stateProvider .state 'login', @@ -22,6 +34,14 @@ angular.module '<%= scriptAppName %>' templateUrl: 'app/account/login/login.html' controller: 'LoginCtrl' + .state 'logout', + url: '/logout?referrer' + referrer: 'main' + controller: ($state, Auth) -> + referrer = $state.params.referrer or $state.current.referrer or "main" + Auth.logout() + $state.go referrer + .state 'signup', url: '/signup' templateUrl: 'app/account/signup/signup.html' @@ -32,4 +52,8 @@ angular.module '<%= scriptAppName %>' templateUrl: 'app/account/settings/settings.html' controller: 'SettingsCtrl' authenticate: true -<% } %> \ No newline at end of file + +.run ($rootScope) -> + $rootScope.$on '$stateChangeStart', (event, next, nextParams, current) -> + next.referrer = current.name if next.name is "logout" and current and current.name and not current.authenticate +<% } %> diff --git a/app/templates/client/app/account(auth)/account(js).js b/app/templates/client/app/account(auth)/account(js).js index 0e30543a5..d31ff19d2 100644 --- a/app/templates/client/app/account(auth)/account(js).js +++ b/app/templates/client/app/account(auth)/account(js).js @@ -7,6 +7,17 @@ angular.module('<%= scriptAppName %>') templateUrl: 'app/account/login/login.html', controller: 'LoginCtrl' }) + .when('/logout', { + name: 'logout', + referrer: '/', + controller: function($location, $route, Auth) { + var referrer = $route.current.params.referrer || + $route.current.referrer || + '/'; + Auth.logout(); + $location.path(referrer); + } + }) .when('/signup', { templateUrl: 'app/account/signup/signup.html', controller: 'SignupCtrl' @@ -16,6 +27,13 @@ angular.module('<%= scriptAppName %>') controller: 'SettingsCtrl', authenticate: true }); + }) + .run(function($rootScope) { + $rootScope.$on('$routeChangeStart', function(event, next, current) { + if (next.name === 'logout' && current && current.originalPath && !current.authenticate) { + next.referrer = current.originalPath; + } + }); });<% } %><% if(filters.uirouter) { %>.config(function ($stateProvider) { $stateProvider .state('login', { @@ -23,6 +41,17 @@ angular.module('<%= scriptAppName %>') templateUrl: 'app/account/login/login.html', controller: 'LoginCtrl' }) + .state('logout', { + url: '/logout?referrer', + referrer: 'main', + controller: function($state, Auth) { + var referrer = $state.params.referrer || + $state.current.referrer || + 'main'; + Auth.logout(); + $state.go(referrer); + } + }) .state('signup', { url: '/signup', templateUrl: 'app/account/signup/signup.html', @@ -34,4 +63,11 @@ angular.module('<%= scriptAppName %>') controller: 'SettingsCtrl', authenticate: true }); - });<% } %> \ No newline at end of file + }) + .run(function($rootScope) { + $rootScope.$on('$stateChangeStart', function(event, next, nextParams, current) { + if (next.name === 'logout' && current && current.name && !current.authenticate) { + next.referrer = current.name; + } + }); + });<% } %> diff --git a/app/templates/client/app/account(auth)/login/login(html).html b/app/templates/client/app/account(auth)/login/login(html).html index 572f2e144..49a81b55d 100644 --- a/app/templates/client/app/account(auth)/login/login(html).html +++ b/app/templates/client/app/account(auth)/login/login(html).html @@ -37,7 +37,7 @@

Login

- + ui-sref="signup"<% } else { %>href="/signup"<% } %>> Register diff --git a/app/templates/client/app/account(auth)/login/login(jade).jade b/app/templates/client/app/account(auth)/login/login(jade).jade index 4b13c0b13..686b1769e 100644 --- a/app/templates/client/app/account(auth)/login/login(jade).jade +++ b/app/templates/client/app/account(auth)/login/login(jade).jade @@ -34,7 +34,7 @@ div(ng-include='"components/navbar/navbar.html"') button.btn.btn-inverse.btn-lg.btn-login(type='submit') | Login = ' ' - a.btn.btn-default.btn-lg.btn-register(href='/signup') + a.btn.btn-default.btn-lg.btn-register(<% if(filters.uirouter) { %>ui-sref='signup'<% } else { %>href='/signup'<% } %>) | Register <% if(filters.oauth) {%> hr diff --git a/app/templates/client/app/account(auth)/login/login.controller(coffee).coffee b/app/templates/client/app/account(auth)/login/login.controller(coffee).coffee index 3f90c25d7..036191f93 100644 --- a/app/templates/client/app/account(auth)/login/login.controller(coffee).coffee +++ b/app/templates/client/app/account(auth)/login/login.controller(coffee).coffee @@ -1,7 +1,7 @@ 'use strict' angular.module '<%= scriptAppName %>' -.controller 'LoginCtrl', ($scope, Auth, $location<% if(filters.oauth) {%>, $window<% } %>) -> +.controller 'LoginCtrl', ($scope, Auth<% if(filters.ngroute) { %>, $location<% } %><% if(filters.uirouter) { %>, $state<% } %><% if(filters.oauth) {%>, $window<% } %>) -> $scope.user = {} $scope.errors = {} $scope.login = (form) -> @@ -14,7 +14,7 @@ angular.module '<%= scriptAppName %>' password: $scope.user.password .then -> - $location.path '/' + <% if(filters.ngroute) { %>$location.path '/'<% } %><% if(filters.uirouter) { %>$state.go 'main'<% } %> .catch (err) -> $scope.errors.other = err.message diff --git a/app/templates/client/app/account(auth)/login/login.controller(js).js b/app/templates/client/app/account(auth)/login/login.controller(js).js index 7b13da384..e2c5dcaa4 100644 --- a/app/templates/client/app/account(auth)/login/login.controller(js).js +++ b/app/templates/client/app/account(auth)/login/login.controller(js).js @@ -1,7 +1,7 @@ 'use strict'; angular.module('<%= scriptAppName %>') - .controller('LoginCtrl', function ($scope, Auth, $location<% if (filters.oauth) { %>, $window<% } %>) { + .controller('LoginCtrl', function ($scope, Auth<% if(filters.ngroute) { %>, $location<% } %><% if(filters.uirouter) { %>, $state<% } %><% if (filters.oauth) { %>, $window<% } %>) { $scope.user = {}; $scope.errors = {}; @@ -15,7 +15,7 @@ angular.module('<%= scriptAppName %>') }) .then( function() { // Logged in, redirect to home - $location.path('/'); + <% if(filters.ngroute) { %>$location.path('/');<% } %><% if(filters.uirouter) { %>$state.go('main');<% } %> }) .catch( function(err) { $scope.errors.other = err.message; diff --git a/app/templates/client/app/account(auth)/signup/signup(html).html b/app/templates/client/app/account(auth)/signup/signup(html).html index 59faed568..28d6c39ab 100644 --- a/app/templates/client/app/account(auth)/signup/signup(html).html +++ b/app/templates/client/app/account(auth)/signup/signup(html).html @@ -58,7 +58,7 @@

Sign up

- + ui-sref="login"<% } else { %>href="/login"<% } %>> Login diff --git a/app/templates/client/app/account(auth)/signup/signup(jade).jade b/app/templates/client/app/account(auth)/signup/signup(jade).jade index 43815a21c..7e21b101c 100644 --- a/app/templates/client/app/account(auth)/signup/signup(jade).jade +++ b/app/templates/client/app/account(auth)/signup/signup(jade).jade @@ -36,7 +36,7 @@ div(ng-include='"components/navbar/navbar.html"') button.btn.btn-inverse.btn-lg.btn-login(type='submit') | Sign up = ' ' - a.btn.btn-default.btn-lg.btn-register(href='/login') + a.btn.btn-default.btn-lg.btn-register(<% if(filters.uirouter) { %>ui-sref='login'<% } else { %>href='/login'<% } %>) | Login <% if(filters.oauth) {%> diff --git a/app/templates/client/app/account(auth)/signup/signup.controller(coffee).coffee b/app/templates/client/app/account(auth)/signup/signup.controller(coffee).coffee index 1b9c9696f..ac240faa8 100644 --- a/app/templates/client/app/account(auth)/signup/signup.controller(coffee).coffee +++ b/app/templates/client/app/account(auth)/signup/signup.controller(coffee).coffee @@ -1,7 +1,7 @@ 'use strict' angular.module '<%= scriptAppName %>' -.controller 'SignupCtrl', ($scope, Auth, $location<% if(filters.oauth) {%>, $window<% } %>) -> +.controller 'SignupCtrl', ($scope, Auth<% if(filters.ngroute) { %>, $location<% } %><% if(filters.uirouter) { %>, $state<% } %><% if(filters.oauth) {%>, $window<% } %>) -> $scope.user = {} $scope.errors = {} $scope.register = (form) -> @@ -15,7 +15,7 @@ angular.module '<%= scriptAppName %>' password: $scope.user.password .then -> - $location.path '/' + <% if(filters.ngroute) { %>$location.path '/'<% } %><% if(filters.uirouter) { %>$state.go 'main'<% } %> .catch (err) -> err = err.data diff --git a/app/templates/client/app/account(auth)/signup/signup.controller(js).js b/app/templates/client/app/account(auth)/signup/signup.controller(js).js index 7d6ba3d38..10685079d 100644 --- a/app/templates/client/app/account(auth)/signup/signup.controller(js).js +++ b/app/templates/client/app/account(auth)/signup/signup.controller(js).js @@ -1,7 +1,7 @@ 'use strict'; angular.module('<%= scriptAppName %>') - .controller('SignupCtrl', function ($scope, Auth, $location<% if (filters.oauth) { %>, $window<% } %>) { + .controller('SignupCtrl', function ($scope, Auth<% if(filters.ngroute) { %>, $location<% } %><% if(filters.uirouter) { %>, $state<% } %><% if (filters.oauth) { %>, $window<% } %>) { $scope.user = {}; $scope.errors = {}; @@ -16,7 +16,7 @@ angular.module('<%= scriptAppName %>') }) .then( function() { // Account created, redirect to home - $location.path('/'); + <% if(filters.ngroute) { %>$location.path('/');<% } %><% if(filters.uirouter) { %>$state.go('main');<% } %> }) .catch( function(err) { err = err.data; diff --git a/app/templates/client/app/app(coffee).coffee b/app/templates/client/app/app(coffee).coffee index 9c0a87a0e..cb6d0c5b1 100644 --- a/app/templates/client/app/app(coffee).coffee +++ b/app/templates/client/app/app(coffee).coffee @@ -15,8 +15,9 @@ angular.module '<%= scriptAppName %>', [<%= angularModules %>] $locationProvider.html5Mode true<% if(filters.auth) { %> $httpProvider.interceptors.push 'authInterceptor'<% } %> <% } %><% if(filters.auth) { %> -.factory 'authInterceptor', ($rootScope, $q, $cookieStore, $location) -> - # Add authorization token to headers +.factory 'authInterceptor', ($rootScope, $q, $cookieStore<% if(filters.ngroute) { %>, $location<% } if(filters.uirouter) { %>, $injector<% } %>) -> + <% if(filters.uirouter) { %>state = null + <% } %># Add authorization token to headers request: (config) -> config.headers = config.headers or {} config.headers.Authorization = 'Bearer ' + $cookieStore.get 'token' if $cookieStore.get 'token' @@ -25,15 +26,15 @@ angular.module '<%= scriptAppName %>', [<%= angularModules %>] # Intercept 401s and redirect you to login responseError: (response) -> if response.status is 401 - $location.path '/login' + <% if(filters.ngroute) { %>$location.path '/login'<% } if(filters.uirouter) { %>(state || state = $injector.get '$state').go 'login'<% } %> # remove any stale tokens $cookieStore.remove 'token' $q.reject response -.run ($rootScope, $location, Auth) -> +.run ($rootScope<% if(filters.ngroute) { %>, $location<% } %><% if(filters.uirouter) { %>, $state<% } %>, Auth) -> # Redirect to login if route requires auth and you're not logged in $rootScope.$on <% if(filters.ngroute) { %>'$routeChangeStart'<% } %><% if(filters.uirouter) { %>'$stateChangeStart'<% } %>, (event, next) -> Auth.isLoggedIn (loggedIn) -> - $location.path "/login" if next.authenticate and not loggedIn + <% if(filters.ngroute) { %>$location.path '/login'<% } %><% if(filters.uirouter) { %>$state.go 'login'<% } %> if next.authenticate and not loggedIn <% } %> diff --git a/app/templates/client/app/app(js).js b/app/templates/client/app/app(js).js index 4e6adea0c..35b8ad327 100644 --- a/app/templates/client/app/app(js).js +++ b/app/templates/client/app/app(js).js @@ -9,16 +9,17 @@ angular.module('<%= scriptAppName %>', [<%= angularModules %>]) $locationProvider.html5Mode(true);<% if(filters.auth) { %> $httpProvider.interceptors.push('authInterceptor');<% } %> - })<% } %><% if(filters.uirouter) { %>.config(function ($stateProvider, $urlRouterProvider, $locationProvider<% if(filters.auth) { %>, $httpProvider<% } %>) { + })<% } if(filters.uirouter) { %>.config(function ($stateProvider, $urlRouterProvider, $locationProvider<% if(filters.auth) { %>, $httpProvider<% } %>) { $urlRouterProvider .otherwise('/'); $locationProvider.html5Mode(true);<% if(filters.auth) { %> $httpProvider.interceptors.push('authInterceptor');<% } %> - })<% } %><% if(filters.auth) { %> + })<% } if(filters.auth) { %> - .factory('authInterceptor', function ($rootScope, $q, $cookieStore, $location) { - return { + .factory('authInterceptor', function ($rootScope, $q, $cookieStore<% if(filters.ngroute) { %>, $location<% } if(filters.uirouter) { %>, $injector<% } %>) { + <% if(filters.uirouter) { %>var state; + <% } %>return { // Add authorization token to headers request: function (config) { config.headers = config.headers || {}; @@ -31,7 +32,7 @@ angular.module('<%= scriptAppName %>', [<%= angularModules %>]) // Intercept 401s and redirect you to login responseError: function(response) { if(response.status === 401) { - $location.path('/login'); + <% if(filters.ngroute) { %>$location.path('/login');<% } if(filters.uirouter) { %>(state || (state = $injector.get('$state'))).go('login');<% } %> // remove any stale tokens $cookieStore.remove('token'); return $q.reject(response); @@ -43,12 +44,12 @@ angular.module('<%= scriptAppName %>', [<%= angularModules %>]) }; }) - .run(function ($rootScope, $location, Auth) { + .run(function ($rootScope<% if(filters.ngroute) { %>, $location<% } if(filters.uirouter) { %>, $state<% } %>, Auth) { // Redirect to login if route requires auth and you're not logged in $rootScope.$on(<% if(filters.ngroute) { %>'$routeChangeStart'<% } %><% if(filters.uirouter) { %>'$stateChangeStart'<% } %>, function (event, next) { Auth.isLoggedIn(function(loggedIn) { if (next.authenticate && !loggedIn) { - $location.path('/login'); + <% if(filters.ngroute) { %>$location.path('/login');<% } if(filters.uirouter) { %>$state.go('login');<% } %> } }); }); diff --git a/app/templates/client/app/main/main.controller.spec(coffee).coffee b/app/templates/client/app/main/main.controller.spec(coffee).coffee index efe9b39a6..f974a081d 100644 --- a/app/templates/client/app/main/main.controller.spec(coffee).coffee +++ b/app/templates/client/app/main/main.controller.spec(coffee).coffee @@ -3,15 +3,17 @@ describe 'Controller: MainCtrl', -> # load the controller's module - beforeEach module '<%= scriptAppName %>' <% if(filters.socketio) {%> + beforeEach module '<%= scriptAppName %>' <% if(filters.uirouter) {%> + beforeEach module 'stateMock' <% } %><% if(filters.socketio) {%> beforeEach module 'socketMock' <% } %> MainCtrl = undefined - scope = undefined + scope = undefined<% if(filters.uirouter) {%> + state = undefined<% } %> $httpBackend = undefined # Initialize the controller and a mock scope - beforeEach inject (_$httpBackend_, $controller, $rootScope) -> + beforeEach inject (_$httpBackend_, $controller, $rootScope<% if(filters.uirouter) {%>, $state<% } %>) -> $httpBackend = _$httpBackend_ $httpBackend.expectGET('/api/things').respond [ 'HTML5 Boilerplate' @@ -19,10 +21,11 @@ describe 'Controller: MainCtrl', -> 'Karma' 'Express' ] - scope = $rootScope.$new() + scope = $rootScope.$new()<% if(filters.uirouter) {%> + state = $state<% } %> MainCtrl = $controller 'MainCtrl', $scope: scope it 'should attach a list of things to the scope', -> $httpBackend.flush() - expect(scope.awesomeThings.length).toBe 4 \ No newline at end of file + expect(scope.awesomeThings.length).toBe 4 diff --git a/app/templates/client/app/main/main.controller.spec(js).js b/app/templates/client/app/main/main.controller.spec(js).js index 373e9db08..21b8aba70 100644 --- a/app/templates/client/app/main/main.controller.spec(js).js +++ b/app/templates/client/app/main/main.controller.spec(js).js @@ -3,20 +3,23 @@ describe('Controller: MainCtrl', function () { // load the controller's module - beforeEach(module('<%= scriptAppName %>'));<% if(filters.socketio) {%> + beforeEach(module('<%= scriptAppName %>'));<% if(filters.uirouter) {%> + beforeEach(module('stateMock'));<% } %><% if(filters.socketio) {%> beforeEach(module('socketMock'));<% } %> var MainCtrl, - scope, + scope,<% if(filters.uirouter) {%> + state,<% } %> $httpBackend; // Initialize the controller and a mock scope - beforeEach(inject(function (_$httpBackend_, $controller, $rootScope) { + beforeEach(inject(function (_$httpBackend_, $controller, $rootScope<% if(filters.uirouter) {%>, $state<% } %>) { $httpBackend = _$httpBackend_; $httpBackend.expectGET('/api/things') .respond(['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']); - scope = $rootScope.$new(); + scope = $rootScope.$new();<% if(filters.uirouter) {%> + state = $state;<% } %> MainCtrl = $controller('MainCtrl', { $scope: scope }); diff --git a/app/templates/client/components/navbar/navbar(html).html b/app/templates/client/components/navbar/navbar(html).html index 71f8606dd..a93839562 100644 --- a/app/templates/client/components/navbar/navbar(html).html +++ b/app/templates/client/components/navbar/navbar(html).html @@ -11,18 +11,18 @@ diff --git a/app/templates/client/components/navbar/navbar(jade).jade b/app/templates/client/components/navbar/navbar(jade).jade index 2b17f29c3..7863f4e0a 100644 --- a/app/templates/client/components/navbar/navbar(jade).jade +++ b/app/templates/client/components/navbar/navbar(jade).jade @@ -10,25 +10,25 @@ div.navbar.navbar-default.navbar-static-top(ng-controller='NavbarCtrl') div#navbar-main.navbar-collapse.collapse(collapse='isCollapsed') ul.nav.navbar-nav - li(ng-repeat='item in menu', ng-class='{active: isActive(item.link)}') - a(ng-href='{{item.link}}') {{item.title}}<% if(filters.auth) { %> + li(ng-repeat='item in menu', <% if(filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive(item.link)}'<% } %>) + a(<% if(filters.uirouter) { %>ui-sref='{{item.state}}'<% } else { %>ng-href='{{item.link}}'<% } %>) {{item.title}}<% if(filters.auth) { %> - li(ng-show='isAdmin()', ng-class='{active: isActive("/admin")}') - a(href='/admin') Admin<% } %><% if(filters.auth) { %> + li(ng-show='isAdmin()', <% if(filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive("/admin")}'<% } %>) + a(<% if(filters.uirouter) { %>ui-sref='admin'<% } else { %>href='/admin'<% } %>) Admin ul.nav.navbar-nav.navbar-right - li(ng-hide='isLoggedIn()', ng-class='{active: isActive("/signup")}') - a(href='/signup') Sign up + li(ng-hide='isLoggedIn()', <% if(filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive("/signup")}'<% } %>) + a(<% if(filters.uirouter) { %>ui-sref='signup'<% } else { %>href='/signup'<% } %>) Sign up - li(ng-hide='isLoggedIn()', ng-class='{active: isActive("/login")}') - a(href='/login') Login + li(ng-hide='isLoggedIn()', <% if(filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive("/login")}'<% } %>) + a(<% if(filters.uirouter) { %>ui-sref='login'<% } else { %>href='/login'<% } %>) Login li(ng-show='isLoggedIn()') p.navbar-text Hello {{ getCurrentUser().name }} - li(ng-show='isLoggedIn()', ng-class='{active: isActive("/settings")}') - a(href='/settings') + li(ng-show='isLoggedIn()', <% if(filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive("/settings")}'<% } %>) + a(<% if(filters.uirouter) { %>ui-sref='settings'<% } else { %>href='/settings'<% } %>) span.glyphicon.glyphicon-cog - li(ng-show='isLoggedIn()', ng-class='{active: isActive("/logout")}') - a(href='', ng-click='logout()') Logout<% } %> \ No newline at end of file + li(ng-show='isLoggedIn()') + a(<% if(filters.uirouter) { %>ui-sref='logout'<% } else { %>href='/logout'<% } %>) Logout<% } %> \ No newline at end of file diff --git a/app/templates/client/components/navbar/navbar.controller(coffee).coffee b/app/templates/client/components/navbar/navbar.controller(coffee).coffee index d3804c5eb..121437cf1 100644 --- a/app/templates/client/components/navbar/navbar.controller(coffee).coffee +++ b/app/templates/client/components/navbar/navbar.controller(coffee).coffee @@ -1,19 +1,15 @@ 'use strict' angular.module '<%= scriptAppName %>' -.controller 'NavbarCtrl', ($scope, $location<% if(filters.auth) {%>, Auth<% } %>) -> +.controller 'NavbarCtrl', ($scope<% if(!filters.uirouter) { %>, $location<% } %><% if(filters.auth) {%>, Auth<% } %>) -> $scope.menu = [ title: 'Home' - link: '/' + <% if(filters.uirouter) { %>state: 'main'<% } else { %>link: '/'<% } %> ] $scope.isCollapsed = true<% if(filters.auth) {%> $scope.isLoggedIn = Auth.isLoggedIn $scope.isAdmin = Auth.isAdmin - $scope.getCurrentUser = Auth.getCurrentUser - - $scope.logout = -> - Auth.logout() - $location.path '/login'<% } %> + $scope.getCurrentUser = Auth.getCurrentUser<% } %><% if(!filters.uirouter) { %> $scope.isActive = (route) -> - route is $location.path() \ No newline at end of file + route is $location.path()<% } %> \ No newline at end of file diff --git a/app/templates/client/components/navbar/navbar.controller(js).js b/app/templates/client/components/navbar/navbar.controller(js).js index 4ce9dbcb5..2428ac15b 100644 --- a/app/templates/client/components/navbar/navbar.controller(js).js +++ b/app/templates/client/components/navbar/navbar.controller(js).js @@ -1,23 +1,18 @@ 'use strict'; angular.module('<%= scriptAppName %>') - .controller('NavbarCtrl', function ($scope, $location<% if(filters.auth) {%>, Auth<% } %>) { + .controller('NavbarCtrl', function ($scope<% if(!filters.uirouter) { %>, $location<% } %><% if(filters.auth) {%>, Auth<% } %>) { $scope.menu = [{ 'title': 'Home', - 'link': '/' + <% if(filters.uirouter) { %>'state': 'main'<% } else { %>'link': '/'<% } %> }]; $scope.isCollapsed = true;<% if(filters.auth) {%> $scope.isLoggedIn = Auth.isLoggedIn; $scope.isAdmin = Auth.isAdmin; - $scope.getCurrentUser = Auth.getCurrentUser; - - $scope.logout = function() { - Auth.logout(); - $location.path('/login'); - };<% } %> + $scope.getCurrentUser = Auth.getCurrentUser;<% } %><% if(!filters.uirouter) { %> $scope.isActive = function(route) { return route === $location.path(); - }; - }); \ No newline at end of file + };<% } %> + }); diff --git a/app/templates/client/components/ui-router(uirouter)/ui-router.mock(coffee).coffee b/app/templates/client/components/ui-router(uirouter)/ui-router.mock(coffee).coffee new file mode 100644 index 000000000..ff3937c35 --- /dev/null +++ b/app/templates/client/components/ui-router(uirouter)/ui-router.mock(coffee).coffee @@ -0,0 +1,26 @@ +'use strict' + +angular.module 'stateMock', [] +angular.module('stateMock').service '$state', ($q) -> + @expectedTransitions = [] + + @transitionTo = (stateName) -> + if @expectedTransitions.length > 0 + expectedState = @expectedTransitions.shift() + throw Error('Expected transition to state: ' + expectedState + ' but transitioned to ' + stateName) if expectedState isnt stateName + else + throw Error('No more transitions were expected! Tried to transition to ' + stateName) + console.log 'Mock transition to: ' + stateName + deferred = $q.defer() + promise = deferred.promise + deferred.resolve() + promise + + @go = @transitionTo + + @expectTransitionTo = (stateName) -> + @expectedTransitions.push stateName + + @ensureAllTransitionsHappened = -> + throw Error('Not all transitions happened!') if @expectedTransitions.length > 0 + @ diff --git a/app/templates/client/components/ui-router(uirouter)/ui-router.mock(js).js b/app/templates/client/components/ui-router(uirouter)/ui-router.mock(js).js new file mode 100644 index 000000000..a5a1bf413 --- /dev/null +++ b/app/templates/client/components/ui-router(uirouter)/ui-router.mock(js).js @@ -0,0 +1,34 @@ +'use strict'; + +angular.module('stateMock', []); +angular.module('stateMock').service('$state', function($q) { + this.expectedTransitions = []; + + this.transitionTo = function(stateName) { + if (this.expectedTransitions.length > 0) { + var expectedState = this.expectedTransitions.shift(); + if (expectedState !== stateName) { + throw Error('Expected transition to state: ' + expectedState + ' but transitioned to ' + stateName); + } + } else { + throw Error('No more transitions were expected! Tried to transition to ' + stateName); + } + console.log('Mock transition to: ' + stateName); + var deferred = $q.defer(); + var promise = deferred.promise; + deferred.resolve(); + return promise; + }; + + this.go = this.transitionTo; + + this.expectTransitionTo = function(stateName) { + this.expectedTransitions.push(stateName); + }; + + this.ensureAllTransitionsHappened = function() { + if (this.expectedTransitions.length > 0) { + throw Error('Not all transitions happened!'); + } + }; +}); From fb28c28daf8ac777f15644bc2cde2fd149f90788 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Tue, 16 Sep 2014 23:22:45 -0600 Subject: [PATCH 019/171] expect uirouter mocks in tests --- test/test-file-creation.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/test-file-creation.js b/test/test-file-creation.js index b6171fabd..05e83c77f 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -174,11 +174,17 @@ describe('angular-fullstack generator', function () { 'protractor.conf.js' ]); + if (ops.router === 'uirouter') { + files = files.concat([ + 'client/components/ui-router/ui-router.mock.' + script + ]); + } + if (ops.uibootstrap) { files = files.concat([ 'client/components/modal/modal.' + markup, 'client/components/modal/modal.' + stylesheet, - 'client/components/modal/modal.service.' + script, + 'client/components/modal/modal.service.' + script ]); } From f0e568a9b427b9fc67eef4f8a2ec752a10bdead3 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sat, 20 Sep 2014 19:31:16 -0600 Subject: [PATCH 020/171] rename server side e2e tests to integration tests --- app/templates/Gruntfile.js | 24 +++++++++---------- .../{thing.e2e.js => thing.integration.js} | 0 .../{user.e2e.js => user.integration.js} | 0 .../{name.e2e.js => name.integration.js} | 0 4 files changed, 12 insertions(+), 12 deletions(-) rename app/templates/server/api/thing/{thing.e2e.js => thing.integration.js} (100%) rename app/templates/server/api/user(auth)/{user.e2e.js => user.integration.js} (100%) rename endpoint/templates/{name.e2e.js => name.integration.js} (100%) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index a72f42dae..6cd422daa 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -170,14 +170,14 @@ module.exports = function (grunt) { }, src: [ 'server/**/*.js', - '!server/**/*.{spec,e2e}.js' + '!server/**/*.{spec,integration}.js' ] }, serverTest: { options: { jshintrc: 'server/.jshintrc-spec' }, - src: ['server/**/*.{spec,e2e}.js'] + src: ['server/**/*.{spec,integration}.js'] }, all: [ '<%%= yeoman.client %>/{app,components}/**/*.js', @@ -493,8 +493,8 @@ module.exports = function (grunt) { unit: { src: ['server/**/*.spec.js'] }, - e2e: { - src: ['server/**/*.e2e.js'] + integration: { + src: ['server/**/*.integration.js'] } }, @@ -504,7 +504,7 @@ module.exports = function (grunt) { excludes: [ '**/*.spec.js', '**/*.mock.js', - '**/*.e2e.js' + '**/*.integration.js' ], reporter: 'spec', require: ['mocha.conf.js'], @@ -513,17 +513,17 @@ module.exports = function (grunt) { }, src: 'server' }, - e2e: { + integration: { options: { excludes: [ '**/*.spec.js', '**/*.mock.js', - '**/*.e2e.js' + '**/*.integration.js' ], reporter: 'spec', require: ['mocha.conf.js'], - mask: '**/*.e2e.js', - coverageFolder: 'coverage/server/e2e' + mask: '**/*.integration.js', + coverageFolder: 'coverage/server/integration' }, src: 'server' } @@ -822,7 +822,7 @@ module.exports = function (grunt) { 'env:all', 'env:test', 'mochaTest:unit', - 'mochaTest:e2e' + 'mochaTest:integration' ]); } @@ -867,11 +867,11 @@ module.exports = function (grunt) { ]); } - else if (option === 'e2e') { + else if (option === 'integration') { return grunt.task.run([ 'env:all', 'env:test', - 'mocha_istanbul:e2e' + 'mocha_istanbul:integration' ]); } diff --git a/app/templates/server/api/thing/thing.e2e.js b/app/templates/server/api/thing/thing.integration.js similarity index 100% rename from app/templates/server/api/thing/thing.e2e.js rename to app/templates/server/api/thing/thing.integration.js diff --git a/app/templates/server/api/user(auth)/user.e2e.js b/app/templates/server/api/user(auth)/user.integration.js similarity index 100% rename from app/templates/server/api/user(auth)/user.e2e.js rename to app/templates/server/api/user(auth)/user.integration.js diff --git a/endpoint/templates/name.e2e.js b/endpoint/templates/name.integration.js similarity index 100% rename from endpoint/templates/name.e2e.js rename to endpoint/templates/name.integration.js From f1b5b9a101f600dae3289960942c290169e280e4 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sat, 20 Sep 2014 20:53:10 -0600 Subject: [PATCH 021/171] style(tests): use consistent code style for route tests, clarify names --- app/templates/server/api/thing/index.spec.js | 64 +++++++------- .../server/api/user(auth)/index.spec.js | 84 +++++++++---------- endpoint/templates/index.spec.js | 64 +++++++------- 3 files changed, 106 insertions(+), 106 deletions(-) diff --git a/app/templates/server/api/thing/index.spec.js b/app/templates/server/api/thing/index.spec.js index e62feff60..3c6c71fc2 100644 --- a/app/templates/server/api/thing/index.spec.js +++ b/app/templates/server/api/thing/index.spec.js @@ -2,42 +2,42 @@ var proxyquire = require('proxyquire').noPreserveCache(); - /* thing.controller stub */ -var thingCtrl = { - index: 'thingCtrl.index'<% if(filters.mongoose) { %>, - show: 'thingCtrl.show', - create: 'thingCtrl.create', - update: 'thingCtrl.update', - destroy: 'thingCtrl.destroy'<% } %> - }, - /* express.Router().router stub */ - router = { - get: sinon.spy()<% if(filters.mongoose) { %>, - put: sinon.spy(), - patch: sinon.spy(), - post: sinon.spy(), - delete: sinon.spy()<% } %> - }, - /* stubbed thing router */ - index = proxyquire('./index.js', { - 'express': { - Router: function() { - return router; - } - }, - './thing.controller': thingCtrl - }); +var thingCtrlStub = { + index: 'thingCtrl.index'<% if(filters.mongoose) { %>, + show: 'thingCtrl.show', + create: 'thingCtrl.create', + update: 'thingCtrl.update', + destroy: 'thingCtrl.destroy'<% } %> +}; + +var routerStub = { + get: sinon.spy()<% if(filters.mongoose) { %>, + put: sinon.spy(), + patch: sinon.spy(), + post: sinon.spy(), + delete: sinon.spy()<% } %> +}; + +// require the index with our stubbed out modules +var thingIndex = proxyquire('./index.js', { + 'express': { + Router: function() { + return routerStub; + } + }, + './thing.controller': thingCtrlStub +}); describe('Thing API Router:', function() { it('should return an express router instance', function() { - index.should.equal(router); + thingIndex.should.equal(routerStub); }); describe('GET /api/things', function() { it('should route to thing.controller.index', function() { - router.get.withArgs('/', 'thingCtrl.index').should.have.been.calledOnce; + routerStub.get.withArgs('/', 'thingCtrl.index').should.have.been.calledOnce; }); });<% if(filters.mongoose) { %> @@ -45,7 +45,7 @@ describe('Thing API Router:', function() { describe('GET /api/things/:id', function() { it('should route to thing.controller.show', function() { - router.get.withArgs('/:id', 'thingCtrl.show').should.have.been.calledOnce; + routerStub.get.withArgs('/:id', 'thingCtrl.show').should.have.been.calledOnce; }); }); @@ -53,7 +53,7 @@ describe('Thing API Router:', function() { describe('POST /api/things', function() { it('should route to thing.controller.create', function() { - router.post.withArgs('/', 'thingCtrl.create').should.have.been.calledOnce; + routerStub.post.withArgs('/', 'thingCtrl.create').should.have.been.calledOnce; }); }); @@ -61,7 +61,7 @@ describe('Thing API Router:', function() { describe('PUT /api/things/:id', function() { it('should route to thing.controller.update', function() { - router.put.withArgs('/:id', 'thingCtrl.update').should.have.been.calledOnce; + routerStub.put.withArgs('/:id', 'thingCtrl.update').should.have.been.calledOnce; }); }); @@ -69,7 +69,7 @@ describe('Thing API Router:', function() { describe('PATCH /api/things/:id', function() { it('should route to thing.controller.update', function() { - router.patch.withArgs('/:id', 'thingCtrl.update').should.have.been.calledOnce; + routerStub.patch.withArgs('/:id', 'thingCtrl.update').should.have.been.calledOnce; }); }); @@ -77,7 +77,7 @@ describe('Thing API Router:', function() { describe('DELETE /api/things/:id', function() { it('should route to thing.controller.destroy', function() { - router.delete.withArgs('/:id', 'thingCtrl.destroy').should.have.been.calledOnce; + routerStub.delete.withArgs('/:id', 'thingCtrl.destroy').should.have.been.calledOnce; }); });<% } %> diff --git a/app/templates/server/api/user(auth)/index.spec.js b/app/templates/server/api/user(auth)/index.spec.js index 30b786fb3..b2510aa00 100644 --- a/app/templates/server/api/user(auth)/index.spec.js +++ b/app/templates/server/api/user(auth)/index.spec.js @@ -2,52 +2,52 @@ var proxyquire = require('proxyquire').noPreserveCache(); - /* user.controller stub */ -var userCtrl = { - index: 'userCtrl.index', - destroy: 'userCtrl.destroy', - me: 'userCtrl.me', - changePassword: 'userCtrl.changePassword', - show: 'userCtrl.show', - create: 'userCtrl.create' - }, - /* auth.service stub */ - authService = { - isAuthenticated: function() { - return 'authService.isAuthenticated'; - }, - hasRole: function(role) { - return 'authService.hasRole.' + role; - } - }, - /* express.Router().router stub */ - router = { - get: sinon.spy(), - put: sinon.spy(), - post: sinon.spy(), - delete: sinon.spy() - }, - /* stubbed user router */ - index = proxyquire('./index', { - 'express': { - Router: function() { - return router; - } - }, - './user.controller': userCtrl, - '../../auth/auth.service': authService - }); +var userCtrlStub = { + index: 'userCtrl.index', + destroy: 'userCtrl.destroy', + me: 'userCtrl.me', + changePassword: 'userCtrl.changePassword', + show: 'userCtrl.show', + create: 'userCtrl.create' +}; + +var authServiceStub = { + isAuthenticated: function() { + return 'authService.isAuthenticated'; + }, + hasRole: function(role) { + return 'authService.hasRole.' + role; + } +}; + +var routerStub = { + get: sinon.spy(), + put: sinon.spy(), + post: sinon.spy(), + delete: sinon.spy() +}; + +// require the index with our stubbed out modules +var userIndex = proxyquire('./index', { + 'express': { + Router: function() { + return routerStub; + } + }, + './user.controller': userCtrlStub, + '../../auth/auth.service': authServiceStub +}); describe('User API Router:', function() { it('should return an express router instance', function() { - index.should.equal(router); + userIndex.should.equal(routerStub); }); describe('GET /api/users', function() { it('should verify admin role and route to user.controller.index', function() { - router.get.withArgs('/', 'authService.hasRole.admin', 'userCtrl.index').should.have.been.calledOnce; + routerStub.get.withArgs('/', 'authService.hasRole.admin', 'userCtrl.index').should.have.been.calledOnce; }); }); @@ -55,7 +55,7 @@ describe('User API Router:', function() { describe('DELETE /api/users/:id', function() { it('should verify admin role and route to user.controller.destroy', function() { - router.delete.withArgs('/:id', 'authService.hasRole.admin', 'userCtrl.destroy').should.have.been.calledOnce; + routerStub.delete.withArgs('/:id', 'authService.hasRole.admin', 'userCtrl.destroy').should.have.been.calledOnce; }); }); @@ -63,7 +63,7 @@ describe('User API Router:', function() { describe('GET /api/users/me', function() { it('should be authenticated and route to user.controller.me', function() { - router.get.withArgs('/me', 'authService.isAuthenticated', 'userCtrl.me').should.have.been.calledOnce; + routerStub.get.withArgs('/me', 'authService.isAuthenticated', 'userCtrl.me').should.have.been.calledOnce; }); }); @@ -71,7 +71,7 @@ describe('User API Router:', function() { describe('PUT /api/users/:id/password', function() { it('should be authenticated and route to user.controller.changePassword', function() { - router.put.withArgs('/:id/password', 'authService.isAuthenticated', 'userCtrl.changePassword').should.have.been.calledOnce; + routerStub.put.withArgs('/:id/password', 'authService.isAuthenticated', 'userCtrl.changePassword').should.have.been.calledOnce; }); }); @@ -79,7 +79,7 @@ describe('User API Router:', function() { describe('GET /api/users/:id', function() { it('should be authenticated and route to user.controller.show', function() { - router.get.withArgs('/:id', 'authService.isAuthenticated', 'userCtrl.show').should.have.been.calledOnce; + routerStub.get.withArgs('/:id', 'authService.isAuthenticated', 'userCtrl.show').should.have.been.calledOnce; }); }); @@ -87,7 +87,7 @@ describe('User API Router:', function() { describe('POST /api/users', function() { it('should route to user.controller.create', function() { - router.post.withArgs('/', 'userCtrl.create').should.have.been.calledOnce; + routerStub.post.withArgs('/', 'userCtrl.create').should.have.been.calledOnce; }); }); diff --git a/endpoint/templates/index.spec.js b/endpoint/templates/index.spec.js index 648a841a0..031e379a4 100644 --- a/endpoint/templates/index.spec.js +++ b/endpoint/templates/index.spec.js @@ -2,42 +2,42 @@ var proxyquire = require('proxyquire').noPreserveCache(); - /* <%= name %>.controller stub */ -var <%= cameledName %>Ctrl = { - index: '<%= name %>Ctrl.index'<% if(filters.mongoose) { %>, - show: '<%= name %>Ctrl.show', - create: '<%= name %>Ctrl.create', - update: '<%= name %>Ctrl.update', - destroy: '<%= name %>Ctrl.destroy'<% } %> - }, - /* express.Router().router stub */ - router = { - get: sinon.spy()<% if(filters.mongoose) { %>, - put: sinon.spy(), - patch: sinon.spy(), - post: sinon.spy(), - delete: sinon.spy()<% } %> - }, - /* stubbed <%= name %> router */ - index = proxyquire('./index.js', { - 'express': { - Router: function() { - return router; - } - }, - './<%= name %>.controller': <%= cameledName %>Ctrl - }); +var <%= cameledName %>CtrlStub = { + index: '<%= name %>Ctrl.index'<% if(filters.mongoose) { %>, + show: '<%= name %>Ctrl.show', + create: '<%= name %>Ctrl.create', + update: '<%= name %>Ctrl.update', + destroy: '<%= name %>Ctrl.destroy'<% } %> +}; + +var routerStub = { + get: sinon.spy()<% if(filters.mongoose) { %>, + put: sinon.spy(), + patch: sinon.spy(), + post: sinon.spy(), + delete: sinon.spy()<% } %> +}; + +// require the index with our stubbed out modules +var <%= name %>Index = proxyquire('./index.js', { + 'express': { + Router: function() { + return routerStub; + } + }, + './<%= name %>.controller': <%= cameledName %>CtrlStub +}); describe('<%= classedName %> API Router:', function() { it('should return an express router instance', function() { - index.should.equal(router); + <%= name %>Index.should.equal(router); }); describe('GET <%= route %>', function() { it('should route to <%= name %>.controller.index', function() { - router.get.withArgs('/', '<%= name %>Ctrl.index').should.have.been.calledOnce; + routerStub.get.withArgs('/', '<%= name %>Ctrl.index').should.have.been.calledOnce; }); });<% if(filters.mongoose) { %> @@ -45,7 +45,7 @@ describe('<%= classedName %> API Router:', function() { describe('GET <%= route %>/:id', function() { it('should route to <%= name %>.controller.show', function() { - router.get.withArgs('/:id', '<%= name %>Ctrl.show').should.have.been.calledOnce; + routerStub.get.withArgs('/:id', '<%= name %>Ctrl.show').should.have.been.calledOnce; }); }); @@ -53,7 +53,7 @@ describe('<%= classedName %> API Router:', function() { describe('POST <%= route %>', function() { it('should route to <%= name %>.controller.create', function() { - router.post.withArgs('/', '<%= name %>Ctrl.create').should.have.been.calledOnce; + routerStub.post.withArgs('/', '<%= name %>Ctrl.create').should.have.been.calledOnce; }); }); @@ -61,7 +61,7 @@ describe('<%= classedName %> API Router:', function() { describe('PUT <%= route %>/:id', function() { it('should route to <%= name %>.controller.update', function() { - router.put.withArgs('/:id', '<%= name %>Ctrl.update').should.have.been.calledOnce; + routerStub.put.withArgs('/:id', '<%= name %>Ctrl.update').should.have.been.calledOnce; }); }); @@ -69,7 +69,7 @@ describe('<%= classedName %> API Router:', function() { describe('PATCH <%= route %>/:id', function() { it('should route to <%= name %>.controller.update', function() { - router.patch.withArgs('/:id', '<%= name %>Ctrl.update').should.have.been.calledOnce; + routerStub.patch.withArgs('/:id', '<%= name %>Ctrl.update').should.have.been.calledOnce; }); }); @@ -77,7 +77,7 @@ describe('<%= classedName %> API Router:', function() { describe('DELETE <%= route %>/:id', function() { it('should route to <%= name %>.controller.destroy', function() { - router.delete.withArgs('/:id', '<%= name %>Ctrl.destroy').should.have.been.calledOnce; + routerStub.delete.withArgs('/:id', '<%= name %>Ctrl.destroy').should.have.been.calledOnce; }); });<% } %> From 56134c0c20a131e5fd14bb3f6cf00cd67d7fc0c3 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sat, 20 Sep 2014 20:59:01 -0600 Subject: [PATCH 022/171] style(server): missing semicolon --- app/templates/server/api/thing/thing.socket(socketio).js | 2 +- endpoint/templates/name.socket(socketio).js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/templates/server/api/thing/thing.socket(socketio).js b/app/templates/server/api/thing/thing.socket(socketio).js index dbf3e2fe7..d38e73e0d 100644 --- a/app/templates/server/api/thing/thing.socket(socketio).js +++ b/app/templates/server/api/thing/thing.socket(socketio).js @@ -13,7 +13,7 @@ exports.register = function(socket) { thing.schema.post('remove', function (doc) { onRemove(socket, doc); }); -} +}; function onSave(socket, doc, cb) { socket.emit('thing:save', doc); diff --git a/endpoint/templates/name.socket(socketio).js b/endpoint/templates/name.socket(socketio).js index 886f585ee..aabdadeb8 100644 --- a/endpoint/templates/name.socket(socketio).js +++ b/endpoint/templates/name.socket(socketio).js @@ -13,7 +13,7 @@ exports.register = function(socket) { <%= classedName %>.schema.post('remove', function (doc) { onRemove(socket, doc); }); -} +}; function onSave(socket, doc, cb) { socket.emit('<%= name %>:save', doc); From cda5261d75679b08400370230ff55662e846f7e5 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sat, 20 Sep 2014 21:33:40 -0600 Subject: [PATCH 023/171] use spread for saveAsync methods fixes #567, closes #568 --- app/templates/server/api/thing/thing.controller.js | 2 +- app/templates/server/api/user(auth)/user.controller.js | 4 ++-- endpoint/templates/name.controller.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/templates/server/api/thing/thing.controller.js b/app/templates/server/api/thing/thing.controller.js index 4d5abca05..23bfd6937 100644 --- a/app/templates/server/api/thing/thing.controller.js +++ b/app/templates/server/api/thing/thing.controller.js @@ -42,7 +42,7 @@ function saveUpdates(updates){ return function(entity){ var updated = _.merge(entity, updates); return updated.saveAsync() - .then(function () { + .spread(function (updated) { return updated; }); }; diff --git a/app/templates/server/api/user(auth)/user.controller.js b/app/templates/server/api/user(auth)/user.controller.js index 28e30199a..ed0b1749c 100644 --- a/app/templates/server/api/user(auth)/user.controller.js +++ b/app/templates/server/api/user(auth)/user.controller.js @@ -46,7 +46,7 @@ exports.create = function (req, res, next) { newUser.provider = 'local'; newUser.role = 'user'; newUser.saveAsync() - .then(function(user) { + .spread(function(user) { var token = jwt.sign({_id: user._id }, config.secrets.session, { expiresInMinutes: 60*5 }); res.json({ token: token }); }) @@ -94,7 +94,7 @@ exports.changePassword = function(req, res, next) { if(user.authenticate(oldPass)) { user.password = newPass; return user.saveAsync() - .then(respondWith(res, 200)) + .spread(respondWith(res, 200)) .catch(validationError(res)); } else { return res.send(403); diff --git a/endpoint/templates/name.controller.js b/endpoint/templates/name.controller.js index 5851a1338..2afb2be8c 100644 --- a/endpoint/templates/name.controller.js +++ b/endpoint/templates/name.controller.js @@ -33,7 +33,7 @@ function saveUpdates(updates){ return function(entity){ var updated = _.merge(entity, updates); return updated.saveAsync() - .then(function () { + .spread(function (updated) { return updated; }); }; From a817ec9ad3ecdbc93ffd21c617eecf4606f87775 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sat, 20 Sep 2014 23:19:41 -0600 Subject: [PATCH 024/171] fix test failures --- endpoint/templates/index.spec.js | 4 ++-- test/test-file-creation.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/endpoint/templates/index.spec.js b/endpoint/templates/index.spec.js index 031e379a4..fd1086b15 100644 --- a/endpoint/templates/index.spec.js +++ b/endpoint/templates/index.spec.js @@ -19,7 +19,7 @@ var routerStub = { }; // require the index with our stubbed out modules -var <%= name %>Index = proxyquire('./index.js', { +var <%= cameledName %>Index = proxyquire('./index.js', { 'express': { Router: function() { return routerStub; @@ -31,7 +31,7 @@ var <%= name %>Index = proxyquire('./index.js', { describe('<%= classedName %> API Router:', function() { it('should return an express router instance', function() { - <%= name %>Index.should.equal(router); + <%= cameledName %>Index.should.equal(routerStub); }); describe('GET <%= route %>', function() { diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 05e83c77f..ced7ce4ee 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -149,7 +149,7 @@ describe('angular-fullstack generator', function () { 'server/api/thing/index.js', 'server/api/thing/index.spec.js', 'server/api/thing/thing.controller.js', - 'server/api/thing/thing.e2e.js', + 'server/api/thing/thing.integration.js', 'server/components/errors/index.js', 'server/config/local.env.js', 'server/config/local.env.sample.js', @@ -215,7 +215,7 @@ describe('angular-fullstack generator', function () { 'server/api/user/index.js', 'server/api/user/index.spec.js', 'server/api/user/user.controller.js', - 'server/api/user/user.e2e.js', + 'server/api/user/user.integration.js', 'server/api/user/user.model.js', 'server/api/user/user.model.spec.js', 'server/auth/index.js', From 0fbea07d92e3e157ea013e5a77a2b4c51b636089 Mon Sep 17 00:00:00 2001 From: kingcody Date: Sun, 21 Sep 2014 05:09:45 -0400 Subject: [PATCH 025/171] test(app): add protractor tests for account pages Changes: - add protractor tests for auth related pages - add `onPrepare` and `params` to `protractor.conf.js` --- .../e2e/account(auth)/login/login.po.js | 27 ++++++++ .../e2e/account(auth)/login/login.spec.js | 62 +++++++++++++++++++ .../e2e/account(auth)/logout/logout.spec.js | 46 ++++++++++++++ .../e2e/account(auth)/signup/signup.po.js | 28 +++++++++ .../e2e/account(auth)/signup/signup.spec.js | 61 ++++++++++++++++++ .../e2e/components/navbar/navbar.po.js | 16 +++++ app/templates/protractor.conf.js | 18 +++++- 7 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 app/templates/e2e/account(auth)/login/login.po.js create mode 100644 app/templates/e2e/account(auth)/login/login.spec.js create mode 100644 app/templates/e2e/account(auth)/logout/logout.spec.js create mode 100644 app/templates/e2e/account(auth)/signup/signup.po.js create mode 100644 app/templates/e2e/account(auth)/signup/signup.spec.js create mode 100644 app/templates/e2e/components/navbar/navbar.po.js diff --git a/app/templates/e2e/account(auth)/login/login.po.js b/app/templates/e2e/account(auth)/login/login.po.js new file mode 100644 index 000000000..1186cdb6b --- /dev/null +++ b/app/templates/e2e/account(auth)/login/login.po.js @@ -0,0 +1,27 @@ +/** + * This file uses the Page Object pattern to define the main page for tests + * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ + */ + +'use strict'; + +var LoginPage = function() { + this.form = element(by.css('.form')); + this.form.email = this.form.element(by.model('user.email')); + this.form.password = this.form.element(by.model('user.password')); + this.form.submit = this.form.element(by.css('.btn-login')); + + this.login = function(data) { + for (var prop in data) { + var formElem = this.form[prop]; + if (data.hasOwnProperty(prop) && formElem && typeof formElem.sendKeys === 'function') { + formElem.sendKeys(data[prop]); + } + } + + this.form.submit.click(); + }; +}; + +module.exports = new LoginPage(); + diff --git a/app/templates/e2e/account(auth)/login/login.spec.js b/app/templates/e2e/account(auth)/login/login.spec.js new file mode 100644 index 000000000..d3f0d48ed --- /dev/null +++ b/app/templates/e2e/account(auth)/login/login.spec.js @@ -0,0 +1,62 @@ +'use strict'; + +var config = protractor.getInstance().params; +var UserModel = require(config.serverConfig.root + '/server/api/user/user.model'); + +describe('Login View', function() { + var page; + + var loadPage = function() { + browser.get('/login'); + page = require('./login.po'); + }; + + var testUser = { + name: 'Test User', + email: 'test@test.com', + password: 'test' + }; + + beforeEach(function(done) { + UserModel.removeAsync() + .then(function() { + return UserModel.createAsync(testUser); + }) + .then(loadPage) + .finally(done); + }); + + it('should include login form with correct inputs and submit button', function() { + expect(page.form.email.getAttribute('type')).toBe('email'); + expect(page.form.email.getAttribute('name')).toBe('email'); + expect(page.form.password.getAttribute('type')).toBe('password'); + expect(page.form.password.getAttribute('name')).toBe('password'); + expect(page.form.submit.getAttribute('type')).toBe('submit'); + expect(page.form.submit.getText()).toBe('Login'); + }); + + describe('with local auth', function() { + + it('should login a user and redirecting to "/"', function() { + page.login(testUser); + + var navbar = require('../../components/navbar/navbar.po'); + + expect(browser.getLocationAbsUrl()).toBe(config.baseUrl + '/'); + expect(navbar.navbarAccountGreeting.getText()).toBe('Hello ' + testUser.name); + }); + + it('should indicate login failures', function() { + page.login({ + email: testUser.email, + password: 'badPassword' + }); + + expect(browser.getLocationAbsUrl()).toBe(config.baseUrl + '/login'); + + var helpBlock = page.form.element(by.css('.form-group.has-error .help-block.ng-binding')); + expect(helpBlock.getText()).toBe('This password is not correct.'); + }); + + }); +}); diff --git a/app/templates/e2e/account(auth)/logout/logout.spec.js b/app/templates/e2e/account(auth)/logout/logout.spec.js new file mode 100644 index 000000000..f496459d8 --- /dev/null +++ b/app/templates/e2e/account(auth)/logout/logout.spec.js @@ -0,0 +1,46 @@ +'use strict'; + +var config = protractor.getInstance().params; +var UserModel = require(config.serverConfig.root + '/server/api/user/user.model'); + +describe('Logout View', function() { + var login = function(user) { + browser.get('/login'); + require('../login/login.po').login(user); + }; + + var testUser = { + name: 'Test User', + email: 'test@test.com', + password: 'test' + }; + + beforeEach(function(done) { + UserModel.removeAsync() + .then(function() { + return UserModel.createAsync(testUser); + }) + .then(function() { + return login(testUser); + }) + .finally(done); + }); + + describe('with local auth', function() { + + it('should logout a user and redirecting to "/"', function() { + var navbar = require('../../components/navbar/navbar.po'); + + expect(browser.getLocationAbsUrl()).toBe(config.baseUrl + '/'); + expect(navbar.navbarAccountGreeting.getText()).toBe('Hello ' + testUser.name); + + browser.get('/logout'); + + navbar = require('../../components/navbar/navbar.po'); + + expect(browser.getLocationAbsUrl()).toBe(config.baseUrl + '/'); + expect(navbar.navbarAccountGreeting.isDisplayed()).toBe(false); + }); + + }); +}); diff --git a/app/templates/e2e/account(auth)/signup/signup.po.js b/app/templates/e2e/account(auth)/signup/signup.po.js new file mode 100644 index 000000000..3a496c6e3 --- /dev/null +++ b/app/templates/e2e/account(auth)/signup/signup.po.js @@ -0,0 +1,28 @@ +/** + * This file uses the Page Object pattern to define the main page for tests + * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ + */ + +'use strict'; + +var SignupPage = function() { + this.form = element(by.css('.form')); + this.form.name = this.form.element(by.model('user.name')); + this.form.email = this.form.element(by.model('user.email')); + this.form.password = this.form.element(by.model('user.password')); + this.form.submit = this.form.element(by.css('.btn-register')); + + this.signup = function(data) { + for (var prop in data) { + var formElem = this.form[prop]; + if (data.hasOwnProperty(prop) && formElem && typeof formElem.sendKeys === 'function') { + formElem.sendKeys(data[prop]); + } + } + + this.form.submit.click(); + }; +}; + +module.exports = new SignupPage(); + diff --git a/app/templates/e2e/account(auth)/signup/signup.spec.js b/app/templates/e2e/account(auth)/signup/signup.spec.js new file mode 100644 index 000000000..cc63a5b7b --- /dev/null +++ b/app/templates/e2e/account(auth)/signup/signup.spec.js @@ -0,0 +1,61 @@ +'use strict'; + +var config = protractor.getInstance().params; +var UserModel = require(config.serverConfig.root + '/server/api/user/user.model'); + +describe('Signup View', function() { + var page; + + var loadPage = function() { + browser.get('/signup'); + page = require('./signup.po'); + }; + + var testUser = { + name: 'Test User', + email: 'test@test.com', + password: 'test' + }; + + beforeEach(function() { + loadPage(); + }); + + it('should include signup form with correct inputs and submit button', function() { + expect(page.form.name.getAttribute('type')).toBe('text'); + expect(page.form.name.getAttribute('name')).toBe('name'); + expect(page.form.email.getAttribute('type')).toBe('email'); + expect(page.form.email.getAttribute('name')).toBe('email'); + expect(page.form.password.getAttribute('type')).toBe('password'); + expect(page.form.password.getAttribute('name')).toBe('password'); + expect(page.form.submit.getAttribute('type')).toBe('submit'); + expect(page.form.submit.getText()).toBe('Sign up'); + }); + + describe('with local auth', function() { + + it('should signup a new user, log them in, and redirecting to "/"', function(done) { + UserModel.remove(function() { + page.signup(testUser); + + var navbar = require('../../components/navbar/navbar.po'); + + expect(browser.getLocationAbsUrl()).toBe(config.baseUrl + '/'); + expect(navbar.navbarAccountGreeting.getText()).toBe('Hello ' + testUser.name); + + done(); + }); + }); + + it('should indicate signup failures', function() { + page.signup(testUser); + + expect(browser.getLocationAbsUrl()).toBe(config.baseUrl + '/signup'); + expect(page.form.email.getAttribute('class')).toContain('ng-invalid-mongoose'); + + var helpBlock = page.form.element(by.css('.form-group.has-error .help-block.ng-binding')); + expect(helpBlock.getText()).toBe('The specified email address is already in use.'); + }); + + }); +}); diff --git a/app/templates/e2e/components/navbar/navbar.po.js b/app/templates/e2e/components/navbar/navbar.po.js new file mode 100644 index 000000000..ee2ec1ada --- /dev/null +++ b/app/templates/e2e/components/navbar/navbar.po.js @@ -0,0 +1,16 @@ +/** + * This file uses the Page Object pattern to define the main page for tests + * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ + */ + +'use strict'; + +var NavbarComponent = function() { + this.navbar = element(by.css('.navbar')); + this.navbarHeader = this.navbar.element(by.css('.navbar-header')); + this.navbarNav = this.navbar.element(by.css('#navbar-main .nav.navbar-nav:not(.navbar-right)'));<% if(filters.auth) { %> + this.navbarAccount = this.navbar.element(by.css('#navbar-main .nav.navbar-nav.navbar-right')); + this.navbarAccountGreeting = this.navbarAccount.element(by.binding('Hello {{ getCurrentUser().name }}'));<% } %> +}; + +module.exports = new NavbarComponent(); diff --git a/app/templates/protractor.conf.js b/app/templates/protractor.conf.js index cb66c67c1..195733525 100644 --- a/app/templates/protractor.conf.js +++ b/app/templates/protractor.conf.js @@ -3,7 +3,7 @@ 'use strict'; -exports.config = { +var config = { // The timeout for each script run on the browser. This should be longer // than the maximum time your application needs to stabilize between tasks. allScriptsTimeout: 110000, @@ -46,5 +46,21 @@ exports.config = { // See the full list at https://github.com/juliemr/minijasminenode jasmineNodeOpts: { defaultTimeoutInterval: 30000 + }, + + // Prepare environment for tests + params: { + serverConfig: require('./server/config/environment') + }, + + onPrepare: function() { + var serverConfig = config.params.serverConfig; + + // Setup mongo tests + var mongoose = require('mongoose-bird')(); + mongoose.connect(serverConfig.mongo.uri, serverConfig.mongo.options); // Connect to database } }; + +config.params.baseUrl = config.baseUrl; +exports.config = config; From 5898e0c98f552bc5a3609a7329f4f200de0cc262 Mon Sep 17 00:00:00 2001 From: kingcody Date: Sun, 21 Sep 2014 05:52:31 -0400 Subject: [PATCH 026/171] fix(app-signup): switch button classes --- .../client/app/account(auth)/signup/signup(html).html | 4 ++-- .../client/app/account(auth)/signup/signup(jade).jade | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/templates/client/app/account(auth)/signup/signup(html).html b/app/templates/client/app/account(auth)/signup/signup(html).html index 28d6c39ab..ee4f17fa4 100644 --- a/app/templates/client/app/account(auth)/signup/signup(html).html +++ b/app/templates/client/app/account(auth)/signup/signup(html).html @@ -55,10 +55,10 @@

Sign up

diff --git a/app/templates/client/app/account(auth)/signup/signup(jade).jade b/app/templates/client/app/account(auth)/signup/signup(jade).jade index 7e21b101c..8c5710686 100644 --- a/app/templates/client/app/account(auth)/signup/signup(jade).jade +++ b/app/templates/client/app/account(auth)/signup/signup(jade).jade @@ -33,10 +33,10 @@ div(ng-include='"components/navbar/navbar.html"') | {{ errors.password }} div - button.btn.btn-inverse.btn-lg.btn-login(type='submit') + button.btn.btn-inverse.btn-lg.btn-register(type='submit') | Sign up = ' ' - a.btn.btn-default.btn-lg.btn-register(<% if(filters.uirouter) { %>ui-sref='login'<% } else { %>href='/login'<% } %>) + a.btn.btn-default.btn-lg.btn-login(<% if(filters.uirouter) { %>ui-sref='login'<% } else { %>href='/login'<% } %>) | Login <% if(filters.oauth) {%> From 650d244b8b36c254848743929fb9ee2498343265 Mon Sep 17 00:00:00 2001 From: kingcody Date: Sun, 21 Sep 2014 05:56:17 -0400 Subject: [PATCH 027/171] fix(app-logout): add blank templates to logout route/state Closes #570 --- app/templates/client/app/account(auth)/account(js).js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/templates/client/app/account(auth)/account(js).js b/app/templates/client/app/account(auth)/account(js).js index d31ff19d2..8c005c952 100644 --- a/app/templates/client/app/account(auth)/account(js).js +++ b/app/templates/client/app/account(auth)/account(js).js @@ -10,6 +10,7 @@ angular.module('<%= scriptAppName %>') .when('/logout', { name: 'logout', referrer: '/', + template: '', controller: function($location, $route, Auth) { var referrer = $route.current.params.referrer || $route.current.referrer || @@ -44,6 +45,7 @@ angular.module('<%= scriptAppName %>') .state('logout', { url: '/logout?referrer', referrer: 'main', + template: '', controller: function($state, Auth) { var referrer = $state.params.referrer || $state.current.referrer || From 50ca41d21b71962e829bc2743cc37819c650dc41 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sat, 20 Sep 2014 23:19:41 -0600 Subject: [PATCH 028/171] feat(generator): use sauce labs for running e2e tests with travis CI closes #572 --- .travis.yml | 6 ++++++ Gruntfile.js | 18 +++++++++++++----- app/templates/protractor.conf.js | 7 ++++--- endpoint/templates/index.spec.js | 4 ++-- test/test-file-creation.js | 24 +++++++++++------------- 5 files changed, 36 insertions(+), 23 deletions(-) diff --git a/.travis.yml b/.travis.yml index 85fbe4a6b..ae5a22e65 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,12 @@ language: node_js node_js: - '0.10' - '0.11' +env: + global: + - secure: fBqMlUrbIwaRJeKe39zxVsRcvtPIP/R98JNzrD0ycHzdfbhsWge0J7FCpdKjgRCf99ggqMhyevJafSJv7cfiVgJhB6kYudhG0G60V+vBjj4NIZnOelpVeQHXyLlekRpu+Qa/DaL43jovRAI0I11cwVfIRWtXZwiQOjCd/Elsdl8= + - secure: GZ95FcH0K88RG7P0SJjoIcHLfUalFRHeu5Vd7Kh0wXTh3O6Zku7iauk6Cd+aYGuFedL5wSzht5qnVBRm10VxhhJMxHiZ+I+VBxt4bysKM0axMZ+SMTfPK3zajVVXKfzIsIYO0m0qwYtHCgSXUrMnwAczESxczxq48VxA/rCXbYc= +addons: + sauce_connect: true before_install: - gem update --system - gem install sass --version "=3.3.7" diff --git a/Gruntfile.js b/Gruntfile.js index 9819a4041..3a510072a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -3,7 +3,7 @@ var markdown = require('marked'); var semver = require('semver'); var _s = require('underscore.string'); var shell = require('shelljs'); -var process = require('child_process'); +var child_process = require('child_process'); var Q = require('q'); var helpers = require('yeoman-generator').test; var fs = require('fs-extra'); @@ -227,12 +227,20 @@ module.exports = function (grunt) { shell.cd('test/fixtures'); grunt.log.ok('installing npm dependencies for generated app'); - process.exec('npm install --quiet', {cwd: '../fixtures'}, function (error, stdout, stderr) { + child_process.exec('npm install --quiet', {cwd: '../fixtures'}, function (error, stdout, stderr) { grunt.log.ok('installing bower dependencies for generated app'); - process.exec('bower install', {cwd: '../fixtures'}, function (error, stdout, stderr) { - shell.cd('../../'); - done(); + child_process.exec('bower install', {cwd: '../fixtures'}, function (error, stdout, stderr) { + + if(!process.env.SAUCE_USERNAME) { + child_process.exec('npm run update-webdriver', function() { + shell.cd('../../'); + done(); + }); + } else { + shell.cd('../../'); + done(); + } }) }); }); diff --git a/app/templates/protractor.conf.js b/app/templates/protractor.conf.js index cb66c67c1..d05f9e4d1 100644 --- a/app/templates/protractor.conf.js +++ b/app/templates/protractor.conf.js @@ -12,9 +12,10 @@ exports.config = { // with relative paths will be prepended with this. baseUrl: 'http://localhost:' + (process.env.PORT || '9000'), - // If true, only chromedriver will be started, not a standalone selenium. - // Tests for browsers other than chrome will not run. - chromeOnly: true, + // Credientials for Saucelabs + sauceUser: process.env.SAUCE_USERNAME, + + sauceKey: process.env.SAUCE_ACCESS_KEY, // list of files / patterns to load in the browser specs: [ diff --git a/endpoint/templates/index.spec.js b/endpoint/templates/index.spec.js index 031e379a4..fd1086b15 100644 --- a/endpoint/templates/index.spec.js +++ b/endpoint/templates/index.spec.js @@ -19,7 +19,7 @@ var routerStub = { }; // require the index with our stubbed out modules -var <%= name %>Index = proxyquire('./index.js', { +var <%= cameledName %>Index = proxyquire('./index.js', { 'express': { Router: function() { return routerStub; @@ -31,7 +31,7 @@ var <%= name %>Index = proxyquire('./index.js', { describe('<%= classedName %> API Router:', function() { it('should return an express router instance', function() { - <%= name %>Index.should.equal(router); + <%= cameledName %>Index.should.equal(routerStub); }); describe('GET <%= route %>', function() { diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 05e83c77f..568315d92 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -149,7 +149,7 @@ describe('angular-fullstack generator', function () { 'server/api/thing/index.js', 'server/api/thing/index.spec.js', 'server/api/thing/thing.controller.js', - 'server/api/thing/thing.e2e.js', + 'server/api/thing/thing.integration.js', 'server/components/errors/index.js', 'server/config/local.env.js', 'server/config/local.env.sample.js', @@ -215,7 +215,7 @@ describe('angular-fullstack generator', function () { 'server/api/user/index.js', 'server/api/user/index.spec.js', 'server/api/user/user.controller.js', - 'server/api/user/user.e2e.js', + 'server/api/user/user.integration.js', 'server/api/user/user.model.js', 'server/api/user/user.model.spec.js', 'server/auth/index.js', @@ -347,17 +347,15 @@ describe('angular-fullstack generator', function () { }); }); -// it('should run e2e tests successfully', function(done) { -// this.timeout(80000); -// gen.run({}, function () { -// exec('npm run update-webdriver', function (error, stdout, stderr) { -// exec('grunt test:e2e', function (error, stdout, stderr) { -// expect(stdout, 'Client tests failed \n' + stdout ).to.contain('Done, without errors.'); -// done(); -// }); -// }); -// }) -// }); + it('should run e2e tests successfully', function(done) { + this.timeout(80000); + gen.run({}, function () { + exec('grunt test:e2e', function (error, stdout, stderr) { + expect(stdout, 'Client tests failed \n' + stdout).to.contain('0 failures'); + done(); + }); + }); + }); }); describe('with other preprocessors and oauth', function() { From 7bd31938ca5116e62fafb30812e80d78b5d4c2ca Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 21 Sep 2014 05:20:24 -0600 Subject: [PATCH 029/171] add log info for running npm run update-webdriver --- Gruntfile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Gruntfile.js b/Gruntfile.js index 3a510072a..8edbe3ccd 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -233,6 +233,7 @@ module.exports = function (grunt) { child_process.exec('bower install', {cwd: '../fixtures'}, function (error, stdout, stderr) { if(!process.env.SAUCE_USERNAME) { + grunt.log.ok('running npm run update-webdriver'); child_process.exec('npm run update-webdriver', function() { shell.cd('../../'); done(); From 24bfde4de8e747ecfcd8b8b7737044fefffb7623 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 21 Sep 2014 13:41:26 -0600 Subject: [PATCH 030/171] add capabilities to protractor config --- app/templates/protractor.conf.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/templates/protractor.conf.js b/app/templates/protractor.conf.js index 2bdcde7fe..18b9ba6af 100644 --- a/app/templates/protractor.conf.js +++ b/app/templates/protractor.conf.js @@ -32,7 +32,10 @@ var config = { // and // https://code.google.com/p/selenium/source/browse/javascript/webdriver/capabilities.js capabilities: { - 'browserName': 'chrome' + 'browserName': 'chrome', + 'name': 'Fullstack E2E', + 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER, + 'build': process.env.TRAVIS_BUILD_NUMBER }, // ----- The test framework ----- From 456631e056fede3d2e0afad6d88b793cdffc45ca Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 21 Sep 2014 14:06:21 -0600 Subject: [PATCH 031/171] use opensauce saucelabs account --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ae5a22e65..d408bd4db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,8 @@ node_js: - '0.11' env: global: - - secure: fBqMlUrbIwaRJeKe39zxVsRcvtPIP/R98JNzrD0ycHzdfbhsWge0J7FCpdKjgRCf99ggqMhyevJafSJv7cfiVgJhB6kYudhG0G60V+vBjj4NIZnOelpVeQHXyLlekRpu+Qa/DaL43jovRAI0I11cwVfIRWtXZwiQOjCd/Elsdl8= - - secure: GZ95FcH0K88RG7P0SJjoIcHLfUalFRHeu5Vd7Kh0wXTh3O6Zku7iauk6Cd+aYGuFedL5wSzht5qnVBRm10VxhhJMxHiZ+I+VBxt4bysKM0axMZ+SMTfPK3zajVVXKfzIsIYO0m0qwYtHCgSXUrMnwAczESxczxq48VxA/rCXbYc= + - secure: hwJX8w4D15UPdU4UIS+iB4XC/lxmh/y6m3IN5Ia5ha++rHs2JZ3FVOPdosb03fMoXYpR2682pzvTEHrZw6Ks0dpmJs3KJWB4kgsK7slfKyKA38j8rDvSU+iBfiB2lo8lTmj9g5Ua8c2hiJcw6EMt8VlLlhgoJLy3heycPTsW04A= + - secure: gl/h7p5S+hfR7Cv2tl53c+biW3Mfhh6P4VI4VxrPFj5Mm1ha89dbMYjPn0PuLkmjaZ2+n9siU7j6lwlOdyWGAo0tkPtK/EC62L2Q938O204jD8VXnvjQ8kbP9zcjHogicD+elF0X5hvnXApW9I7nOZIs59V5Pp8DXkuHEWMIo8U= addons: sauce_connect: true before_install: From fca79d9a93c736328d19686dd1d77c2ed423d778 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 21 Sep 2014 18:30:14 -0600 Subject: [PATCH 032/171] caching folders to reduce build time --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index d408bd4db..63d45919a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,11 @@ env: - secure: gl/h7p5S+hfR7Cv2tl53c+biW3Mfhh6P4VI4VxrPFj5Mm1ha89dbMYjPn0PuLkmjaZ2+n9siU7j6lwlOdyWGAo0tkPtK/EC62L2Q938O204jD8VXnvjQ8kbP9zcjHogicD+elF0X5hvnXApW9I7nOZIs59V5Pp8DXkuHEWMIo8U= addons: sauce_connect: true +cache: + directories: + - node_modules + - test/fixtures/node_modules + - test/fixtures/bower_components before_install: - gem update --system - gem install sass --version "=3.3.7" From f9e7f6d7af72201675a1f528062313df95b098d4 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 21 Sep 2014 18:38:38 -0600 Subject: [PATCH 033/171] revert caching --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 63d45919a..d408bd4db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,11 +8,6 @@ env: - secure: gl/h7p5S+hfR7Cv2tl53c+biW3Mfhh6P4VI4VxrPFj5Mm1ha89dbMYjPn0PuLkmjaZ2+n9siU7j6lwlOdyWGAo0tkPtK/EC62L2Q938O204jD8VXnvjQ8kbP9zcjHogicD+elF0X5hvnXApW9I7nOZIs59V5Pp8DXkuHEWMIo8U= addons: sauce_connect: true -cache: - directories: - - node_modules - - test/fixtures/node_modules - - test/fixtures/bower_components before_install: - gem update --system - gem install sass --version "=3.3.7" From 25d91873d1e9adedd39a2d165ca807bfbbf15d5e Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 21 Sep 2014 19:32:57 -0600 Subject: [PATCH 034/171] fix test timeouts and sign up test failure --- app/templates/e2e/account(auth)/signup/signup.spec.js | 2 +- test/test-file-creation.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/templates/e2e/account(auth)/signup/signup.spec.js b/app/templates/e2e/account(auth)/signup/signup.spec.js index cc63a5b7b..856786e4a 100644 --- a/app/templates/e2e/account(auth)/signup/signup.spec.js +++ b/app/templates/e2e/account(auth)/signup/signup.spec.js @@ -12,7 +12,7 @@ describe('Signup View', function() { }; var testUser = { - name: 'Test User', + name: 'Test', email: 'test@test.com', password: 'test' }; diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 568315d92..35ca54538 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -348,7 +348,7 @@ describe('angular-fullstack generator', function () { }); it('should run e2e tests successfully', function(done) { - this.timeout(80000); + this.timeout(240000); // 4 minutes gen.run({}, function () { exec('grunt test:e2e', function (error, stdout, stderr) { expect(stdout, 'Client tests failed \n' + stdout).to.contain('0 failures'); From 2f3ee4f5cb790144d8b2d24c6e4f56d814fa1375 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 21 Sep 2014 19:50:56 -0600 Subject: [PATCH 035/171] run e2e tests for other builds --- .../app/account(auth)/login/login(jade).jade | 2 +- test/test-file-creation.js | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/app/templates/client/app/account(auth)/login/login(jade).jade b/app/templates/client/app/account(auth)/login/login(jade).jade index 686b1769e..5f608a67b 100644 --- a/app/templates/client/app/account(auth)/login/login(jade).jade +++ b/app/templates/client/app/account(auth)/login/login(jade).jade @@ -20,7 +20,7 @@ div(ng-include='"components/navbar/navbar.html"') form.form(name='form', ng-submit='login(form)', novalidate='') .form-group label Email - input.form-control(type='text', name='email', ng-model='user.email') + input.form-control(type='email', name='email', ng-model='user.email') .form-group label Password input.form-control(type='password', name='password', ng-model='user.password') diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 35ca54538..5eab66047 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -408,6 +408,16 @@ describe('angular-fullstack generator', function () { assertOnlyFiles(genFiles(testOptions), done); }); }); + + it('should run e2e tests successfully', function(done) { + this.timeout(240000); // 4 minutes + gen.run({}, function () { + exec('grunt test:e2e', function (error, stdout, stderr) { + expect(stdout, 'Client tests failed \n' + stdout).to.contain('0 failures'); + done(); + }); + }); + }); }); describe('with other preprocessors and no server options', function() { @@ -461,6 +471,16 @@ describe('angular-fullstack generator', function () { assertOnlyFiles(genFiles(testOptions), done); }); }); + + it('should run e2e tests successfully', function(done) { + this.timeout(240000); // 4 minutes + gen.run({}, function () { + exec('grunt test:e2e', function (error, stdout, stderr) { + expect(stdout, 'Client tests failed \n' + stdout).to.contain('0 failures'); + done(); + }); + }); + }); }); describe('with no preprocessors and no server options', function() { @@ -506,6 +526,16 @@ describe('angular-fullstack generator', function () { assertOnlyFiles(genFiles(testOptions), done); }); }); + + it('should run e2e tests successfully', function(done) { + this.timeout(240000); // 4 minutes + gen.run({}, function () { + exec('grunt test:e2e', function (error, stdout, stderr) { + expect(stdout, 'Client tests failed \n' + stdout).to.contain('0 failures'); + done(); + }); + }); + }); }); }); }); From 4bf07505509b64c65906220ec12da3288402790f Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 21 Sep 2014 20:14:23 -0600 Subject: [PATCH 036/171] chore(docs): remove outdated readme from test folder --- test/readme.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 test/readme.md diff --git a/test/readme.md b/test/readme.md deleted file mode 100644 index 6c709f50b..000000000 --- a/test/readme.md +++ /dev/null @@ -1 +0,0 @@ -Run bower install and npm install in the fixtures folder before running tests \ No newline at end of file From c131bc4d451e02404785130df0f5b36ad8802da8 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 21 Sep 2014 22:15:24 -0600 Subject: [PATCH 037/171] make tests more dry --- test/test-file-creation.js | 42 +++++++++++++------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 5eab66047..aecb08dcb 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -61,6 +61,16 @@ describe('angular-fullstack generator', function () { }); } + function runE2E() { + this.timeout(240000); // 4 minutes + gen.run({}, function () { + exec('grunt test:e2e', function (error, stdout, stderr) { + expect(stdout, 'Client tests failed \n' + stdout).to.contain('0 failures'); + done(); + }); + }); + }; + 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, @@ -348,13 +358,7 @@ describe('angular-fullstack generator', function () { }); it('should run e2e tests successfully', function(done) { - this.timeout(240000); // 4 minutes - gen.run({}, function () { - exec('grunt test:e2e', function (error, stdout, stderr) { - expect(stdout, 'Client tests failed \n' + stdout).to.contain('0 failures'); - done(); - }); - }); + runE2E(); }); }); @@ -410,13 +414,7 @@ describe('angular-fullstack generator', function () { }); it('should run e2e tests successfully', function(done) { - this.timeout(240000); // 4 minutes - gen.run({}, function () { - exec('grunt test:e2e', function (error, stdout, stderr) { - expect(stdout, 'Client tests failed \n' + stdout).to.contain('0 failures'); - done(); - }); - }); + runE2E(); }); }); @@ -473,13 +471,7 @@ describe('angular-fullstack generator', function () { }); it('should run e2e tests successfully', function(done) { - this.timeout(240000); // 4 minutes - gen.run({}, function () { - exec('grunt test:e2e', function (error, stdout, stderr) { - expect(stdout, 'Client tests failed \n' + stdout).to.contain('0 failures'); - done(); - }); - }); + runE2E(); }); }); @@ -528,13 +520,7 @@ describe('angular-fullstack generator', function () { }); it('should run e2e tests successfully', function(done) { - this.timeout(240000); // 4 minutes - gen.run({}, function () { - exec('grunt test:e2e', function (error, stdout, stderr) { - expect(stdout, 'Client tests failed \n' + stdout).to.contain('0 failures'); - done(); - }); - }); + runE2E(); }); }); }); From 828857bbd1a2c4147ecd943fdc9a6625888e89ec Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Mon, 22 Sep 2014 01:51:23 -0600 Subject: [PATCH 038/171] fix test failure --- test/test-file-creation.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/test-file-creation.js b/test/test-file-creation.js index aecb08dcb..31ed71ab2 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -61,8 +61,8 @@ describe('angular-fullstack generator', function () { }); } - function runE2E() { - this.timeout(240000); // 4 minutes + function runE2E(self, done) { + self.timeout(240000); // 4 minutes gen.run({}, function () { exec('grunt test:e2e', function (error, stdout, stderr) { expect(stdout, 'Client tests failed \n' + stdout).to.contain('0 failures'); @@ -87,6 +87,9 @@ describe('angular-fullstack generator', function () { case 'grunt jshint': expect(stdout).to.contain('Done, without errors.'); break; + case 'grunt jscs': + expect(stdout).to.contain('Done, without errors.'); + break; case 'grunt test:server': expect(stdout, 'Server tests failed (do you have mongoDB running?) \n' + stdout).to.contain('Done, without errors.'); break; @@ -358,7 +361,7 @@ describe('angular-fullstack generator', function () { }); it('should run e2e tests successfully', function(done) { - runE2E(); + runE2E(this, done); }); }); @@ -414,7 +417,7 @@ describe('angular-fullstack generator', function () { }); it('should run e2e tests successfully', function(done) { - runE2E(); + runE2E(this, done); }); }); @@ -471,7 +474,7 @@ describe('angular-fullstack generator', function () { }); it('should run e2e tests successfully', function(done) { - runE2E(); + runE2E(this, done); }); }); @@ -520,7 +523,7 @@ describe('angular-fullstack generator', function () { }); it('should run e2e tests successfully', function(done) { - runE2E(); + runE2E(this, done); }); }); }); From 4667013bb9f621e514a3f178985389ddc1d8af10 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Thu, 25 Sep 2014 19:40:11 -0600 Subject: [PATCH 039/171] update sauce labs config so e2e tests can run against pull requests --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d408bd4db..6b84b4557 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,8 @@ node_js: - '0.11' env: global: - - secure: hwJX8w4D15UPdU4UIS+iB4XC/lxmh/y6m3IN5Ia5ha++rHs2JZ3FVOPdosb03fMoXYpR2682pzvTEHrZw6Ks0dpmJs3KJWB4kgsK7slfKyKA38j8rDvSU+iBfiB2lo8lTmj9g5Ua8c2hiJcw6EMt8VlLlhgoJLy3heycPTsW04A= - - secure: gl/h7p5S+hfR7Cv2tl53c+biW3Mfhh6P4VI4VxrPFj5Mm1ha89dbMYjPn0PuLkmjaZ2+n9siU7j6lwlOdyWGAo0tkPtK/EC62L2Q938O204jD8VXnvjQ8kbP9zcjHogicD+elF0X5hvnXApW9I7nOZIs59V5Pp8DXkuHEWMIo8U= + - SAUCE_USERNAME=fullstack-ci + - SAUCE_ACCESS_KEY=1a527ca6-4aa5-4618-86ce-0278bf158cbf addons: sauce_connect: true before_install: From d2bd1f5d820e0fd03f81ab7aa8d3880530721195 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Thu, 25 Sep 2014 23:12:25 -0600 Subject: [PATCH 040/171] fix sauce username --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6b84b4557..676afbb43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ node_js: - '0.11' env: global: - - SAUCE_USERNAME=fullstack-ci + - SAUCE_USERNAME=fullstack_ci - SAUCE_ACCESS_KEY=1a527ca6-4aa5-4618-86ce-0278bf158cbf addons: sauce_connect: true From 654de87ff7d695b9ff8d2b91b43f0b860d6c7e56 Mon Sep 17 00:00:00 2001 From: kingcody Date: Mon, 22 Sep 2014 06:37:09 -0400 Subject: [PATCH 041/171] feat(gen): unify testing framework Changes: - add prompt for Jasmine or Mocha - if Mocha choosen, prompt for Expect or Should - use `<%= does() %>` to dynamically insert assertions (expect|should) - add mocha variants for protractor tests - add mocha options to protractor.conf - remove `test/fixtures/(bower|package).json` from repo - move runE2E functionality to runTest and simplify switch - comment generator test functions - use node `0.11.13` in travis due to issues with `0.11.14`+ Note: Server-side jasmine test are needed to fully unify testing frameworks. Once Jasmine tests are included for server, mocha dep can be removed fully when selecting Jasmine. --- .travis.yml | 2 +- app/index.js | 133 +++++++++++++----- app/templates/_package.json | 18 ++- .../main/main.controller.spec(coffee).coffee | 5 +- .../app/main/main.controller.spec(js).js | 5 +- .../{login.spec.js => login.spec(jasmine).js} | 0 .../account(auth)/login/login.spec(mocha).js | 73 ++++++++++ ...logout.spec.js => logout.spec(jasmine).js} | 0 .../logout/logout.spec(mocha).js | 50 +++++++ ...signup.spec.js => signup.spec(jasmine).js} | 0 .../signup/signup.spec(mocha).js | 72 ++++++++++ .../{main.spec.js => main.spec(jasmine).js} | 0 app/templates/e2e/main/main.spec(mocha).js | 16 +++ app/templates/karma.conf.js | 13 +- app/templates/mocha.conf.js | 16 ++- app/templates/protractor.conf.js | 26 +++- script-base.js | 11 +- test/fixtures/bower.json | 25 ---- test/fixtures/package.json | 104 -------------- test/test-file-creation.js | 85 ++++++++--- 20 files changed, 438 insertions(+), 216 deletions(-) rename app/templates/e2e/account(auth)/login/{login.spec.js => login.spec(jasmine).js} (100%) create mode 100644 app/templates/e2e/account(auth)/login/login.spec(mocha).js rename app/templates/e2e/account(auth)/logout/{logout.spec.js => logout.spec(jasmine).js} (100%) create mode 100644 app/templates/e2e/account(auth)/logout/logout.spec(mocha).js rename app/templates/e2e/account(auth)/signup/{signup.spec.js => signup.spec(jasmine).js} (100%) create mode 100644 app/templates/e2e/account(auth)/signup/signup.spec(mocha).js rename app/templates/e2e/main/{main.spec.js => main.spec(jasmine).js} (100%) create mode 100644 app/templates/e2e/main/main.spec(mocha).js delete mode 100644 test/fixtures/bower.json delete mode 100644 test/fixtures/package.json diff --git a/.travis.yml b/.travis.yml index 676afbb43..e1abfbd26 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: node_js node_js: - '0.10' - - '0.11' + - '0.11.13' env: global: - SAUCE_USERNAME=fullstack_ci diff --git a/app/index.js b/app/index.js index 6e4724801..7dcee4d55 100644 --- a/app/index.js +++ b/app/index.js @@ -24,6 +24,15 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ this.pkg = require('../package.json'); this.filters = {}; + + // dynamic assertion statement + this.does = this.is = function(foo) { + if (this.filters.should) { + return foo + '.should'; + } else { + return 'expect(' + foo + ').to'; + } + }.bind(this); }, info: function () { @@ -36,9 +45,9 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ if(this.config.get('filters')) { this.prompt([{ - type: "confirm", - name: "skipConfig", - message: "Existing .yo-rc configuration found, would you like to use it?", + type: 'confirm', + name: 'skipConfig', + message: 'Existing .yo-rc configuration found, would you like to use it?', default: true, }], function (answers) { this.skipConfig = answers.skipConfig; @@ -66,10 +75,10 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ this.log('# Client\n'); this.prompt([{ - type: "list", - name: "script", - message: "What would you like to write scripts with?", - choices: [ "JavaScript", "CoffeeScript"], + type: 'list', + name: 'script', + message: 'What would you like to write scripts with?', + choices: [ 'JavaScript', 'CoffeeScript'], filter: function( val ) { var filterMap = { 'JavaScript': 'js', @@ -79,33 +88,33 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ return filterMap[val]; } }, { - type: "list", - name: "markup", - message: "What would you like to write markup with?", - choices: [ "HTML", "Jade"], + type: 'list', + name: 'markup', + message: 'What would you like to write markup with?', + choices: [ 'HTML', 'Jade'], filter: function( val ) { return val.toLowerCase(); } }, { - type: "list", - name: "stylesheet", + type: 'list', + name: 'stylesheet', default: 1, - message: "What would you like to write stylesheets with?", - choices: [ "CSS", "Sass", "Stylus", "Less"], + message: 'What would you like to write stylesheets with?', + choices: [ 'CSS', 'Sass', 'Stylus', 'Less'], filter: function( val ) { return val.toLowerCase(); } }, { - type: "list", - name: "router", + type: 'list', + name: 'router', default: 1, - message: "What Angular router would you like to use?", - choices: [ "ngRoute", "uiRouter"], + message: 'What Angular router would you like to use?', + choices: [ 'ngRoute', 'uiRouter'], filter: function( val ) { return val.toLowerCase(); } }, { - type: "confirm", - name: "bootstrap", - message: "Would you like to include Bootstrap?" + type: 'confirm', + name: 'bootstrap', + message: 'Would you like to include Bootstrap?' }, { - type: "confirm", - name: "uibootstrap", - message: "Would you like to include UI Bootstrap?", + type: 'confirm', + name: 'uibootstrap', + message: 'Would you like to include UI Bootstrap?', when: function (answers) { return answers.bootstrap; } @@ -116,7 +125,7 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ this.filters[answers.router] = true; this.filters.bootstrap = !!answers.bootstrap; this.filters.uibootstrap = !!answers.uibootstrap; - cb(); + cb(); }.bind(this)); }, @@ -128,13 +137,13 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ this.log('\n# Server\n'); this.prompt([{ - type: "confirm", - name: "mongoose", - message: "Would you like to use mongoDB with Mongoose for data modeling?" + type: 'confirm', + name: 'mongoose', + message: 'Would you like to use mongoDB with Mongoose for data modeling?' }, { - type: "confirm", - name: "auth", - message: "Would you scaffold out an authentication boilerplate?", + type: 'confirm', + name: 'auth', + message: 'Would you scaffold out an authentication boilerplate?', when: function (answers) { return answers.mongoose; } @@ -163,9 +172,9 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ } ] }, { - type: "confirm", - name: "socketio", - message: "Would you like to use socket.io?", + type: 'confirm', + name: 'socketio', + message: 'Would you like to use socket.io?', // to-do: should not be dependent on mongoose when: function (answers) { return answers.mongoose; @@ -186,6 +195,47 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ }.bind(this)); }, + projectPrompts: function() { + if(this.skipConfig) return; + var cb = this.async(); + var self = this; + + this.log('\n# Project\n'); + + this.prompt([{ + type: 'list', + name: 'testing', + message: 'What would you like to write tests with?', + choices: [ 'Jasmine', 'Mocha + Chai + Sinon'], + filter: function( val ) { + var filterMap = { + 'Jasmine': 'jasmine', + 'Mocha + Chai + Sinon': 'mocha' + }; + + return filterMap[val]; + } + }, { + type: 'list', + name: 'chai', + message: 'What would you like to write Chai assertions with?', + choices: ['Expect', 'Should'], + filter: function( val ) { + return val.toLowerCase(); + }, + when: function( answers ) { + return answers.testing === 'mocha'; + } + }], function (answers) { + this.filters[answers.testing] = true; + if (this.filters.mocha) { + this.filters[answers.chai] = true; + } + + cb(); + }.bind(this)); + }, + saveSettings: function() { if(this.skipConfig) return; this.config.set('insertRoutes', true); @@ -207,10 +257,15 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ if(this.skipConfig) return; var appPath = 'client/app/'; var extensions = []; - var filters = []; + var filters = [ + 'ngroute', + 'uirouter', + 'jasmine', + 'mocha', + 'expect', + 'should' + ].filter(function(v) {return this.filters[v];}, this); - if(this.filters.ngroute) filters.push('ngroute'); - if(this.filters.uirouter) filters.push('uirouter'); if(this.filters.coffee) extensions.push('coffee'); if(this.filters.js) extensions.push('js'); if(this.filters.html) extensions.push('html'); @@ -249,7 +304,7 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ if(this.filters.uirouter) angModules.push("'ui.router'"); if(this.filters.uibootstrap) angModules.push("'ui.bootstrap'"); - this.angularModules = "\n " + angModules.join(",\n ") +"\n"; + this.angularModules = '\n ' + angModules.join(',\n ') +'\n'; }, generate: function() { diff --git a/app/templates/_package.json b/app/templates/_package.json index 1912903fa..08784aa6c 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -31,7 +31,6 @@ "socketio-jwt": "^2.0.2"<% } %> }, "devDependencies": { - "chai-as-promised": "^4.1.1", "grunt": "~0.4.4", "grunt-autoprefixer": "~0.7.2", "grunt-wiredep": "~1.8.0", @@ -62,9 +61,7 @@ "grunt-protractor-runner": "^1.1.0", "grunt-asset-injector": "^0.1.0", "grunt-karma": "~0.8.2", - "grunt-build-control": "DaftMonk/grunt-build-control", - "grunt-mocha-test": "~0.10.2", - "grunt-mocha-istanbul": "^2.0.0",<% if(filters.sass) { %> + "grunt-build-control": "DaftMonk/grunt-build-control",<% if(filters.sass) { %> "grunt-contrib-sass": "^0.7.3",<% } %><% if(filters.stylus) { %> "grunt-contrib-stylus": "latest",<% } %> "jit-grunt": "^0.5.0", @@ -74,12 +71,19 @@ "open": "~0.0.4", "jshint-stylish": "~0.1.5", "connect-livereload": "~0.4.0", + "grunt-mocha-test": "~0.10.2", + "grunt-mocha-istanbul": "^2.0.0", + "chai-as-promised": "^4.1.1", + "chai-things": "^0.2.0", + "sinon-chai": "^2.5.0",<% if(filters.mocha) { %> + "karma-mocha": "^0.1.9", + "karma-chai-plugins": "^0.2.3",<% } if(filters.jasmine) { %> + "karma-jasmine": "~0.1.5",<% } %> "karma-ng-scenario": "~0.1.0", "karma-firefox-launcher": "~0.1.3", "karma-script-launcher": "~0.1.0", "karma-html2js-preprocessor": "~0.1.0", "karma-ng-jade2js-preprocessor": "^0.1.2", - "karma-jasmine": "~0.1.5", "karma-chrome-launcher": "~0.1.3", "requirejs": "~2.1.11", "karma-requirejs": "~0.2.1", @@ -88,9 +92,9 @@ "karma-phantomjs-launcher": "~0.1.4", "karma": "~0.12.9", "karma-ng-html2js-preprocessor": "~0.1.0", + "karma-spec-reporter": "0.0.13", "proxyquire": "^1.0.1", - "supertest": "~0.11.0", - "sinon-chai": "^2.5.0" + "supertest": "~0.11.0" }, "engines": { "node": ">=0.10.0" diff --git a/app/templates/client/app/main/main.controller.spec(coffee).coffee b/app/templates/client/app/main/main.controller.spec(coffee).coffee index f974a081d..5bdf4f840 100644 --- a/app/templates/client/app/main/main.controller.spec(coffee).coffee +++ b/app/templates/client/app/main/main.controller.spec(coffee).coffee @@ -27,5 +27,6 @@ describe 'Controller: MainCtrl', -> $scope: scope it 'should attach a list of things to the scope', -> - $httpBackend.flush() - expect(scope.awesomeThings.length).toBe 4 + $httpBackend.flush()<% if (filters.jasmine) { %> + expect(scope.awesomeThings.length).toBe 4 <% } if (filters.mocha) { %> + <%= does("scope.awesomeThings.length") %>.equal 4<% } %> diff --git a/app/templates/client/app/main/main.controller.spec(js).js b/app/templates/client/app/main/main.controller.spec(js).js index 21b8aba70..dc048b4af 100644 --- a/app/templates/client/app/main/main.controller.spec(js).js +++ b/app/templates/client/app/main/main.controller.spec(js).js @@ -26,7 +26,8 @@ describe('Controller: MainCtrl', function () { })); it('should attach a list of things to the scope', function () { - $httpBackend.flush(); - expect(scope.awesomeThings.length).toBe(4); + $httpBackend.flush();<% if (filters.jasmine) { %> + expect(scope.awesomeThings.length).toBe(4);<% } if (filters.mocha) { %> + <%= does("scope.awesomeThings.length") %>.equal(4);<% } %> }); }); diff --git a/app/templates/e2e/account(auth)/login/login.spec.js b/app/templates/e2e/account(auth)/login/login.spec(jasmine).js similarity index 100% rename from app/templates/e2e/account(auth)/login/login.spec.js rename to app/templates/e2e/account(auth)/login/login.spec(jasmine).js diff --git a/app/templates/e2e/account(auth)/login/login.spec(mocha).js b/app/templates/e2e/account(auth)/login/login.spec(mocha).js new file mode 100644 index 000000000..a2c986f32 --- /dev/null +++ b/app/templates/e2e/account(auth)/login/login.spec(mocha).js @@ -0,0 +1,73 @@ +'use strict'; + +var config = protractor.getInstance().params; +var UserModel = require(config.serverConfig.root + '/server/api/user/user.model'); + +describe('Login View', function() { + var page; + + var loadPage = function() { + browser.get('/login'); + page = require('./login.po'); + }; + + var testUser = { + name: 'Test User', + email: 'test@test.com', + password: 'test' + }; + + before(function() { + return UserModel + .removeAsync() + .then(function() { + return UserModel.createAsync(testUser); + }) + .then(loadPage); + }); + + after(function() { + return UserModel.removeAsync(); + }); + + it('should include login form with correct inputs and submit button', function() { + <%= does("page.form.email.getAttribute('type')") %>.eventually.equal('email'); + <%= does("page.form.email.getAttribute('name')") %>.eventually.equal('email'); + <%= does("page.form.password.getAttribute('type')") %>.eventually.equal('password'); + <%= does("page.form.password.getAttribute('name')") %>.eventually.equal('password'); + <%= does("page.form.submit.getAttribute('type')") %>.eventually.equal('submit'); + <%= does("page.form.submit.getText()") %>.eventually.equal('Login'); + }); + + describe('with local auth', function() { + + it('should login a user and redirecting to "/"', function() { + page.login(testUser); + + var navbar = require('../../components/navbar/navbar.po'); + + <%= does("browser.getLocationAbsUrl()") %>.eventually.equal(config.baseUrl + '/'); + <%= does("navbar.navbarAccountGreeting.getText()") %>.eventually.equal('Hello ' + testUser.name); + }); + + describe('and invalid credentials', function() { + before(function() { + return loadPage(); + }) + + it('should indicate login failures', function() { + page.login({ + email: testUser.email, + password: 'badPassword' + }); + + <%= does("browser.getLocationAbsUrl()") %>.eventually.equal(config.baseUrl + '/login'); + + var helpBlock = page.form.element(by.css('.form-group.has-error .help-block.ng-binding')); + <%= does("helpBlock.getText()") %>.eventually.equal('This password is not correct.'); + }); + + }); + + }); +}); diff --git a/app/templates/e2e/account(auth)/logout/logout.spec.js b/app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js similarity index 100% rename from app/templates/e2e/account(auth)/logout/logout.spec.js rename to app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js diff --git a/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js b/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js new file mode 100644 index 000000000..3adba51f8 --- /dev/null +++ b/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js @@ -0,0 +1,50 @@ +'use strict'; + +var config = protractor.getInstance().params; +var UserModel = require(config.serverConfig.root + '/server/api/user/user.model'); + +describe('Logout View', function() { + var login = function(user) { + browser.get('/login'); + require('../login/login.po').login(user); + }; + + var testUser = { + name: 'Test User', + email: 'test@test.com', + password: 'test' + }; + + beforeEach(function() { + return UserModel + .removeAsync() + .then(function() { + return UserModel.createAsync(testUser); + }) + .then(function() { + return login(testUser); + }); + }); + + after(function() { + return UserModel.removeAsync(); + }) + + describe('with local auth', function() { + + it('should logout a user and redirecting to "/"', function() { + var navbar = require('../../components/navbar/navbar.po'); + + <%= does("browser.getLocationAbsUrl()") %>.eventually.equal(config.baseUrl + '/'); + <%= does("navbar.navbarAccountGreeting.getText()") %>.eventually.equal('Hello ' + testUser.name); + + browser.get('/logout'); + + navbar = require('../../components/navbar/navbar.po'); + + <%= does("browser.getLocationAbsUrl()") %>.eventually.equal(config.baseUrl + '/'); + <%= does("navbar.navbarAccountGreeting.isDisplayed()") %>.eventually.equal(false); + }); + + }); +}); diff --git a/app/templates/e2e/account(auth)/signup/signup.spec.js b/app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js similarity index 100% rename from app/templates/e2e/account(auth)/signup/signup.spec.js rename to app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js diff --git a/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js b/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js new file mode 100644 index 000000000..6a6b0a775 --- /dev/null +++ b/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js @@ -0,0 +1,72 @@ +'use strict'; + +var config = protractor.getInstance().params; +var UserModel = require(config.serverConfig.root + '/server/api/user/user.model'); + +describe('Signup View', function() { + var page; + + var loadPage = function() { + browser.get('/signup'); + page = require('./signup.po'); + }; + + var testUser = { + name: 'Test', + email: 'test@test.com', + password: 'test' + }; + + before(function() { + return loadPage(); + }); + + after(function() { + return UserModel.removeAsync(); + }); + + it('should include signup form with correct inputs and submit button', function() { + <%= does("page.form.name.getAttribute('type')") %>.eventually.equal('text'); + <%= does("page.form.name.getAttribute('name')") %>.eventually.equal('name'); + <%= does("page.form.email.getAttribute('type')") %>.eventually.equal('email'); + <%= does("page.form.email.getAttribute('name')") %>.eventually.equal('email'); + <%= does("page.form.password.getAttribute('type')") %>.eventually.equal('password'); + <%= does("page.form.password.getAttribute('name')") %>.eventually.equal('password'); + <%= does("page.form.submit.getAttribute('type')") %>.eventually.equal('submit'); + <%= does("page.form.submit.getText()") %>.eventually.equal('Sign up'); + }); + + describe('with local auth', function() { + + it('should signup a new user, log them in, and redirecting to "/"', function(done) { + UserModel.remove(function() { + page.signup(testUser); + + var navbar = require('../../components/navbar/navbar.po'); + + <%= does("browser.getLocationAbsUrl()") %>.eventually.equal(config.baseUrl + '/'); + <%= does("navbar.navbarAccountGreeting.getText()") %>.eventually.equal('Hello ' + testUser.name); + + done(); + }); + }); + + describe('and invalid credentials', function() { + before(function() { + return loadPage(); + }); + + it('should indicate signup failures', function() { + page.signup(testUser); + + <%= does("browser.getLocationAbsUrl()") %>.eventually.equal(config.baseUrl + '/signup'); + <%= does("page.form.email.getAttribute('class')") %>.eventually.contain('ng-invalid-mongoose'); + + var helpBlock = page.form.element(by.css('.form-group.has-error .help-block.ng-binding')); + <%= does("helpBlock.getText()") %>.eventually.equal('The specified email address is already in use.'); + }); + + }); + + }); +}); diff --git a/app/templates/e2e/main/main.spec.js b/app/templates/e2e/main/main.spec(jasmine).js similarity index 100% rename from app/templates/e2e/main/main.spec.js rename to app/templates/e2e/main/main.spec(jasmine).js diff --git a/app/templates/e2e/main/main.spec(mocha).js b/app/templates/e2e/main/main.spec(mocha).js new file mode 100644 index 000000000..1962ba765 --- /dev/null +++ b/app/templates/e2e/main/main.spec(mocha).js @@ -0,0 +1,16 @@ +'use strict'; + +describe('Main View', function() { + var page; + + beforeEach(function() { + browser.get('/'); + page = require('./main.po'); + }); + + it('should include jumbotron with correct data', function() { + <%= does("page.h1El.getText()") %>.eventually.equal('\'Allo, \'Allo!'); + <%= does("page.imgEl.getAttribute('src')") %>.eventually.match(/assets\/images\/yeoman.png$/); + <%= does("page.imgEl.getAttribute('alt')") %>.eventually.equal('I\'m Yeoman'); + }); +}); diff --git a/app/templates/karma.conf.js b/app/templates/karma.conf.js index 57b3fa6f2..6b6c6c477 100644 --- a/app/templates/karma.conf.js +++ b/app/templates/karma.conf.js @@ -6,8 +6,9 @@ module.exports = function(config) { // base path, that will be used to resolve files and exclude basePath: '', - // testing framework to use (jasmine/mocha/qunit/...) - frameworks: ['jasmine'], + // testing framework to use (jasmine/mocha/qunit/...)<% if(filters.jasmine) { %> + frameworks: ['jasmine'],<% } if(filters.mocha) { %> + frameworks: ['mocha', 'chai', 'sinon-chai', 'chai-as-promised', 'chai-things'],<% } %> // list of files / patterns to load in the browser files: [ @@ -58,6 +59,14 @@ module.exports = function(config) { // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG logLevel: config.LOG_INFO, + // reporter types: + // - dots + // - progress (default) + // - spec (karma-spec-reporter) + // - junit + // - growl + // - coverage + reporters: ['spec'], // enable / disable watching file and executing tests whenever any file changes autoWatch: false, diff --git a/app/templates/mocha.conf.js b/app/templates/mocha.conf.js index 497d43b2c..54e33bb6f 100644 --- a/app/templates/mocha.conf.js +++ b/app/templates/mocha.conf.js @@ -1,14 +1,16 @@ 'use strict'; var chai = require('chai'); -var sinon = require('sinon'); -var sinonChai = require('sinon-chai'); -var chaiAsPromised = require('chai-as-promised'); +// Load Chai assertions global.expect = chai.expect; global.assert = chai.assert; -global.sinon = sinon; - chai.should(); -chai.use(sinonChai); -chai.use(chaiAsPromised); + +// Load Sinon +global.sinon = require('sinon'); + +// Initialize Chai plugins +chai.use(require('sinon-chai')); +chai.use(require('chai-as-promised')); +chai.use(require('chai-things')) diff --git a/app/templates/protractor.conf.js b/app/templates/protractor.conf.js index 18b9ba6af..14f704b3d 100644 --- a/app/templates/protractor.conf.js +++ b/app/templates/protractor.conf.js @@ -43,24 +43,40 @@ var config = { // Jasmine and Cucumber are fully supported as a test and assertion framework. // Mocha has limited beta support. You will need to include your own // assertion framework if working with mocha. - framework: 'jasmine', - + framework: '<% if(filters.jasmine) { %>jasmine<% } if(filters.mocha) { %>mocha<% } %>', +<% if(filters.jasmine) { %> // ----- Options to be passed to minijasminenode ----- // // See the full list at https://github.com/juliemr/minijasminenode jasmineNodeOpts: { defaultTimeoutInterval: 30000 - }, + },<% } if(filters.mocha) { %> + // ----- Options to be passed to mocha ----- + mochaOpts: { + reporter: 'spec', + timeout: 30000, + defaultTimeoutInterval: 30000 + },<% } %> // Prepare environment for tests params: { serverConfig: require('./server/config/environment') }, - onPrepare: function() { + onPrepare: function() {<% if(filters.mocha) { %> + // Load Mocha and Chai + plugins + require('./mocha.conf'); + + // Expose should assertions (see https://github.com/angular/protractor/issues/633) + Object.defineProperty( + protractor.promise.Promise.prototype, + 'should', + Object.getOwnPropertyDescriptor(Object.prototype, 'should') + ); +<% } %> var serverConfig = config.params.serverConfig; - // Setup mongo tests + // Setup mongo for tests var mongoose = require('mongoose-bird')(); mongoose.connect(serverConfig.mongo.uri, serverConfig.mongo.options); // Connect to database } diff --git a/script-base.js b/script-base.js index 2f44ce09f..5d6c7dd2e 100644 --- a/script-base.js +++ b/script-base.js @@ -18,8 +18,17 @@ var Generator = module.exports = function Generator() { this.cameledName = this._.camelize(this.name); this.classedName = this._.classify(this.name); + // dynamic assertion statement + this.does = this.is = function(foo) { + if (this.filters.should) { + return foo + '.should'; + } else { + return 'expect(' + foo + ').to'; + } + }.bind(this); + this.filters = this.config.get('filters'); this.sourceRoot(path.join(__dirname, '/templates')); }; -util.inherits(Generator, yeoman.generators.NamedBase); \ No newline at end of file +util.inherits(Generator, yeoman.generators.NamedBase); diff --git a/test/fixtures/bower.json b/test/fixtures/bower.json deleted file mode 100644 index 7d9aae354..000000000 --- a/test/fixtures/bower.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "tempApp", - "version": "0.0.0", - "dependencies": { - "angular": ">=1.2.*", - "json3": "~3.3.1", - "es5-shim": "~3.0.1", - "jquery": "~1.11.0", - "bootstrap-sass-official": "~3.1.1", - "bootstrap": "~3.1.1", - "angular-resource": ">=1.2.*", - "angular-cookies": ">=1.2.*", - "angular-sanitize": ">=1.2.*", - "angular-route": ">=1.2.*", - "angular-bootstrap": "~0.11.0", - "font-awesome": ">=4.1.0", - "lodash": "~2.4.1", - "angular-socket-io": "~0.6.0", - "angular-ui-router": "~0.2.10" - }, - "devDependencies": { - "angular-mocks": ">=1.2.*", - "angular-scenario": ">=1.2.*" - } -} diff --git a/test/fixtures/package.json b/test/fixtures/package.json deleted file mode 100644 index 48d48607e..000000000 --- a/test/fixtures/package.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "name": "tempApp", - "version": "0.0.0", - "main": "server/app.js", - "dependencies": { - "express": "~4.0.0", - "morgan": "~1.0.0", - "body-parser": "~1.5.0", - "method-override": "~1.0.0", - "serve-favicon": "~2.0.1", - "cookie-parser": "~1.0.1", - "express-session": "~1.0.2", - "errorhandler": "~1.0.0", - "compression": "~1.0.1", - "lodash": "~2.4.1", - "jade": "~1.2.0", - "ejs": "~0.8.4", - "mongoose": "~3.8.8", - "mongoose-bird": "~0.0.1", - "jsonwebtoken": "^0.3.0", - "express-jwt": "^0.1.3", - "passport": "~0.2.0", - "passport-local": "~0.1.6", - "passport-facebook": "latest", - "passport-twitter": "latest", - "passport-google-oauth": "latest", - "composable-middleware": "^0.3.0", - "connect-mongo": "^0.4.1", - "socket.io": "^1.0.6", - "socket.io-client": "^1.0.6", - "socketio-jwt": "^2.0.2" - }, - "devDependencies": { - "chai-as-promised": "^4.1.1", - "grunt": "~0.4.4", - "grunt-autoprefixer": "~0.7.2", - "grunt-wiredep": "~1.8.0", - "grunt-concurrent": "~0.5.0", - "grunt-contrib-clean": "~0.5.0", - "grunt-contrib-concat": "~0.4.0", - "grunt-contrib-copy": "~0.5.0", - "grunt-contrib-cssmin": "~0.9.0", - "grunt-contrib-htmlmin": "~0.2.0", - "grunt-contrib-imagemin": "~0.7.1", - "grunt-contrib-jshint": "~0.10.0", - "grunt-contrib-uglify": "~0.4.0", - "grunt-contrib-watch": "~0.6.1", - "grunt-contrib-coffee": "^0.10.1", - "grunt-contrib-jade": "^0.11.0", - "grunt-contrib-less": "^0.11.4", - "grunt-google-cdn": "~0.4.0", - "grunt-newer": "~0.7.0", - "grunt-ng-annotate": "^0.2.3", - "grunt-rev": "~0.1.0", - "grunt-svgmin": "~0.4.0", - "grunt-usemin": "~2.1.1", - "grunt-env": "~0.4.1", - "grunt-node-inspector": "~0.1.5", - "grunt-nodemon": "~0.2.0", - "grunt-angular-templates": "^0.5.4", - "grunt-dom-munger": "^3.4.0", - "grunt-protractor-runner": "^1.1.0", - "grunt-asset-injector": "^0.1.0", - "grunt-karma": "~0.8.2", - "grunt-build-control": "DaftMonk/grunt-build-control", - "grunt-mocha-test": "~0.10.2", - "grunt-mocha-istanbul": "^2.0.0", - "grunt-contrib-sass": "^0.7.3", - "grunt-contrib-stylus": "latest", - "jit-grunt": "^0.5.0", - "time-grunt": "~0.3.1", - "grunt-express-server": "~0.4.17", - "grunt-open": "~0.2.3", - "open": "~0.0.4", - "jshint-stylish": "~0.1.5", - "connect-livereload": "~0.4.0", - "karma-ng-scenario": "~0.1.0", - "karma-firefox-launcher": "~0.1.3", - "karma-script-launcher": "~0.1.0", - "karma-html2js-preprocessor": "~0.1.0", - "karma-ng-jade2js-preprocessor": "^0.1.2", - "karma-jasmine": "~0.1.5", - "karma-chrome-launcher": "~0.1.3", - "requirejs": "~2.1.11", - "karma-requirejs": "~0.2.1", - "karma-coffee-preprocessor": "~0.2.1", - "karma-jade-preprocessor": "0.0.11", - "karma-phantomjs-launcher": "~0.1.4", - "karma": "~0.12.9", - "karma-ng-html2js-preprocessor": "~0.1.0", - "proxyquire": "^1.0.1", - "supertest": "~0.11.0", - "sinon-chai": "^2.5.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "scripts": { - "start": "node server/app.js", - "test": "grunt test", - "update-webdriver": "node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update" - }, - "private": true -} diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 31ed71ab2..7db1d4afe 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -14,6 +14,8 @@ describe('angular-fullstack generator', function () { markup: 'html', stylesheet: 'sass', router: 'uirouter', + testing: 'mocha', + chai: 'expect', bootstrap: true, uibootstrap: true, mongoose: true, @@ -35,9 +37,18 @@ describe('angular-fullstack generator', function () { }); } + /** + * 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} path - top level path to assert files at (optional) + * @param {Array} skip - array of paths to skip/ignore (optional) + * + */ function assertOnlyFiles(expectedFiles, done, path, skip) { path = path || './'; - skip = skip || ['e2e', 'node_modules', 'client/bower_components']; + skip = skip || ['node_modules', 'client/bower_components']; recursiveReadDir(path, skip, function(err, actualFiles) { if (err) { return done(err); } @@ -61,16 +72,16 @@ describe('angular-fullstack generator', function () { }); } - function runE2E(self, done) { - self.timeout(240000); // 4 minutes - gen.run({}, function () { - exec('grunt test:e2e', function (error, stdout, stderr) { - expect(stdout, 'Client tests failed \n' + stdout).to.contain('0 failures'); - 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, @@ -81,15 +92,14 @@ describe('angular-fullstack generator', function () { var execFn = function() { exec(cmd, function(error, stdout, stderr) { switch(cmd) { - case 'grunt test:client': - expect(stdout, 'Client tests failed \n' + stdout ).to.contain('Executed 1 of 1\u001b[32m SUCCESS\u001b'); - break; case 'grunt jshint': - expect(stdout).to.contain('Done, without errors.'); - break; case 'grunt jscs': expect(stdout).to.contain('Done, without errors.'); break; + case 'grunt test:client': + case 'grunt test:e2e': + expect(stdout, 'Client tests failed \n' + stdout).to.contain('Done, without errors.'); + break; case 'grunt test:server': expect(stdout, 'Server tests failed (do you have mongoDB running?) \n' + stdout).to.contain('Done, without errors.'); break; @@ -108,6 +118,13 @@ describe('angular-fullstack generator', function () { } } + /** + * 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: { @@ -127,6 +144,13 @@ describe('angular-fullstack generator', function () { }, 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', @@ -139,6 +163,7 @@ describe('angular-fullstack generator', function () { markup = mapping.markup[ops.markup], stylesheet = mapping.stylesheet[ops.stylesheet]; + /* Core Files */ files = files.concat([ 'client/.htaccess', 'client/.jshintrc', @@ -172,6 +197,9 @@ describe('angular-fullstack generator', function () { 'server/config/environment/production.js', 'server/config/environment/test.js', 'server/views/404.' + markup, + 'e2e/main/main.po.js', + 'e2e/main/main.spec.js', + 'e2e/components/navbar/navbar.po.js', '.bowerrc', '.buildignore', '.editorconfig', @@ -187,12 +215,14 @@ describe('angular-fullstack generator', function () { 'protractor.conf.js' ]); + /* 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, @@ -201,6 +231,7 @@ describe('angular-fullstack generator', function () { ]); } + /* Mongoose */ if (ops.mongoose) { files = files.concat([ 'server/api/thing/thing.model.js', @@ -208,6 +239,7 @@ describe('angular-fullstack generator', function () { ]); } + /* Authentication */ if (ops.auth) { files = files.concat([ 'client/app/account/account.' + script, @@ -234,16 +266,23 @@ describe('angular-fullstack generator', function () { 'server/auth/index.js', 'server/auth/auth.service.js', 'server/auth/local/index.js', - 'server/auth/local/passport.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' ]); } + /* OAuth (see oauthFiles function above) */ if (ops.oauth) { ops.oauth.forEach(function(type, i) { files = files.concat(oauthFiles(type.replace('Auth', ''))); }); } + /* Socket.IO */ if (ops.socketio) { files = files.concat([ 'client/components/socket/socket.service.' + script, @@ -361,7 +400,7 @@ describe('angular-fullstack generator', function () { }); it('should run e2e tests successfully', function(done) { - runE2E(this, done); + runTest('grunt test:e2e', this, done, 240000); }); }); @@ -371,6 +410,7 @@ describe('angular-fullstack generator', function () { markup: 'jade', stylesheet: 'less', router: 'uirouter', + testing: 'jasmine', mongoose: true, auth: true, oauth: ['twitterAuth', 'facebookAuth', 'googleAuth'], @@ -417,7 +457,7 @@ describe('angular-fullstack generator', function () { }); it('should run e2e tests successfully', function(done) { - runE2E(this, done); + runTest('grunt test:e2e', this, done, 240000); }); }); @@ -427,6 +467,8 @@ describe('angular-fullstack generator', function () { markup: 'jade', stylesheet: 'stylus', router: 'ngroute', + testing: 'mocha', + chai: 'should', mongoose: false, auth: false, oauth: [], @@ -474,7 +516,7 @@ describe('angular-fullstack generator', function () { }); it('should run e2e tests successfully', function(done) { - runE2E(this, done); + runTest('grunt test:e2e', this, done, 240000); }); }); @@ -484,6 +526,7 @@ describe('angular-fullstack generator', function () { markup: 'html', stylesheet: 'css', router: 'ngroute', + testing: 'jasmine', mongoose: false, auth: false, oauth: [], @@ -523,7 +566,7 @@ describe('angular-fullstack generator', function () { }); it('should run e2e tests successfully', function(done) { - runE2E(this, done); + runTest('grunt test:e2e', this, done, 240000); }); }); }); From 696c6fb9336e3319083dd7a684da541996b22db7 Mon Sep 17 00:00:00 2001 From: BenFradet Date: Sat, 27 Sep 2014 17:16:18 +0200 Subject: [PATCH 042/171] docs(gen:endpoint): standardized comments --- endpoint/templates/name.controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/endpoint/templates/name.controller.js b/endpoint/templates/name.controller.js index 2afb2be8c..87dd641de 100644 --- a/endpoint/templates/name.controller.js +++ b/endpoint/templates/name.controller.js @@ -50,7 +50,7 @@ function removeEntity(res){ }; }<% } %> -// Get list of <%= name %>s +// Gets list of <%= name %>s from the DB. exports.index = function(req, res) {<% if (!filters.mongoose) { %> res.json([]);<% } if (filters.mongoose) { %> <%= classedName %>.findAsync() @@ -58,7 +58,7 @@ exports.index = function(req, res) {<% if (!filters.mongoose) { %> .catch(handleError(res));<% } %> };<% if (filters.mongoose) { %> -// Get a single <%= name %> +// Gets a single <%= name %> from the DB. exports.show = function(req, res) { <%= classedName %>.findByIdAsync(req.params.id) .then(handleEntityNotFound(res)) From a29b71310239b2ef6a7e39ea077781abb78e582f Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sat, 27 Sep 2014 23:52:15 -0600 Subject: [PATCH 043/171] remove sauce connect addon from travis and replace with shell script --- .travis.yml | 5 ++- scripts/sauce_connect_setup.sh | 60 ++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) create mode 100755 scripts/sauce_connect_setup.sh diff --git a/.travis.yml b/.travis.yml index e1abfbd26..598a4b2ae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,9 @@ env: global: - SAUCE_USERNAME=fullstack_ci - SAUCE_ACCESS_KEY=1a527ca6-4aa5-4618-86ce-0278bf158cbf -addons: - sauce_connect: true before_install: + - ./scripts/sauce_connect_setup.sh - gem update --system - gem install sass --version "=3.3.7" - npm install -g bower grunt-cli -services: mongodb +services: mongodb \ No newline at end of file diff --git a/scripts/sauce_connect_setup.sh b/scripts/sauce_connect_setup.sh new file mode 100755 index 000000000..06b6e2092 --- /dev/null +++ b/scripts/sauce_connect_setup.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# Setup and start Sauce Connect for your TravisCI build +# This script requires your .travis.yml to include the following two private env variables: +# SAUCE_USERNAME +# SAUCE_ACCESS_KEY +# Follow the steps at https://saucelabs.com/opensource/travis to set that up. +# https://gist.githubusercontent.com/santiycr/4683e262467c0a84d857/raw/1254ace59257e341ab200cbc8946e626756f079f/sauce-connect.sh + +if [ -z "${SAUCE_USERNAME}" ] || [ -z "${SAUCE_ACCESS_KEY}" ]; then + echo "This script can't run without your Sauce credentials" + echo "Please set SAUCE_USERNAME and SAUCE_ACCESS_KEY env variables" + echo "export SAUCE_USERNAME=ur-username" + echo "export SAUCE_ACCESS_KEY=ur-access-key" + exit 1 +fi + +SAUCE_TMP_DIR="$(mktemp -d -t sc.XXXX)" +echo "Using temp dir $SAUCE_TMP_DIR" +pushd $SAUCE_TMP_DIR + +SAUCE_CONNECT_PLATFORM=$(uname | sed -e 's/Darwin/osx/' -e 's/Linux/linux/') +case "${SAUCE_CONNECT_PLATFORM}" in + linux) + SC_DISTRIBUTION_FMT=tar.gz;; + *) + SC_DISTRIBUTION_FMT=zip;; +esac +SC_DISTRIBUTION=sc-latest-${SAUCE_CONNECT_PLATFORM}.${SC_DISTRIBUTION_FMT} +SC_READYFILE=sauce-connect-ready-$RANDOM +SC_LOGFILE=$HOME/sauce-connect.log +if [ ! -z "${TRAVIS_JOB_NUMBER}" ]; then + SC_TUNNEL_ID="-i ${TRAVIS_JOB_NUMBER}" +fi +echo "Downloading Sauce Connect" +wget https://saucelabs.com/downloads/${SC_DISTRIBUTION} +SC_DIR=$(tar -ztf ${SC_DISTRIBUTION} | head -n1) + +echo "Extracting Sauce Connect" +case "${SC_DISTRIBUTION_FMT}" in + tar.gz) + tar zxf $SC_DISTRIBUTION;; + zip) + unzip $SC_DISTRIBUTION;; +esac + +echo "Starting Sauce Connect" +${SC_DIR}/bin/sc \ + ${SC_TUNNEL_ID} \ + -f ${SC_READYFILE} \ + -l ${SC_LOGFILE} & + +echo "Waiting for Sauce Connect readyfile" +while [ ! -f ${SC_READYFILE} ]; do + sleep .5 +done + +unset SAUCE_CONNECT_PLATFORM SAUCE_TMP_DIR SC_DIR SC_DISTRIBUTION SC_READYFILE SC_LOGFILE SC_TUNNEL_ID + +popd \ No newline at end of file From 8a1a245f212c24604fc78ba8442a87af31586537 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 21 Sep 2014 22:11:35 -0600 Subject: [PATCH 044/171] feat(app): add grunt jscs task for maintaining consistent code style --- app/templates/.jscs.json | 3 +++ app/templates/Gruntfile.js | 15 +++++++++++++++ app/templates/_package.json | 1 + 3 files changed, 19 insertions(+) create mode 100644 app/templates/.jscs.json diff --git a/app/templates/.jscs.json b/app/templates/.jscs.json new file mode 100644 index 000000000..dc46a2f34 --- /dev/null +++ b/app/templates/.jscs.json @@ -0,0 +1,3 @@ +{ + "preset": "google" +} \ No newline at end of file diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index 6cd422daa..3e2da06c8 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -192,6 +192,21 @@ module.exports = function (grunt) { } }, + jscs: { + options: { + config: ".jscs.json" + }, + main: { + files: { + src: [ + '<%%= yeoman.client %>/app/**/*.js', + '<%%= yeoman.client %>/app/**/*.js', + 'server/**/*.js' + ] + } + } + }, + // Empties folders to start fresh clean: { dist: { diff --git a/app/templates/_package.json b/app/templates/_package.json index 08784aa6c..3775d0d90 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -48,6 +48,7 @@ "grunt-contrib-jade": "^0.11.0",<% } %><% if(filters.less) { %> "grunt-contrib-less": "^0.11.4",<% } %> "grunt-google-cdn": "~0.4.0", + "grunt-jscs": "~0.7.1", "grunt-newer": "~0.7.0", "grunt-ng-annotate": "^0.2.3", "grunt-rev": "~0.1.0", From 18c4c31d3c8c35690810966e0e27b22f74c26a13 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sat, 27 Sep 2014 01:41:39 -0600 Subject: [PATCH 045/171] style(app): update style to match jscs style guide --- app/templates/.jscs.json | 11 ++- app/templates/Gruntfile.js | 86 +++++++++---------- app/templates/_.gitignore | 2 +- app/templates/_bower.json | 10 +-- app/templates/_package.json | 30 +++---- .../app/account(auth)/account(coffee).coffee | 4 +- .../client/app/account(auth)/account(js).js | 4 +- .../app/account(auth)/login/login(html).html | 10 +-- .../app/account(auth)/login/login(jade).jade | 10 +-- .../app/account(auth)/login/login(less).less | 2 +- .../app/account(auth)/login/login(sass).scss | 2 +- .../login/login.controller(coffee).coffee | 6 +- .../login/login.controller(js).js | 12 +-- .../settings/settings.controller(js).js | 24 +++--- .../account(auth)/signup/signup(html).html | 10 +-- .../account(auth)/signup/signup(jade).jade | 10 +-- .../signup/signup.controller(coffee).coffee | 6 +- .../signup/signup.controller(js).js | 12 +-- .../app/admin(auth)/admin(coffee).coffee | 4 +- .../client/app/admin(auth)/admin(js).js | 6 +- .../app/admin(auth)/admin.controller(js).js | 2 +- app/templates/client/app/app(coffee).coffee | 22 ++--- app/templates/client/app/app(css).css | 4 +- app/templates/client/app/app(js).js | 26 +++--- app/templates/client/app/app(less).less | 6 +- app/templates/client/app/app(sass).scss | 6 +- app/templates/client/app/app(stylus).styl | 4 +- .../client/app/main/main(coffee).coffee | 4 +- app/templates/client/app/main/main(html).html | 4 +- app/templates/client/app/main/main(jade).jade | 4 +- app/templates/client/app/main/main(js).js | 6 +- .../app/main/main.controller(coffee).coffee | 8 +- .../client/app/main/main.controller(js).js | 12 +-- .../main/main.controller.spec(coffee).coffee | 10 +-- .../app/main/main.controller.spec(js).js | 20 ++--- .../components/navbar/navbar(html).html | 18 ++-- .../components/navbar/navbar(jade).jade | 22 ++--- .../navbar/navbar.controller(coffee).coffee | 6 +- .../navbar/navbar.controller(js).js | 6 +- app/templates/client/index.html | 4 +- .../e2e/components/navbar/navbar.po.js | 2 +- app/templates/karma.conf.js | 10 +-- app/templates/protractor.conf.js | 8 +- app/templates/server/api/thing/index.js | 2 +- app/templates/server/api/thing/index.spec.js | 30 +++++-- .../server/api/thing/thing.controller.js | 73 ++++++++-------- .../server/api/thing/thing.integration.js | 28 ++++-- .../server/api/thing/thing.model(mongoose).js | 4 +- .../api/thing/thing.socket(socketio).js | 4 +- .../server/api/user(auth)/index.spec.js | 24 ++++-- .../server/api/user(auth)/user.controller.js | 32 ++++--- .../server/api/user(auth)/user.integration.js | 4 +- .../server/api/user(auth)/user.model.js | 72 ++++++++++------ .../server/api/user(auth)/user.model.spec.js | 4 +- app/templates/server/app.js | 4 +- .../server/auth(auth)/auth.service.js | 21 +++-- .../facebook(facebookAuth)/index.js | 2 +- .../facebook(facebookAuth)/passport.js | 63 +++++++------- .../auth(auth)/google(googleAuth)/index.js | 2 +- .../auth(auth)/google(googleAuth)/passport.js | 55 ++++++------ app/templates/server/auth(auth)/index.js | 2 +- .../server/auth(auth)/local/index.js | 16 ++-- .../server/auth(auth)/local/passport.js | 48 ++++++----- .../auth(auth)/twitter(twitterAuth)/index.js | 2 +- .../twitter(twitterAuth)/passport.js | 11 +-- .../server/components/errors/index.js | 6 +- app/templates/server/config/_local.env.js | 2 +- .../server/config/environment/index.js | 10 +-- .../server/config/environment/production.js | 5 +- .../server/config/environment/test.js | 2 +- app/templates/server/config/express.js | 6 +- app/templates/server/config/seed(mongoose).js | 40 +++++---- .../server/config/socketio(socketio).js | 17 ++-- app/templates/server/routes.js | 6 +- endpoint/templates/index.js | 2 +- endpoint/templates/index.spec.js | 24 ++++-- endpoint/templates/name.controller.js | 36 ++++---- endpoint/templates/name.integration.js | 24 ++++-- endpoint/templates/name.model(mongoose).js | 4 +- endpoint/templates/name.socket(socketio).js | 6 +- test/test-file-creation.js | 29 +++++++ 81 files changed, 676 insertions(+), 521 deletions(-) diff --git a/app/templates/.jscs.json b/app/templates/.jscs.json index dc46a2f34..1c278d0c4 100644 --- a/app/templates/.jscs.json +++ b/app/templates/.jscs.json @@ -1,3 +1,12 @@ { - "preset": "google" + "preset": "google", + "maximumLineLength": { + "value": 100, + "allowComments": true, + "allowRegex": true + }, + "requireCamelCaseOrUpperCaseIdentifiers": "ignoreProperties", + "requireSpacesInsideObjectBrackets": "all", + "disallowSpacesInsideObjectBrackets": "nested", + "requireSpacesInConditionalExpression": true } \ No newline at end of file diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index 3e2da06c8..224e84c66 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -80,7 +80,7 @@ module.exports = function (grunt) { '<%%= yeoman.client %>/{app,components}/**/*.mock.js' ], tasks: ['newer:jshint:all', 'karma'] - },<% if(filters.stylus) { %> + },<% if (filters.stylus) { %> injectStylus: { files: [ '<%%= yeoman.client %>/{app,components}/**/*.styl'], @@ -90,7 +90,7 @@ module.exports = function (grunt) { files: [ '<%%= yeoman.client %>/{app,components}/**/*.styl'], tasks: ['stylus', 'autoprefixer'] - },<% } %><% if(filters.sass) { %> + },<% } %><% if (filters.sass) { %> injectSass: { files: [ '<%%= yeoman.client %>/{app,components}/**/*.{scss,sass}'], @@ -100,7 +100,7 @@ module.exports = function (grunt) { files: [ '<%%= yeoman.client %>/{app,components}/**/*.{scss,sass}'], tasks: ['sass', 'autoprefixer'] - },<% } %><% if(filters.less) { %> + },<% } %><% if (filters.less) { %> injectLess: { files: [ '<%%= yeoman.client %>/{app,components}/**/*.less'], @@ -110,13 +110,13 @@ module.exports = function (grunt) { files: [ '<%%= yeoman.client %>/{app,components}/**/*.less'], tasks: ['less', 'autoprefixer'] - },<% } %><% if(filters.jade) { %> + },<% } %><% if (filters.jade) { %> jade: { files: [ '<%%= yeoman.client %>/{app,components}/*', '<%%= yeoman.client %>/{app,components}/**/*.jade'], tasks: ['jade'] - },<% } %><% if(filters.coffee) { %> + },<% } %><% if (filters.coffee) { %> coffee: { files: [ '<%%= yeoman.client %>/{app,components}/**/*.{coffee,litcoffee,coffee.md}', @@ -458,18 +458,18 @@ module.exports = function (grunt) { // Run some tasks in parallel to speed up the build process concurrent: { - server: [<% if(filters.coffee) { %> - 'coffee',<% } %><% if(filters.jade) { %> - 'jade',<% } %><% if(filters.stylus) { %> - 'stylus',<% } %><% if(filters.sass) { %> - 'sass',<% } %><% if(filters.less) { %> + server: [<% if (filters.coffee) { %> + 'coffee',<% } %><% if (filters.jade) { %> + 'jade',<% } %><% if (filters.stylus) { %> + 'stylus',<% } %><% if (filters.sass) { %> + 'sass',<% } %><% if (filters.less) { %> 'less',<% } %> ], - test: [<% if(filters.coffee) { %> - 'coffee',<% } %><% if(filters.jade) { %> - 'jade',<% } %><% if(filters.stylus) { %> - 'stylus',<% } %><% if(filters.sass) { %> - 'sass',<% } %><% if(filters.less) { %> + test: [<% if (filters.coffee) { %> + 'coffee',<% } %><% if (filters.jade) { %> + 'jade',<% } %><% if (filters.stylus) { %> + 'stylus',<% } %><% if (filters.sass) { %> + 'sass',<% } %><% if (filters.less) { %> 'less',<% } %> ], debug: { @@ -481,11 +481,11 @@ module.exports = function (grunt) { logConcurrentOutput: true } }, - dist: [<% if(filters.coffee) { %> - 'coffee',<% } %><% if(filters.jade) { %> - 'jade',<% } %><% if(filters.stylus) { %> - 'stylus',<% } %><% if(filters.sass) { %> - 'sass',<% } %><% if(filters.less) { %> + dist: [<% if (filters.coffee) { %> + 'coffee',<% } %><% if (filters.jade) { %> + 'jade',<% } %><% if (filters.stylus) { %> + 'stylus',<% } %><% if (filters.sass) { %> + 'sass',<% } %><% if (filters.less) { %> 'less',<% } %> 'imagemin', 'svgmin' @@ -579,7 +579,7 @@ module.exports = function (grunt) { NODE_ENV: 'production' }, all: localConfig - },<% if(filters.jade) { %> + },<% if (filters.jade) { %> // Compiles Jade to html jade: { @@ -599,7 +599,7 @@ module.exports = function (grunt) { ext: '.html' }] } - },<% } %><% if(filters.coffee) { %> + },<% } %><% if (filters.coffee) { %> // Compiles CoffeeScript to JavaScript coffee: { @@ -619,7 +619,7 @@ module.exports = function (grunt) { ext: '.js' }] } - },<% } %><% if(filters.stylus) { %> + },<% } %><% if (filters.stylus) { %> // Compiles Stylus to CSS stylus: { @@ -636,7 +636,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: { @@ -653,7 +653,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: { @@ -694,7 +694,7 @@ module.exports = function (grunt) { '!{.tmp,<%%= yeoman.client %>}/{app,components}/**/*.mock.js'] ] } - },<% if(filters.stylus) { %> + },<% if (filters.stylus) { %> // Inject component styl into app.styl stylus: { @@ -713,7 +713,7 @@ module.exports = function (grunt) { '!<%%= yeoman.client %>/app/app.styl' ] } - },<% } %><% if(filters.sass) { %> + },<% } %><% if (filters.sass) { %> // Inject component scss into app.scss sass: { @@ -732,7 +732,7 @@ module.exports = function (grunt) { '!<%%= yeoman.client %>/app/app.{scss,sass}' ] } - },<% } %><% if(filters.less) { %> + },<% } %><% if (filters.less) { %> // Inject component less into app.less less: { @@ -797,9 +797,9 @@ module.exports = function (grunt) { if (target === 'debug') { return grunt.task.run([ 'clean:server', - 'env:all',<% if(filters.stylus) { %> - 'injector:stylus', <% } %><% if(filters.less) { %> - 'injector:less', <% } %><% if(filters.sass) { %> + 'env:all',<% if (filters.stylus) { %> + 'injector:stylus', <% } %><% if (filters.less) { %> + 'injector:less', <% } %><% if (filters.sass) { %> 'injector:sass', <% } %> 'concurrent:server', 'injector', @@ -811,9 +811,9 @@ module.exports = function (grunt) { grunt.task.run([ 'clean:server', - 'env:all',<% if(filters.stylus) { %> - 'injector:stylus', <% } %><% if(filters.less) { %> - 'injector:less', <% } %><% if(filters.sass) { %> + 'env:all',<% if (filters.stylus) { %> + 'injector:stylus', <% } %><% if (filters.less) { %> + 'injector:less', <% } %><% if (filters.sass) { %> 'injector:sass', <% } %> 'concurrent:server', 'injector', @@ -844,9 +844,9 @@ module.exports = function (grunt) { else if (target === 'client') { return grunt.task.run([ 'clean:server', - 'env:all',<% if(filters.stylus) { %> - 'injector:stylus', <% } %><% if(filters.less) { %> - 'injector:less', <% } %><% if(filters.sass) { %> + 'env:all',<% if (filters.stylus) { %> + 'injector:stylus', <% } %><% if (filters.less) { %> + 'injector:less', <% } %><% if (filters.sass) { %> 'injector:sass', <% } %> 'concurrent:test', 'injector', @@ -859,9 +859,9 @@ module.exports = function (grunt) { return grunt.task.run([ 'clean:server', 'env:all', - 'env:test',<% if(filters.stylus) { %> - 'injector:stylus', <% } %><% if(filters.less) { %> - 'injector:less', <% } %><% if(filters.sass) { %> + 'env:test',<% if (filters.stylus) { %> + 'injector:stylus', <% } %><% if (filters.less) { %> + 'injector:less', <% } %><% if (filters.sass) { %> 'injector:sass', <% } %> 'concurrent:test', 'injector', @@ -914,9 +914,9 @@ module.exports = function (grunt) { }); grunt.registerTask('build', [ - 'clean:dist',<% if(filters.stylus) { %> - 'injector:stylus', <% } %><% if(filters.less) { %> - 'injector:less', <% } %><% if(filters.sass) { %> + 'clean:dist',<% if (filters.stylus) { %> + 'injector:stylus', <% } %><% if (filters.less) { %> + 'injector:less', <% } %><% if (filters.sass) { %> 'injector:sass', <% } %> 'concurrent:dist', 'injector', diff --git a/app/templates/_.gitignore b/app/templates/_.gitignore index 6b4afbb0f..6d88c2a0a 100644 --- a/app/templates/_.gitignore +++ b/app/templates/_.gitignore @@ -1,6 +1,6 @@ node_modules public -.tmp<% if(filters.sass) { %> +.tmp<% if (filters.sass) { %> .sass-cache<% } %> .idea client/bower_components diff --git a/app/templates/_bower.json b/app/templates/_bower.json index 1681050a2..aaf0f8b81 100644 --- a/app/templates/_bower.json +++ b/app/templates/_bower.json @@ -5,17 +5,17 @@ "angular": ">=1.2.*", "json3": "~3.3.1", "es5-shim": "~3.0.1", - "jquery": "~1.11.0",<% if(filters.bootstrap) { %><% if (filters.sass) { %> + "jquery": "~1.11.0",<% if (filters.bootstrap) { %><% if (filters.sass) { %> "bootstrap-sass-official": "~3.1.1",<% } %> "bootstrap": "~3.1.1",<% } %> "angular-resource": ">=1.2.*", "angular-cookies": ">=1.2.*", - "angular-sanitize": ">=1.2.*",<% if(filters.ngroute) { %> - "angular-route": ">=1.2.*",<% } %><% if(filters.uibootstrap) { %> + "angular-sanitize": ">=1.2.*",<% if (filters.ngroute) { %> + "angular-route": ">=1.2.*",<% } %><% if (filters.uibootstrap) { %> "angular-bootstrap": "~0.11.0",<% } %> "font-awesome": ">=4.1.0", - "lodash": "~2.4.1"<% if(filters.socketio) { %>, - "angular-socket-io": "~0.6.0"<% } %><% if(filters.uirouter) { %>, + "lodash": "~2.4.1"<% if (filters.socketio) { %>, + "angular-socket-io": "~0.6.0"<% } %><% if (filters.uirouter) { %>, "angular-ui-router": "~0.2.10"<% } %> }, "devDependencies": { diff --git a/app/templates/_package.json b/app/templates/_package.json index 3775d0d90..8963ff15d 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -12,20 +12,20 @@ "express-session": "~1.0.2", "errorhandler": "~1.0.0", "compression": "~1.0.1", - "lodash": "~2.4.1",<% if(filters.jade) { %> - "jade": "~1.2.0",<% } %><% if(filters.html) { %> - "ejs": "~0.8.4",<% } %><% if(filters.mongoose) { %> + "lodash": "~2.4.1",<% if (filters.jade) { %> + "jade": "~1.2.0",<% } %><% if (filters.html) { %> + "ejs": "~0.8.4",<% } %><% if (filters.mongoose) { %> "mongoose": "~3.8.8", - "mongoose-bird": "~0.0.1",<% } %><% if(filters.auth) { %> + "mongoose-bird": "~0.0.1",<% } %><% if (filters.auth) { %> "jsonwebtoken": "^0.3.0", "express-jwt": "^0.1.3", "passport": "~0.2.0", - "passport-local": "~0.1.6",<% } %><% if(filters.facebookAuth) { %> - "passport-facebook": "latest",<% } %><% if(filters.twitterAuth) { %> - "passport-twitter": "latest",<% } %><% if(filters.googleAuth) { %> + "passport-local": "~0.1.6",<% } %><% if (filters.facebookAuth) { %> + "passport-facebook": "latest",<% } %><% if (filters.twitterAuth) { %> + "passport-twitter": "latest",<% } %><% if (filters.googleAuth) { %> "passport-google-oauth": "latest",<% } %> "composable-middleware": "^0.3.0", - "connect-mongo": "^0.4.1"<% if(filters.socketio) { %>, + "connect-mongo": "^0.4.1"<% if (filters.socketio) { %>, "socket.io": "^1.0.6", "socket.io-client": "^1.0.6", "socketio-jwt": "^2.0.2"<% } %> @@ -43,9 +43,9 @@ "grunt-contrib-imagemin": "~0.7.1", "grunt-contrib-jshint": "~0.10.0", "grunt-contrib-uglify": "~0.4.0", - "grunt-contrib-watch": "~0.6.1",<% if(filters.coffee) { %> - "grunt-contrib-coffee": "^0.10.1",<% } %><% if(filters.jade) { %> - "grunt-contrib-jade": "^0.11.0",<% } %><% if(filters.less) { %> + "grunt-contrib-watch": "~0.6.1",<% if (filters.coffee) { %> + "grunt-contrib-coffee": "^0.10.1",<% } %><% if (filters.jade) { %> + "grunt-contrib-jade": "^0.11.0",<% } %><% if (filters.less) { %> "grunt-contrib-less": "^0.11.4",<% } %> "grunt-google-cdn": "~0.4.0", "grunt-jscs": "~0.7.1", @@ -62,8 +62,8 @@ "grunt-protractor-runner": "^1.1.0", "grunt-asset-injector": "^0.1.0", "grunt-karma": "~0.8.2", - "grunt-build-control": "DaftMonk/grunt-build-control",<% if(filters.sass) { %> - "grunt-contrib-sass": "^0.7.3",<% } %><% if(filters.stylus) { %> + "grunt-build-control": "DaftMonk/grunt-build-control",<% if (filters.sass) { %> + "grunt-contrib-sass": "^0.7.3",<% } %><% if (filters.stylus) { %> "grunt-contrib-stylus": "latest",<% } %> "jit-grunt": "^0.5.0", "time-grunt": "~0.3.1", @@ -76,9 +76,9 @@ "grunt-mocha-istanbul": "^2.0.0", "chai-as-promised": "^4.1.1", "chai-things": "^0.2.0", - "sinon-chai": "^2.5.0",<% if(filters.mocha) { %> + "sinon-chai": "^2.5.0",<% if (filters.mocha) { %> "karma-mocha": "^0.1.9", - "karma-chai-plugins": "^0.2.3",<% } if(filters.jasmine) { %> + "karma-chai-plugins": "^0.2.3",<% } if (filters.jasmine) { %> "karma-jasmine": "~0.1.5",<% } %> "karma-ng-scenario": "~0.1.0", "karma-firefox-launcher": "~0.1.3", diff --git a/app/templates/client/app/account(auth)/account(coffee).coffee b/app/templates/client/app/account(auth)/account(coffee).coffee index 088fb6840..c794d7f04 100644 --- a/app/templates/client/app/account(auth)/account(coffee).coffee +++ b/app/templates/client/app/account(auth)/account(coffee).coffee @@ -1,7 +1,7 @@ 'use strict' angular.module '<%= scriptAppName %>' -<% if(filters.ngroute) { %>.config ($routeProvider) -> +<% if (filters.ngroute) { %>.config ($routeProvider) -> $routeProvider .when '/login', templateUrl: 'app/account/login/login.html' @@ -27,7 +27,7 @@ angular.module '<%= scriptAppName %>' .run ($rootScope) -> $rootScope.$on '$routeChangeStart', (event, next, current) -> next.referrer = current.originalPath if next.name is "logout" and current and current.originalPath and not current.authenticate -<% } %><% if(filters.uirouter) { %>.config ($stateProvider) -> +<% } %><% if (filters.uirouter) { %>.config ($stateProvider) -> $stateProvider .state 'login', url: '/login' diff --git a/app/templates/client/app/account(auth)/account(js).js b/app/templates/client/app/account(auth)/account(js).js index 8c005c952..d60fd72fe 100644 --- a/app/templates/client/app/account(auth)/account(js).js +++ b/app/templates/client/app/account(auth)/account(js).js @@ -1,7 +1,7 @@ 'use strict'; angular.module('<%= scriptAppName %>') - <% if(filters.ngroute) { %>.config(function ($routeProvider) { + <% if (filters.ngroute) { %>.config(function($routeProvider) { $routeProvider .when('/login', { templateUrl: 'app/account/login/login.html', @@ -35,7 +35,7 @@ angular.module('<%= scriptAppName %>') next.referrer = current.originalPath; } }); - });<% } %><% if(filters.uirouter) { %>.config(function ($stateProvider) { + });<% } %><% if (filters.uirouter) { %>.config(function($stateProvider) { $stateProvider .state('login', { url: '/login', diff --git a/app/templates/client/app/account(auth)/login/login(html).html b/app/templates/client/app/account(auth)/login/login(html).html index 49a81b55d..f02de2616 100644 --- a/app/templates/client/app/account(auth)/login/login(html).html +++ b/app/templates/client/app/account(auth)/login/login(html).html @@ -37,19 +37,19 @@

Login

- ui-sref="signup"<% } else { %>href="/signup"<% } %>> + ui-sref="signup"<% } else { %>href="/signup"<% } %>> Register -<% if(filters.oauth) {%> +<% if (filters.oauth) {%>
-
<% if(filters.facebookAuth) {%> +
<% if (filters.facebookAuth) {%> Connect with Facebook - <% } %><% if(filters.googleAuth) {%> + <% } %><% if (filters.googleAuth) {%> Connect with Google+ - <% } %><% if(filters.twitterAuth) {%> + <% } %><% if (filters.twitterAuth) {%> Connect with Twitter <% } %> diff --git a/app/templates/client/app/account(auth)/login/login(jade).jade b/app/templates/client/app/account(auth)/login/login(jade).jade index 5f608a67b..fd95e6ff1 100644 --- a/app/templates/client/app/account(auth)/login/login(jade).jade +++ b/app/templates/client/app/account(auth)/login/login(jade).jade @@ -34,20 +34,20 @@ div(ng-include='"components/navbar/navbar.html"') button.btn.btn-inverse.btn-lg.btn-login(type='submit') | Login = ' ' - a.btn.btn-default.btn-lg.btn-register(<% if(filters.uirouter) { %>ui-sref='signup'<% } else { %>href='/signup'<% } %>) + a.btn.btn-default.btn-lg.btn-register(<% if (filters.uirouter) { %>ui-sref='signup'<% } else { %>href='/signup'<% } %>) | Register -<% if(filters.oauth) {%> +<% if (filters.oauth) {%> hr - div<% if(filters.facebookAuth) {%> + div<% if (filters.facebookAuth) {%> a.btn.btn-facebook(href='', ng-click='loginOauth("facebook")') i.fa.fa-facebook | Connect with Facebook - = ' '<% } %><% if(filters.googleAuth) {%> + = ' '<% } %><% if (filters.googleAuth) {%> a.btn.btn-google-plus(href='', ng-click='loginOauth("google")') i.fa.fa-google-plus | Connect with Google+ - = ' '<% } %><% if(filters.twitterAuth) {%> + = ' '<% } %><% if (filters.twitterAuth) {%> a.btn.btn-twitter(href='', ng-click='loginOauth("twitter")') i.fa.fa-twitter | Connect with Twitter<% } %><% } %> diff --git a/app/templates/client/app/account(auth)/login/login(less).less b/app/templates/client/app/account(auth)/login/login(less).less index bd01a056e..6eaecd90c 100644 --- a/app/templates/client/app/account(auth)/login/login(less).less +++ b/app/templates/client/app/account(auth)/login/login(less).less @@ -1,4 +1,4 @@ -<% if(filters.bootstrap) { %>// Colors +<% if (filters.bootstrap) { %>// Colors // -------------------------------------------------- @btnText: #fff; diff --git a/app/templates/client/app/account(auth)/login/login(sass).scss b/app/templates/client/app/account(auth)/login/login(sass).scss index eb214a8ca..5b6956124 100644 --- a/app/templates/client/app/account(auth)/login/login(sass).scss +++ b/app/templates/client/app/account(auth)/login/login(sass).scss @@ -1,4 +1,4 @@ -<% if(filters.bootstrap) { %>// Colors +<% if (filters.bootstrap) { %>// Colors // -------------------------------------------------- $btnText: #fff; diff --git a/app/templates/client/app/account(auth)/login/login.controller(coffee).coffee b/app/templates/client/app/account(auth)/login/login.controller(coffee).coffee index 036191f93..7bcb69969 100644 --- a/app/templates/client/app/account(auth)/login/login.controller(coffee).coffee +++ b/app/templates/client/app/account(auth)/login/login.controller(coffee).coffee @@ -1,7 +1,7 @@ 'use strict' angular.module '<%= scriptAppName %>' -.controller 'LoginCtrl', ($scope, Auth<% if(filters.ngroute) { %>, $location<% } %><% if(filters.uirouter) { %>, $state<% } %><% if(filters.oauth) {%>, $window<% } %>) -> +.controller 'LoginCtrl', ($scope, Auth<% if (filters.ngroute) { %>, $location<% } %><% if (filters.uirouter) { %>, $state<% } %><% if (filters.oauth) {%>, $window<% } %>) -> $scope.user = {} $scope.errors = {} $scope.login = (form) -> @@ -14,10 +14,10 @@ angular.module '<%= scriptAppName %>' password: $scope.user.password .then -> - <% if(filters.ngroute) { %>$location.path '/'<% } %><% if(filters.uirouter) { %>$state.go 'main'<% } %> + <% if (filters.ngroute) { %>$location.path '/'<% } %><% if (filters.uirouter) { %>$state.go 'main'<% } %> .catch (err) -> $scope.errors.other = err.message -<% if(filters.oauth) {%> +<% if (filters.oauth) {%> $scope.loginOauth = (provider) -> $window.location.href = '/auth/' + provider<% } %> diff --git a/app/templates/client/app/account(auth)/login/login.controller(js).js b/app/templates/client/app/account(auth)/login/login.controller(js).js index e2c5dcaa4..2417e62f4 100644 --- a/app/templates/client/app/account(auth)/login/login.controller(js).js +++ b/app/templates/client/app/account(auth)/login/login.controller(js).js @@ -1,28 +1,28 @@ 'use strict'; angular.module('<%= scriptAppName %>') - .controller('LoginCtrl', function ($scope, Auth<% if(filters.ngroute) { %>, $location<% } %><% if(filters.uirouter) { %>, $state<% } %><% if (filters.oauth) { %>, $window<% } %>) { + .controller('LoginCtrl', function($scope, Auth<% if (filters.ngroute) { %>, $location<% } %><% if (filters.uirouter) { %>, $state<% } %><% if (filters.oauth) { %>, $window<% } %>) { $scope.user = {}; $scope.errors = {}; $scope.login = function(form) { $scope.submitted = true; - if(form.$valid) { + if (form.$valid) { Auth.login({ email: $scope.user.email, password: $scope.user.password }) - .then( function() { + .then(function() { // Logged in, redirect to home - <% if(filters.ngroute) { %>$location.path('/');<% } %><% if(filters.uirouter) { %>$state.go('main');<% } %> + <% if (filters.ngroute) { %>$location.path('/');<% } %><% if (filters.uirouter) { %>$state.go('main');<% } %> }) - .catch( function(err) { + .catch(function(err) { $scope.errors.other = err.message; }); } }; -<% if(filters.oauth) {%> +<% if (filters.oauth) {%> $scope.loginOauth = function(provider) { $window.location.href = '/auth/' + provider; };<% } %> diff --git a/app/templates/client/app/account(auth)/settings/settings.controller(js).js b/app/templates/client/app/account(auth)/settings/settings.controller(js).js index 829bd8248..eeb1219cf 100644 --- a/app/templates/client/app/account(auth)/settings/settings.controller(js).js +++ b/app/templates/client/app/account(auth)/settings/settings.controller(js).js @@ -1,21 +1,21 @@ 'use strict'; angular.module('<%= scriptAppName %>') - .controller('SettingsCtrl', function ($scope, User, Auth) { + .controller('SettingsCtrl', function($scope, User, Auth) { $scope.errors = {}; $scope.changePassword = function(form) { $scope.submitted = true; - if(form.$valid) { - Auth.changePassword( $scope.user.oldPassword, $scope.user.newPassword ) - .then( function() { - $scope.message = 'Password successfully changed.'; - }) - .catch( function() { - form.password.$setValidity('mongoose', false); - $scope.errors.other = 'Incorrect password'; - $scope.message = ''; - }); + if (form.$valid) { + Auth.changePassword($scope.user.oldPassword, $scope.user.newPassword) + .then(function() { + $scope.message = 'Password successfully changed.'; + }) + .catch(function() { + form.password.$setValidity('mongoose', false); + $scope.errors.other = 'Incorrect password'; + $scope.message = ''; + }); } - }; + }; }); diff --git a/app/templates/client/app/account(auth)/signup/signup(html).html b/app/templates/client/app/account(auth)/signup/signup(html).html index ee4f17fa4..fc55d3790 100644 --- a/app/templates/client/app/account(auth)/signup/signup(html).html +++ b/app/templates/client/app/account(auth)/signup/signup(html).html @@ -58,19 +58,19 @@

Sign up

-
-<% if(filters.oauth) {%> +<% if (filters.oauth) {%>
-
<% if(filters.facebookAuth) {%> +
<% if (filters.facebookAuth) {%> Connect with Facebook - <% } %><% if(filters.googleAuth) {%> + <% } %><% if (filters.googleAuth) {%> Connect with Google+ - <% } %><% if(filters.twitterAuth) {%> + <% } %><% if (filters.twitterAuth) {%> Connect with Twitter <% } %> diff --git a/app/templates/client/app/account(auth)/signup/signup(jade).jade b/app/templates/client/app/account(auth)/signup/signup(jade).jade index 8c5710686..081657a6d 100644 --- a/app/templates/client/app/account(auth)/signup/signup(jade).jade +++ b/app/templates/client/app/account(auth)/signup/signup(jade).jade @@ -36,21 +36,21 @@ div(ng-include='"components/navbar/navbar.html"') button.btn.btn-inverse.btn-lg.btn-register(type='submit') | Sign up = ' ' - a.btn.btn-default.btn-lg.btn-login(<% if(filters.uirouter) { %>ui-sref='login'<% } else { %>href='/login'<% } %>) + a.btn.btn-default.btn-lg.btn-login(<% if (filters.uirouter) { %>ui-sref='login'<% } else { %>href='/login'<% } %>) | Login -<% if(filters.oauth) {%> +<% if (filters.oauth) {%> hr - div<% if(filters.facebookAuth) {%> + div<% if (filters.facebookAuth) {%> a.btn.btn-facebook(href='', ng-click='loginOauth("facebook")') i.fa.fa-facebook | Connect with Facebook - = ' '<% } %><% if(filters.googleAuth) {%> + = ' '<% } %><% if (filters.googleAuth) {%> a.btn.btn-google-plus(href='', ng-click='loginOauth("google")') i.fa.fa-google-plus | Connect with Google+ - = ' '<% } %><% if(filters.twitterAuth) {%> + = ' '<% } %><% if (filters.twitterAuth) {%> a.btn.btn-twitter(href='', ng-click='loginOauth("twitter")') i.fa.fa-twitter | Connect with Twitter<% } %><% } %> diff --git a/app/templates/client/app/account(auth)/signup/signup.controller(coffee).coffee b/app/templates/client/app/account(auth)/signup/signup.controller(coffee).coffee index ac240faa8..3d96f3bf5 100644 --- a/app/templates/client/app/account(auth)/signup/signup.controller(coffee).coffee +++ b/app/templates/client/app/account(auth)/signup/signup.controller(coffee).coffee @@ -1,7 +1,7 @@ 'use strict' angular.module '<%= scriptAppName %>' -.controller 'SignupCtrl', ($scope, Auth<% if(filters.ngroute) { %>, $location<% } %><% if(filters.uirouter) { %>, $state<% } %><% if(filters.oauth) {%>, $window<% } %>) -> +.controller 'SignupCtrl', ($scope, Auth<% if (filters.ngroute) { %>, $location<% } %><% if (filters.uirouter) { %>, $state<% } %><% if (filters.oauth) {%>, $window<% } %>) -> $scope.user = {} $scope.errors = {} $scope.register = (form) -> @@ -15,7 +15,7 @@ angular.module '<%= scriptAppName %>' password: $scope.user.password .then -> - <% if(filters.ngroute) { %>$location.path '/'<% } %><% if(filters.uirouter) { %>$state.go 'main'<% } %> + <% if (filters.ngroute) { %>$location.path '/'<% } %><% if (filters.uirouter) { %>$state.go 'main'<% } %> .catch (err) -> err = err.data @@ -25,6 +25,6 @@ angular.module '<%= scriptAppName %>' angular.forEach err.errors, (error, field) -> form[field].$setValidity 'mongoose', false $scope.errors[field] = error.message -<% if(filters.oauth) {%> +<% if (filters.oauth) {%> $scope.loginOauth = (provider) -> $window.location.href = '/auth/' + provider<% } %> diff --git a/app/templates/client/app/account(auth)/signup/signup.controller(js).js b/app/templates/client/app/account(auth)/signup/signup.controller(js).js index 10685079d..7e93a6949 100644 --- a/app/templates/client/app/account(auth)/signup/signup.controller(js).js +++ b/app/templates/client/app/account(auth)/signup/signup.controller(js).js @@ -1,24 +1,24 @@ 'use strict'; angular.module('<%= scriptAppName %>') - .controller('SignupCtrl', function ($scope, Auth<% if(filters.ngroute) { %>, $location<% } %><% if(filters.uirouter) { %>, $state<% } %><% if (filters.oauth) { %>, $window<% } %>) { + .controller('SignupCtrl', function($scope, Auth<% if (filters.ngroute) { %>, $location<% } %><% if (filters.uirouter) { %>, $state<% } %><% if (filters.oauth) { %>, $window<% } %>) { $scope.user = {}; $scope.errors = {}; $scope.register = function(form) { $scope.submitted = true; - if(form.$valid) { + if (form.$valid) { Auth.createUser({ name: $scope.user.name, email: $scope.user.email, password: $scope.user.password }) - .then( function() { + .then(function() { // Account created, redirect to home - <% if(filters.ngroute) { %>$location.path('/');<% } %><% if(filters.uirouter) { %>$state.go('main');<% } %> + <% if (filters.ngroute) { %>$location.path('/');<% } %><% if (filters.uirouter) { %>$state.go('main');<% } %> }) - .catch( function(err) { + .catch(function(err) { err = err.data; $scope.errors = {}; @@ -30,7 +30,7 @@ angular.module('<%= scriptAppName %>') }); } }; -<% if(filters.oauth) {%> +<% if (filters.oauth) {%> $scope.loginOauth = function(provider) { $window.location.href = '/auth/' + provider; };<% } %> diff --git a/app/templates/client/app/admin(auth)/admin(coffee).coffee b/app/templates/client/app/admin(auth)/admin(coffee).coffee index a0497445e..18bb9d4ad 100644 --- a/app/templates/client/app/admin(auth)/admin(coffee).coffee +++ b/app/templates/client/app/admin(auth)/admin(coffee).coffee @@ -1,12 +1,12 @@ 'use strict' angular.module '<%= scriptAppName %>' -<% if(filters.ngroute) { %>.config ($routeProvider) -> +<% if (filters.ngroute) { %>.config ($routeProvider) -> $routeProvider .when '/admin', templateUrl: 'app/admin/admin.html' controller: 'AdminCtrl' -<% } %><% if(filters.uirouter) { %>.config ($stateProvider) -> +<% } %><% if (filters.uirouter) { %>.config ($stateProvider) -> $stateProvider .state 'admin', url: '/admin' diff --git a/app/templates/client/app/admin(auth)/admin(js).js b/app/templates/client/app/admin(auth)/admin(js).js index 270e8a974..f37ba9fcc 100644 --- a/app/templates/client/app/admin(auth)/admin(js).js +++ b/app/templates/client/app/admin(auth)/admin(js).js @@ -1,17 +1,17 @@ 'use strict'; angular.module('<%= scriptAppName %>') - <% if(filters.ngroute) { %>.config(function ($routeProvider) { + <% if (filters.ngroute) { %>.config(function($routeProvider) { $routeProvider .when('/admin', { templateUrl: 'app/admin/admin.html', controller: 'AdminCtrl' }); - });<% } %><% if(filters.uirouter) { %>.config(function ($stateProvider) { + });<% } %><% if (filters.uirouter) { %>.config(function($stateProvider) { $stateProvider .state('admin', { url: '/admin', templateUrl: 'app/admin/admin.html', controller: 'AdminCtrl' }); - });<% } %> \ No newline at end of file + });<% } %> diff --git a/app/templates/client/app/admin(auth)/admin.controller(js).js b/app/templates/client/app/admin(auth)/admin.controller(js).js index dd6b09405..51a52b759 100644 --- a/app/templates/client/app/admin(auth)/admin.controller(js).js +++ b/app/templates/client/app/admin(auth)/admin.controller(js).js @@ -1,7 +1,7 @@ 'use strict'; angular.module('<%= scriptAppName %>') - .controller('AdminCtrl', function ($scope, $http, Auth, User) { + .controller('AdminCtrl', function($scope, $http, Auth, User) { // Use the User $resource to fetch all users $scope.users = User.query(); diff --git a/app/templates/client/app/app(coffee).coffee b/app/templates/client/app/app(coffee).coffee index cb6d0c5b1..25b572a75 100644 --- a/app/templates/client/app/app(coffee).coffee +++ b/app/templates/client/app/app(coffee).coffee @@ -1,22 +1,22 @@ 'use strict' angular.module '<%= scriptAppName %>', [<%= angularModules %>] -<% if(filters.ngroute) { %>.config ($routeProvider, $locationProvider<% if(filters.auth) { %>, $httpProvider<% } %>) -> +<% if (filters.ngroute) { %>.config ($routeProvider, $locationProvider<% if (filters.auth) { %>, $httpProvider<% } %>) -> $routeProvider .otherwise redirectTo: '/' - $locationProvider.html5Mode true<% if(filters.auth) { %> + $locationProvider.html5Mode true<% if (filters.auth) { %> $httpProvider.interceptors.push 'authInterceptor'<% } %> -<% } %><% if(filters.uirouter) { %>.config ($stateProvider, $urlRouterProvider, $locationProvider<% if(filters.auth) { %>, $httpProvider<% } %>) -> +<% } %><% if (filters.uirouter) { %>.config ($stateProvider, $urlRouterProvider, $locationProvider<% if (filters.auth) { %>, $httpProvider<% } %>) -> $urlRouterProvider .otherwise '/' - $locationProvider.html5Mode true<% if(filters.auth) { %> + $locationProvider.html5Mode true<% if (filters.auth) { %> $httpProvider.interceptors.push 'authInterceptor'<% } %> -<% } %><% if(filters.auth) { %> -.factory 'authInterceptor', ($rootScope, $q, $cookieStore<% if(filters.ngroute) { %>, $location<% } if(filters.uirouter) { %>, $injector<% } %>) -> - <% if(filters.uirouter) { %>state = null +<% } %><% if (filters.auth) { %> +.factory 'authInterceptor', ($rootScope, $q, $cookieStore<% if (filters.ngroute) { %>, $location<% } if (filters.uirouter) { %>, $injector<% } %>) -> + <% if (filters.uirouter) { %>state = null <% } %># Add authorization token to headers request: (config) -> config.headers = config.headers or {} @@ -26,15 +26,15 @@ angular.module '<%= scriptAppName %>', [<%= angularModules %>] # Intercept 401s and redirect you to login responseError: (response) -> if response.status is 401 - <% if(filters.ngroute) { %>$location.path '/login'<% } if(filters.uirouter) { %>(state || state = $injector.get '$state').go 'login'<% } %> + <% if (filters.ngroute) { %>$location.path '/login'<% } if (filters.uirouter) { %>(state || state = $injector.get '$state').go 'login'<% } %> # remove any stale tokens $cookieStore.remove 'token' $q.reject response -.run ($rootScope<% if(filters.ngroute) { %>, $location<% } %><% if(filters.uirouter) { %>, $state<% } %>, Auth) -> +.run ($rootScope<% if (filters.ngroute) { %>, $location<% } %><% if (filters.uirouter) { %>, $state<% } %>, Auth) -> # Redirect to login if route requires auth and you're not logged in - $rootScope.$on <% if(filters.ngroute) { %>'$routeChangeStart'<% } %><% if(filters.uirouter) { %>'$stateChangeStart'<% } %>, (event, next) -> + $rootScope.$on <% if (filters.ngroute) { %>'$routeChangeStart'<% } %><% if (filters.uirouter) { %>'$stateChangeStart'<% } %>, (event, next) -> Auth.isLoggedIn (loggedIn) -> - <% if(filters.ngroute) { %>$location.path '/login'<% } %><% if(filters.uirouter) { %>$state.go 'login'<% } %> if next.authenticate and not loggedIn + <% if (filters.ngroute) { %>$location.path '/login'<% } %><% if (filters.uirouter) { %>$state.go 'login'<% } %> if next.authenticate and not loggedIn <% } %> diff --git a/app/templates/client/app/app(css).css b/app/templates/client/app/app(css).css index f1a61a918..b4e05273d 100644 --- a/app/templates/client/app/app(css).css +++ b/app/templates/client/app/app(css).css @@ -1,4 +1,4 @@ -<% if(filters.bootstrap) { %> +<% if (filters.bootstrap) { %> /** * Bootstrap Fonts */ @@ -37,7 +37,7 @@ color: #000; padding: 0.2em 0; } -<% if (!filters.bootstrap) { %> +<% if(!filters.bootstrap) { %> /* Responsive: Portrait tablets and up */ @media screen and (min-width: 768px) { .container { diff --git a/app/templates/client/app/app(js).js b/app/templates/client/app/app(js).js index 35b8ad327..b45db3c7f 100644 --- a/app/templates/client/app/app(js).js +++ b/app/templates/client/app/app(js).js @@ -1,27 +1,27 @@ 'use strict'; angular.module('<%= scriptAppName %>', [<%= angularModules %>]) - <% if(filters.ngroute) { %>.config(function ($routeProvider, $locationProvider<% if(filters.auth) { %>, $httpProvider<% } %>) { + <% if (filters.ngroute) { %>.config(function($routeProvider, $locationProvider<% if (filters.auth) { %>, $httpProvider<% } %>) { $routeProvider .otherwise({ redirectTo: '/' }); - $locationProvider.html5Mode(true);<% if(filters.auth) { %> + $locationProvider.html5Mode(true);<% if (filters.auth) { %> $httpProvider.interceptors.push('authInterceptor');<% } %> - })<% } if(filters.uirouter) { %>.config(function ($stateProvider, $urlRouterProvider, $locationProvider<% if(filters.auth) { %>, $httpProvider<% } %>) { + })<% } if (filters.uirouter) { %>.config(function($stateProvider, $urlRouterProvider, $locationProvider<% if (filters.auth) { %>, $httpProvider<% } %>) { $urlRouterProvider .otherwise('/'); - $locationProvider.html5Mode(true);<% if(filters.auth) { %> + $locationProvider.html5Mode(true);<% if (filters.auth) { %> $httpProvider.interceptors.push('authInterceptor');<% } %> - })<% } if(filters.auth) { %> + })<% } if (filters.auth) { %> - .factory('authInterceptor', function ($rootScope, $q, $cookieStore<% if(filters.ngroute) { %>, $location<% } if(filters.uirouter) { %>, $injector<% } %>) { - <% if(filters.uirouter) { %>var state; + .factory('authInterceptor', function($rootScope, $q, $cookieStore<% if (filters.ngroute) { %>, $location<% } if (filters.uirouter) { %>, $injector<% } %>) { + <% if (filters.uirouter) { %>var state; <% } %>return { // Add authorization token to headers - request: function (config) { + request: function(config) { config.headers = config.headers || {}; if ($cookieStore.get('token')) { config.headers.Authorization = 'Bearer ' + $cookieStore.get('token'); @@ -31,8 +31,8 @@ angular.module('<%= scriptAppName %>', [<%= angularModules %>]) // Intercept 401s and redirect you to login responseError: function(response) { - if(response.status === 401) { - <% if(filters.ngroute) { %>$location.path('/login');<% } if(filters.uirouter) { %>(state || (state = $injector.get('$state'))).go('login');<% } %> + if (response.status === 401) { + <% if (filters.ngroute) { %>$location.path('/login');<% } if (filters.uirouter) { %>(state || (state = $injector.get('$state'))).go('login');<% } %> // remove any stale tokens $cookieStore.remove('token'); return $q.reject(response); @@ -44,12 +44,12 @@ angular.module('<%= scriptAppName %>', [<%= angularModules %>]) }; }) - .run(function ($rootScope<% if(filters.ngroute) { %>, $location<% } if(filters.uirouter) { %>, $state<% } %>, Auth) { + .run(function($rootScope<% if (filters.ngroute) { %>, $location<% } if (filters.uirouter) { %>, $state<% } %>, Auth) { // Redirect to login if route requires auth and you're not logged in - $rootScope.$on(<% if(filters.ngroute) { %>'$routeChangeStart'<% } %><% if(filters.uirouter) { %>'$stateChangeStart'<% } %>, function (event, next) { + $rootScope.$on(<% if (filters.ngroute) { %>'$routeChangeStart'<% } %><% if (filters.uirouter) { %>'$stateChangeStart'<% } %>, function(event, next) { Auth.isLoggedIn(function(loggedIn) { if (next.authenticate && !loggedIn) { - <% if(filters.ngroute) { %>$location.path('/login');<% } if(filters.uirouter) { %>$state.go('login');<% } %> + <% if (filters.ngroute) { %>$location.path('/login');<% } if (filters.uirouter) { %>$state.go('login');<% } %> } }); }); diff --git a/app/templates/client/app/app(less).less b/app/templates/client/app/app(less).less index 30639f539..71381ceb1 100644 --- a/app/templates/client/app/app(less).less +++ b/app/templates/client/app/app(less).less @@ -1,7 +1,7 @@ -<% if(filters.bootstrap) { %>@import 'bootstrap/less/bootstrap.less';<% } %> +<% if (filters.bootstrap) { %>@import 'bootstrap/less/bootstrap.less';<% } %> @import 'font-awesome/less/font-awesome.less'; -<% if(filters.bootstrap) { %>@icon-font-path: '/bower_components/bootstrap/fonts/';<% } %> +<% if (filters.bootstrap) { %>@icon-font-path: '/bower_components/bootstrap/fonts/';<% } %> @fa-font-path: '/bower_components/font-awesome/fonts'; /** @@ -14,7 +14,7 @@ color: #000; padding: 0.2em 0; } -<% if (!filters.bootstrap) { %> +<% if(!filters.bootstrap) { %> /* Responsive: Portrait tablets and up */ @media screen and (min-width: 768px) { .container { diff --git a/app/templates/client/app/app(sass).scss b/app/templates/client/app/app(sass).scss index 4b8ae7a04..fe03d7044 100644 --- a/app/templates/client/app/app(sass).scss +++ b/app/templates/client/app/app(sass).scss @@ -1,6 +1,6 @@ -<% if(filters.bootstrap) { %>$icon-font-path: "/bower_components/bootstrap-sass-official/vendor/assets/fonts/bootstrap/";<% } %> +<% if (filters.bootstrap) { %>$icon-font-path: "/bower_components/bootstrap-sass-official/vendor/assets/fonts/bootstrap/";<% } %> $fa-font-path: "/bower_components/font-awesome/fonts"; -<% if(filters.bootstrap) { %> +<% if (filters.bootstrap) { %> @import 'bootstrap-sass-official/vendor/assets/stylesheets/bootstrap';<% } %> @import 'font-awesome/scss/font-awesome'; @@ -14,7 +14,7 @@ $fa-font-path: "/bower_components/font-awesome/fonts"; color: #000; padding: 0.2em 0; } -<% if (!filters.bootstrap) { %> +<% if(!filters.bootstrap) { %> /* Responsive: Portrait tablets and up */ @media screen and (min-width: 768px) { .container { diff --git a/app/templates/client/app/app(stylus).styl b/app/templates/client/app/app(stylus).styl index b7e4bb9c1..2d8afe893 100644 --- a/app/templates/client/app/app(stylus).styl +++ b/app/templates/client/app/app(stylus).styl @@ -1,5 +1,5 @@ @import "font-awesome/css/font-awesome.css" -<% if(filters.bootstrap) { %>@import "bootstrap/dist/css/bootstrap.css" +<% if (filters.bootstrap) { %>@import "bootstrap/dist/css/bootstrap.css" // // Bootstrap Fonts @@ -36,7 +36,7 @@ color #000 margin 0.2em 0 padding 0.2em 0 -<% if (!filters.bootstrap) { %> +<% if(!filters.bootstrap) { %> // Responsive: Portrait tablets and up @media screen and (min-width: 768px) .container diff --git a/app/templates/client/app/main/main(coffee).coffee b/app/templates/client/app/main/main(coffee).coffee index 6d84bdc1e..5d28335d1 100644 --- a/app/templates/client/app/main/main(coffee).coffee +++ b/app/templates/client/app/main/main(coffee).coffee @@ -1,12 +1,12 @@ 'use strict' angular.module '<%= scriptAppName %>' -<% if(filters.ngroute) { %>.config ($routeProvider) -> +<% if (filters.ngroute) { %>.config ($routeProvider) -> $routeProvider .when '/', templateUrl: 'app/main/main.html' controller: 'MainCtrl' -<% } %><% if(filters.uirouter) { %>.config ($stateProvider) -> +<% } %><% if (filters.uirouter) { %>.config ($stateProvider) -> $stateProvider .state 'main', url: '/' diff --git a/app/templates/client/app/main/main(html).html b/app/templates/client/app/main/main(html).html index cd0f185b2..7d0d5c44b 100644 --- a/app/templates/client/app/main/main(html).html +++ b/app/templates/client/app/main/main(html).html @@ -13,10 +13,10 @@

'Allo, 'Allo!

-
<% if(filters.socketio) { %> +
<% if (filters.socketio) { %>
diff --git a/app/templates/client/app/main/main(jade).jade b/app/templates/client/app/main/main(jade).jade index 76784c855..ee66a5fc6 100644 --- a/app/templates/client/app/main/main(jade).jade +++ b/app/templates/client/app/main/main(jade).jade @@ -13,8 +13,8 @@ header#banner.hero-unit ul.nav.nav-tabs.nav-stacked.col-md-4.col-lg-4.col-sm-6(ng-repeat='thing in awesomeThings') li a(href='#', tooltip='{{thing.info}}') - | {{thing.name}}<% if(filters.socketio) { %> - button.close(type='button', ng-click='deleteThing(thing)') ×<% } %><% if(filters.socketio) { %> + | {{thing.name}}<% if (filters.socketio) { %> + button.close(type='button', ng-click='deleteThing(thing)') ×<% } %><% if (filters.socketio) { %> form.thing-form label Syncs in realtime across clients diff --git a/app/templates/client/app/main/main(js).js b/app/templates/client/app/main/main(js).js index 1d3bc318a..165181ffe 100644 --- a/app/templates/client/app/main/main(js).js +++ b/app/templates/client/app/main/main(js).js @@ -1,17 +1,17 @@ 'use strict'; angular.module('<%= scriptAppName %>') - <% if(filters.ngroute) { %>.config(function ($routeProvider) { + <% if (filters.ngroute) { %>.config(function($routeProvider) { $routeProvider .when('/', { templateUrl: 'app/main/main.html', controller: 'MainCtrl' }); - });<% } %><% if(filters.uirouter) { %>.config(function ($stateProvider) { + });<% } %><% if (filters.uirouter) { %>.config(function($stateProvider) { $stateProvider .state('main', { url: '/', templateUrl: 'app/main/main.html', controller: 'MainCtrl' }); - });<% } %> \ No newline at end of file + });<% } %> diff --git a/app/templates/client/app/main/main.controller(coffee).coffee b/app/templates/client/app/main/main.controller(coffee).coffee index 143e7f387..3dffae2f7 100644 --- a/app/templates/client/app/main/main.controller(coffee).coffee +++ b/app/templates/client/app/main/main.controller(coffee).coffee @@ -1,13 +1,13 @@ 'use strict' angular.module '<%= scriptAppName %>' -.controller 'MainCtrl', ($scope, $http<% if(filters.socketio) { %>, socket<% } %>) -> +.controller 'MainCtrl', ($scope, $http<% if (filters.socketio) { %>, socket<% } %>) -> $scope.awesomeThings = [] $http.get('/api/things').success (awesomeThings) -> $scope.awesomeThings = awesomeThings - <% if(filters.socketio) { %>socket.syncUpdates 'thing', $scope.awesomeThings<% } %> -<% if(filters.mongoose) { %> + <% if (filters.socketio) { %>socket.syncUpdates 'thing', $scope.awesomeThings<% } %> +<% if (filters.mongoose) { %> $scope.addThing = -> return if $scope.newThing is '' $http.post '/api/things', @@ -16,7 +16,7 @@ angular.module '<%= scriptAppName %>' $scope.newThing = '' $scope.deleteThing = (thing) -> - $http.delete '/api/things/' + thing._id<% } %><% if(filters.socketio) { %> + $http.delete '/api/things/' + thing._id<% } %><% if (filters.socketio) { %> $scope.$on '$destroy', -> socket.unsyncUpdates 'thing'<% } %> diff --git a/app/templates/client/app/main/main.controller(js).js b/app/templates/client/app/main/main.controller(js).js index 433a10fe4..8164a90dc 100644 --- a/app/templates/client/app/main/main.controller(js).js +++ b/app/templates/client/app/main/main.controller(js).js @@ -1,16 +1,16 @@ 'use strict'; angular.module('<%= scriptAppName %>') - .controller('MainCtrl', function ($scope, $http<% if(filters.socketio) { %>, socket<% } %>) { + .controller('MainCtrl', function($scope, $http<% if (filters.socketio) { %>, socket<% } %>) { $scope.awesomeThings = []; $http.get('/api/things').success(function(awesomeThings) { - $scope.awesomeThings = awesomeThings;<% if(filters.socketio) { %> + $scope.awesomeThings = awesomeThings;<% if (filters.socketio) { %> socket.syncUpdates('thing', $scope.awesomeThings);<% } %> }); -<% if(filters.mongoose) { %> +<% if (filters.mongoose) { %> $scope.addThing = function() { - if($scope.newThing === '') { + if ($scope.newThing === '') { return; } $http.post('/api/things', { name: $scope.newThing }); @@ -19,9 +19,9 @@ angular.module('<%= scriptAppName %>') $scope.deleteThing = function(thing) { $http.delete('/api/things/' + thing._id); - };<% } %><% if(filters.socketio) { %> + };<% } %><% if (filters.socketio) { %> - $scope.$on('$destroy', function () { + $scope.$on('$destroy', function() { socket.unsyncUpdates('thing'); });<% } %> }); diff --git a/app/templates/client/app/main/main.controller.spec(coffee).coffee b/app/templates/client/app/main/main.controller.spec(coffee).coffee index 5bdf4f840..a72ae8695 100644 --- a/app/templates/client/app/main/main.controller.spec(coffee).coffee +++ b/app/templates/client/app/main/main.controller.spec(coffee).coffee @@ -3,17 +3,17 @@ describe 'Controller: MainCtrl', -> # load the controller's module - beforeEach module '<%= scriptAppName %>' <% if(filters.uirouter) {%> - beforeEach module 'stateMock' <% } %><% if(filters.socketio) {%> + beforeEach module '<%= scriptAppName %>' <% if (filters.uirouter) {%> + beforeEach module 'stateMock' <% } %><% if (filters.socketio) {%> beforeEach module 'socketMock' <% } %> MainCtrl = undefined - scope = undefined<% if(filters.uirouter) {%> + scope = undefined<% if (filters.uirouter) {%> state = undefined<% } %> $httpBackend = undefined # Initialize the controller and a mock scope - beforeEach inject (_$httpBackend_, $controller, $rootScope<% if(filters.uirouter) {%>, $state<% } %>) -> + beforeEach inject (_$httpBackend_, $controller, $rootScope<% if (filters.uirouter) {%>, $state<% } %>) -> $httpBackend = _$httpBackend_ $httpBackend.expectGET('/api/things').respond [ 'HTML5 Boilerplate' @@ -21,7 +21,7 @@ describe 'Controller: MainCtrl', -> 'Karma' 'Express' ] - scope = $rootScope.$new()<% if(filters.uirouter) {%> + scope = $rootScope.$new()<% if (filters.uirouter) {%> state = $state<% } %> MainCtrl = $controller 'MainCtrl', $scope: scope diff --git a/app/templates/client/app/main/main.controller.spec(js).js b/app/templates/client/app/main/main.controller.spec(js).js index dc048b4af..71fd2a783 100644 --- a/app/templates/client/app/main/main.controller.spec(js).js +++ b/app/templates/client/app/main/main.controller.spec(js).js @@ -1,31 +1,31 @@ 'use strict'; -describe('Controller: MainCtrl', function () { +describe('Controller: MainCtrl', function() { // load the controller's module - beforeEach(module('<%= scriptAppName %>'));<% if(filters.uirouter) {%> - beforeEach(module('stateMock'));<% } %><% if(filters.socketio) {%> + beforeEach(module('<%= scriptAppName %>'));<% if (filters.uirouter) {%> + beforeEach(module('stateMock'));<% } %><% if (filters.socketio) {%> beforeEach(module('socketMock'));<% } %> - var MainCtrl, - scope,<% if(filters.uirouter) {%> - state,<% } %> - $httpBackend; + var MainCtrl; + var scope;<% if (filters.uirouter) {%> + var state;<% } %> + var $httpBackend; // Initialize the controller and a mock scope - beforeEach(inject(function (_$httpBackend_, $controller, $rootScope<% if(filters.uirouter) {%>, $state<% } %>) { + beforeEach(inject(function(_$httpBackend_, $controller, $rootScope<% if (filters.uirouter) {%>, $state<% } %>) { $httpBackend = _$httpBackend_; $httpBackend.expectGET('/api/things') .respond(['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']); - scope = $rootScope.$new();<% if(filters.uirouter) {%> + scope = $rootScope.$new();<% if (filters.uirouter) {%> state = $state;<% } %> MainCtrl = $controller('MainCtrl', { $scope: scope }); })); - it('should attach a list of things to the scope', function () { + it('should attach a list of things to the scope', function() { $httpBackend.flush();<% if (filters.jasmine) { %> expect(scope.awesomeThings.length).toBe(4);<% } if (filters.mocha) { %> <%= does("scope.awesomeThings.length") %>.equal(4);<% } %> diff --git a/app/templates/client/components/navbar/navbar(html).html b/app/templates/client/components/navbar/navbar(html).html index a93839562..ec9e4682d 100644 --- a/app/templates/client/components/navbar/navbar(html).html +++ b/app/templates/client/components/navbar/navbar(html).html @@ -11,18 +11,18 @@
diff --git a/app/templates/client/components/navbar/navbar(jade).jade b/app/templates/client/components/navbar/navbar(jade).jade index 7863f4e0a..5d7cb2795 100644 --- a/app/templates/client/components/navbar/navbar(jade).jade +++ b/app/templates/client/components/navbar/navbar(jade).jade @@ -10,25 +10,25 @@ div.navbar.navbar-default.navbar-static-top(ng-controller='NavbarCtrl') div#navbar-main.navbar-collapse.collapse(collapse='isCollapsed') ul.nav.navbar-nav - li(ng-repeat='item in menu', <% if(filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive(item.link)}'<% } %>) - a(<% if(filters.uirouter) { %>ui-sref='{{item.state}}'<% } else { %>ng-href='{{item.link}}'<% } %>) {{item.title}}<% if(filters.auth) { %> + li(ng-repeat='item in menu', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive(item.link)}'<% } %>) + a(<% if (filters.uirouter) { %>ui-sref='{{item.state}}'<% } else { %>ng-href='{{item.link}}'<% } %>) {{item.title}}<% if (filters.auth) { %> - li(ng-show='isAdmin()', <% if(filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive("/admin")}'<% } %>) - a(<% if(filters.uirouter) { %>ui-sref='admin'<% } else { %>href='/admin'<% } %>) Admin + li(ng-show='isAdmin()', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive("/admin")}'<% } %>) + a(<% if (filters.uirouter) { %>ui-sref='admin'<% } else { %>href='/admin'<% } %>) Admin ul.nav.navbar-nav.navbar-right - li(ng-hide='isLoggedIn()', <% if(filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive("/signup")}'<% } %>) - a(<% if(filters.uirouter) { %>ui-sref='signup'<% } else { %>href='/signup'<% } %>) Sign up + li(ng-hide='isLoggedIn()', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive("/signup")}'<% } %>) + a(<% if (filters.uirouter) { %>ui-sref='signup'<% } else { %>href='/signup'<% } %>) Sign up - li(ng-hide='isLoggedIn()', <% if(filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive("/login")}'<% } %>) - a(<% if(filters.uirouter) { %>ui-sref='login'<% } else { %>href='/login'<% } %>) Login + li(ng-hide='isLoggedIn()', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive("/login")}'<% } %>) + a(<% if (filters.uirouter) { %>ui-sref='login'<% } else { %>href='/login'<% } %>) Login li(ng-show='isLoggedIn()') p.navbar-text Hello {{ getCurrentUser().name }} - li(ng-show='isLoggedIn()', <% if(filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive("/settings")}'<% } %>) - a(<% if(filters.uirouter) { %>ui-sref='settings'<% } else { %>href='/settings'<% } %>) + li(ng-show='isLoggedIn()', <% if (filters.uirouter) { %>ui-sref-active='active'<% } else { %>ng-class='{active: isActive("/settings")}'<% } %>) + a(<% if (filters.uirouter) { %>ui-sref='settings'<% } else { %>href='/settings'<% } %>) span.glyphicon.glyphicon-cog li(ng-show='isLoggedIn()') - a(<% if(filters.uirouter) { %>ui-sref='logout'<% } else { %>href='/logout'<% } %>) Logout<% } %> \ No newline at end of file + a(<% if (filters.uirouter) { %>ui-sref='logout'<% } else { %>href='/logout'<% } %>) Logout<% } %> \ No newline at end of file diff --git a/app/templates/client/components/navbar/navbar.controller(coffee).coffee b/app/templates/client/components/navbar/navbar.controller(coffee).coffee index 121437cf1..9dda1ae4b 100644 --- a/app/templates/client/components/navbar/navbar.controller(coffee).coffee +++ b/app/templates/client/components/navbar/navbar.controller(coffee).coffee @@ -1,12 +1,12 @@ 'use strict' angular.module '<%= scriptAppName %>' -.controller 'NavbarCtrl', ($scope<% if(!filters.uirouter) { %>, $location<% } %><% if(filters.auth) {%>, Auth<% } %>) -> +.controller 'NavbarCtrl', ($scope<% if(!filters.uirouter) { %>, $location<% } %><% if (filters.auth) {%>, Auth<% } %>) -> $scope.menu = [ title: 'Home' - <% if(filters.uirouter) { %>state: 'main'<% } else { %>link: '/'<% } %> + <% if (filters.uirouter) { %>state: 'main'<% } else { %>link: '/'<% } %> ] - $scope.isCollapsed = true<% if(filters.auth) {%> + $scope.isCollapsed = true<% if (filters.auth) {%> $scope.isLoggedIn = Auth.isLoggedIn $scope.isAdmin = Auth.isAdmin $scope.getCurrentUser = Auth.getCurrentUser<% } %><% if(!filters.uirouter) { %> diff --git a/app/templates/client/components/navbar/navbar.controller(js).js b/app/templates/client/components/navbar/navbar.controller(js).js index 2428ac15b..b3eef7cf6 100644 --- a/app/templates/client/components/navbar/navbar.controller(js).js +++ b/app/templates/client/components/navbar/navbar.controller(js).js @@ -1,13 +1,13 @@ 'use strict'; angular.module('<%= scriptAppName %>') - .controller('NavbarCtrl', function ($scope<% if(!filters.uirouter) { %>, $location<% } %><% if(filters.auth) {%>, Auth<% } %>) { + .controller('NavbarCtrl', function ($scope<% if(!filters.uirouter) { %>, $location<% } %><% if (filters.auth) {%>, Auth<% } %>) { $scope.menu = [{ 'title': 'Home', - <% if(filters.uirouter) { %>'state': 'main'<% } else { %>'link': '/'<% } %> + <% if (filters.uirouter) { %>'state': 'main'<% } else { %>'link': '/'<% } %> }]; - $scope.isCollapsed = true;<% if(filters.auth) {%> + $scope.isCollapsed = true;<% if (filters.auth) {%> $scope.isLoggedIn = Auth.isLoggedIn; $scope.isAdmin = Auth.isAdmin; $scope.getCurrentUser = Auth.getCurrentUser;<% } %><% if(!filters.uirouter) { %> diff --git a/app/templates/client/index.html b/app/templates/client/index.html index e823e372d..479a3f2a3 100644 --- a/app/templates/client/index.html +++ b/app/templates/client/index.html @@ -27,7 +27,7 @@ - <% if(filters.ngroute) { %>
<% } %><% if(filters.uirouter) { %>
<% } %> + <% if (filters.ngroute) { %>
<% } %><% if (filters.uirouter) { %>
<% } %> <% } %> diff --git a/app/templates/e2e/components/navbar/navbar.po.js b/app/templates/e2e/components/navbar/navbar.po.js index ee2ec1ada..62f407210 100644 --- a/app/templates/e2e/components/navbar/navbar.po.js +++ b/app/templates/e2e/components/navbar/navbar.po.js @@ -8,7 +8,7 @@ var NavbarComponent = function() { this.navbar = element(by.css('.navbar')); this.navbarHeader = this.navbar.element(by.css('.navbar-header')); - this.navbarNav = this.navbar.element(by.css('#navbar-main .nav.navbar-nav:not(.navbar-right)'));<% if(filters.auth) { %> + this.navbarNav = this.navbar.element(by.css('#navbar-main .nav.navbar-nav:not(.navbar-right)'));<% if (filters.auth) { %> this.navbarAccount = this.navbar.element(by.css('#navbar-main .nav.navbar-nav.navbar-right')); this.navbarAccountGreeting = this.navbarAccount.element(by.binding('Hello {{ getCurrentUser().name }}'));<% } %> }; diff --git a/app/templates/karma.conf.js b/app/templates/karma.conf.js index 6b6c6c477..ed4cd2955 100644 --- a/app/templates/karma.conf.js +++ b/app/templates/karma.conf.js @@ -6,8 +6,8 @@ module.exports = function(config) { // base path, that will be used to resolve files and exclude basePath: '', - // testing framework to use (jasmine/mocha/qunit/...)<% if(filters.jasmine) { %> - frameworks: ['jasmine'],<% } if(filters.mocha) { %> + // testing framework to use (jasmine/mocha/qunit/...)<% if (filters.jasmine) { %> + frameworks: ['jasmine'],<% } if (filters.mocha) { %> frameworks: ['mocha', 'chai', 'sinon-chai', 'chai-as-promised', 'chai-things'],<% } %> // list of files / patterns to load in the browser @@ -18,10 +18,10 @@ module.exports = function(config) { 'client/bower_components/angular-resource/angular-resource.js', 'client/bower_components/angular-cookies/angular-cookies.js', 'client/bower_components/angular-sanitize/angular-sanitize.js', - 'client/bower_components/angular-route/angular-route.js',<% if(filters.uibootstrap) { %> + 'client/bower_components/angular-route/angular-route.js',<% if (filters.uibootstrap) { %> 'client/bower_components/angular-bootstrap/ui-bootstrap-tpls.js',<% } %> - 'client/bower_components/lodash/dist/lodash.compat.js',<% if(filters.socketio) { %> - 'client/bower_components/angular-socket-io/socket.js',<% } %><% if(filters.uirouter) { %> + 'client/bower_components/lodash/dist/lodash.compat.js',<% if (filters.socketio) { %> + 'client/bower_components/angular-socket-io/socket.js',<% } %><% if (filters.uirouter) { %> 'client/bower_components/angular-ui-router/release/angular-ui-router.js',<% } %> 'client/app/app.js', 'client/app/app.coffee', diff --git a/app/templates/protractor.conf.js b/app/templates/protractor.conf.js index 14f704b3d..f0061ff86 100644 --- a/app/templates/protractor.conf.js +++ b/app/templates/protractor.conf.js @@ -43,14 +43,14 @@ var config = { // Jasmine and Cucumber are fully supported as a test and assertion framework. // Mocha has limited beta support. You will need to include your own // assertion framework if working with mocha. - framework: '<% if(filters.jasmine) { %>jasmine<% } if(filters.mocha) { %>mocha<% } %>', -<% if(filters.jasmine) { %> + framework: '<% if (filters.jasmine) { %>jasmine<% } if (filters.mocha) { %>mocha<% } %>', +<% if (filters.jasmine) { %> // ----- Options to be passed to minijasminenode ----- // // See the full list at https://github.com/juliemr/minijasminenode jasmineNodeOpts: { defaultTimeoutInterval: 30000 - },<% } if(filters.mocha) { %> + },<% } if (filters.mocha) { %> // ----- Options to be passed to mocha ----- mochaOpts: { reporter: 'spec', @@ -63,7 +63,7 @@ var config = { serverConfig: require('./server/config/environment') }, - onPrepare: function() {<% if(filters.mocha) { %> + onPrepare: function() {<% if (filters.mocha) { %> // Load Mocha and Chai + plugins require('./mocha.conf'); diff --git a/app/templates/server/api/thing/index.js b/app/templates/server/api/thing/index.js index e77e80c5b..ea697d38b 100644 --- a/app/templates/server/api/thing/index.js +++ b/app/templates/server/api/thing/index.js @@ -5,7 +5,7 @@ var controller = require('./thing.controller'); var router = express.Router(); -router.get('/', controller.index);<% if(filters.mongoose) { %> +router.get('/', controller.index);<% if (filters.mongoose) { %> router.get('/:id', controller.show); router.post('/', controller.create); router.put('/:id', controller.update); diff --git a/app/templates/server/api/thing/index.spec.js b/app/templates/server/api/thing/index.spec.js index 3c6c71fc2..899b22d79 100644 --- a/app/templates/server/api/thing/index.spec.js +++ b/app/templates/server/api/thing/index.spec.js @@ -3,7 +3,7 @@ var proxyquire = require('proxyquire').noPreserveCache(); var thingCtrlStub = { - index: 'thingCtrl.index'<% if(filters.mongoose) { %>, + index: 'thingCtrl.index'<% if (filters.mongoose) { %>, show: 'thingCtrl.show', create: 'thingCtrl.create', update: 'thingCtrl.update', @@ -11,7 +11,7 @@ var thingCtrlStub = { }; var routerStub = { - get: sinon.spy()<% if(filters.mongoose) { %>, + get: sinon.spy()<% if (filters.mongoose) { %>, put: sinon.spy(), patch: sinon.spy(), post: sinon.spy(), @@ -37,15 +37,19 @@ describe('Thing API Router:', function() { describe('GET /api/things', function() { it('should route to thing.controller.index', function() { - routerStub.get.withArgs('/', 'thingCtrl.index').should.have.been.calledOnce; + routerStub.get + .withArgs('/', 'thingCtrl.index') + .should.have.been.calledOnce; }); - });<% if(filters.mongoose) { %> + });<% if (filters.mongoose) { %> describe('GET /api/things/:id', function() { it('should route to thing.controller.show', function() { - routerStub.get.withArgs('/:id', 'thingCtrl.show').should.have.been.calledOnce; + routerStub.get + .withArgs('/:id', 'thingCtrl.show') + .should.have.been.calledOnce; }); }); @@ -53,7 +57,9 @@ describe('Thing API Router:', function() { describe('POST /api/things', function() { it('should route to thing.controller.create', function() { - routerStub.post.withArgs('/', 'thingCtrl.create').should.have.been.calledOnce; + routerStub.post + .withArgs('/', 'thingCtrl.create') + .should.have.been.calledOnce; }); }); @@ -61,7 +67,9 @@ describe('Thing API Router:', function() { describe('PUT /api/things/:id', function() { it('should route to thing.controller.update', function() { - routerStub.put.withArgs('/:id', 'thingCtrl.update').should.have.been.calledOnce; + routerStub.put + .withArgs('/:id', 'thingCtrl.update') + .should.have.been.calledOnce; }); }); @@ -69,7 +77,9 @@ describe('Thing API Router:', function() { describe('PATCH /api/things/:id', function() { it('should route to thing.controller.update', function() { - routerStub.patch.withArgs('/:id', 'thingCtrl.update').should.have.been.calledOnce; + routerStub.patch + .withArgs('/:id', 'thingCtrl.update') + .should.have.been.calledOnce; }); }); @@ -77,7 +87,9 @@ describe('Thing API Router:', function() { describe('DELETE /api/things/:id', function() { it('should route to thing.controller.destroy', function() { - routerStub.delete.withArgs('/:id', 'thingCtrl.destroy').should.have.been.calledOnce; + routerStub.delete + .withArgs('/:id', 'thingCtrl.destroy') + .should.have.been.calledOnce; }); });<% } %> diff --git a/app/templates/server/api/thing/thing.controller.js b/app/templates/server/api/thing/thing.controller.js index 23bfd6937..710c7cd38 100644 --- a/app/templates/server/api/thing/thing.controller.js +++ b/app/templates/server/api/thing/thing.controller.js @@ -12,25 +12,25 @@ var _ = require('lodash'); var Thing = require('./thing.model'); -function handleError(res, statusCode){ +function handleError(res, statusCode) { statusCode = statusCode || 500; - return function(err){ + return function(err) { res.send(statusCode, err); }; } -function responseWithResult(res, statusCode){ +function responseWithResult(res, statusCode) { statusCode = statusCode || 200; - return function(entity){ - if(entity){ + return function(entity) { + if (entity) { return res.json(statusCode, entity); } }; } -function handleEntityNotFound(res){ - return function(entity){ - if(!entity){ +function handleEntityNotFound(res) { + return function(entity) { + if (!entity) { res.send(404); return null; } @@ -38,19 +38,19 @@ function handleEntityNotFound(res){ }; } -function saveUpdates(updates){ - return function(entity){ +function saveUpdates(updates) { + return function(entity) { var updated = _.merge(entity, updates); return updated.saveAsync() - .spread(function (updated) { + .spread(function(updated) { return updated; }); }; } -function removeEntity(res){ - return function (entity) { - if(entity){ +function removeEntity(res) { + return function(entity) { + if (entity) { return entity.removeAsync() .then(function() { return res.send(204); @@ -60,28 +60,31 @@ function removeEntity(res){ }<% } %> // Get list of things -exports.index = function(req, res) {<% if (!filters.mongoose) { %> - res.json([ - { - name : 'Development Tools', - info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' +exports.index = function(req, res) {<% if(!filters.mongoose) { %> + res.json([{ + name : 'Development Tools', + info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, ' + + 'Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' }, { - name : 'Server and Client integration', - info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' + name : 'Server and Client integration', + info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' }, { - name : 'Smart Build System', - info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of scripts and styles into your index.html' - }, { - name : 'Modular Structure', - info : 'Best practice client and server structures allow for more code reusability and maximum scalability' - }, { - name : 'Optimized Build', - info : 'Build process packs up your templates as a single JavaScript payload, minifies your scripts/css/images, and rewrites asset names for caching.' - },{ - name : 'Deployment Ready', - info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' - } - ]);<% } if (filters.mongoose) { %> + name : 'Smart Build System', + info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. ' + + 'Automatic injection of scripts and styles into your index.html' + }, { + name : 'Modular Structure', + info : 'Best practice client and server structures allow for more code reusability and ' + + 'maximum scalability' + }, { + name : 'Optimized Build', + info : 'Build process packs up your templates as a single JavaScript payload, minifies ' + + 'your scripts/css/images, and rewrites asset names for caching.' + }, { + name : 'Deployment Ready', + info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift ' + + 'subgenerators' + }]);<% } if (filters.mongoose) { %> Thing.findAsync() .then(responseWithResult(res)) .catch(handleError(res));<% } %> @@ -104,7 +107,7 @@ exports.create = function(req, res) { // Updates an existing thing in the DB. exports.update = function(req, res) { - if(req.body._id) { + if (req.body._id) { delete req.body._id; } Thing.findByIdAsync(req.params.id) diff --git a/app/templates/server/api/thing/thing.integration.js b/app/templates/server/api/thing/thing.integration.js index 1140df7e9..de7ed7ff5 100644 --- a/app/templates/server/api/thing/thing.integration.js +++ b/app/templates/server/api/thing/thing.integration.js @@ -1,7 +1,7 @@ 'use strict'; var app = require('../../app'); -var request = require('supertest');<% if(filters.mongoose) { %> +var request = require('supertest');<% if (filters.mongoose) { %> var newThing;<% } %> @@ -16,7 +16,9 @@ describe('Thing API:', function() { .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { - if (err) return done(err); + if (err) { + return done(err); + } things = res.body; done(); }); @@ -26,7 +28,7 @@ describe('Thing API:', function() { things.should.be.instanceOf(Array); }); - });<% if(filters.mongoose) { %> + });<% if (filters.mongoose) { %> describe('POST /api/things', function() { beforeEach(function(done) { @@ -39,7 +41,9 @@ describe('Thing API:', function() { .expect(201) .expect('Content-Type', /json/) .end(function(err, res) { - if (err) return done(err); + if (err) { + return done(err); + } newThing = res.body; done(); }); @@ -61,7 +65,9 @@ describe('Thing API:', function() { .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { - if (err) return done(err); + if (err) { + return done(err); + } thing = res.body; done(); }); @@ -91,7 +97,9 @@ describe('Thing API:', function() { .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { - if (err) return done(err); + if (err) { + return done(err); + } updatedThing = res.body; done(); }); @@ -115,7 +123,9 @@ describe('Thing API:', function() { .delete('/api/things/' + newThing._id) .expect(204) .end(function(err, res) { - if (err) return done(err); + if (err) { + return done(err); + } done(); }); }); @@ -125,7 +135,9 @@ describe('Thing API:', function() { .delete('/api/things/' + newThing._id) .expect(404) .end(function(err, res) { - if (err) return done(err); + if (err) { + return done(err); + } done(); }); }); diff --git a/app/templates/server/api/thing/thing.model(mongoose).js b/app/templates/server/api/thing/thing.model(mongoose).js index a44bc710e..d1e6e49ea 100644 --- a/app/templates/server/api/thing/thing.model(mongoose).js +++ b/app/templates/server/api/thing/thing.model(mongoose).js @@ -1,7 +1,7 @@ 'use strict'; -var mongoose = require('mongoose-bird')(), - Schema = mongoose.Schema; +var mongoose = require('mongoose-bird')(); +var Schema = mongoose.Schema; var ThingSchema = new Schema({ name: String, diff --git a/app/templates/server/api/thing/thing.socket(socketio).js b/app/templates/server/api/thing/thing.socket(socketio).js index d38e73e0d..2d0171cdc 100644 --- a/app/templates/server/api/thing/thing.socket(socketio).js +++ b/app/templates/server/api/thing/thing.socket(socketio).js @@ -7,10 +7,10 @@ var thing = require('./thing.model'); exports.register = function(socket) { - thing.schema.post('save', function (doc) { + thing.schema.post('save', function(doc) { onSave(socket, doc); }); - thing.schema.post('remove', function (doc) { + thing.schema.post('remove', function(doc) { onRemove(socket, doc); }); }; diff --git a/app/templates/server/api/user(auth)/index.spec.js b/app/templates/server/api/user(auth)/index.spec.js index b2510aa00..d2ee914bd 100644 --- a/app/templates/server/api/user(auth)/index.spec.js +++ b/app/templates/server/api/user(auth)/index.spec.js @@ -47,7 +47,9 @@ describe('User API Router:', function() { describe('GET /api/users', function() { it('should verify admin role and route to user.controller.index', function() { - routerStub.get.withArgs('/', 'authService.hasRole.admin', 'userCtrl.index').should.have.been.calledOnce; + routerStub.get + .withArgs('/', 'authService.hasRole.admin', 'userCtrl.index') + .should.have.been.calledOnce; }); }); @@ -55,7 +57,9 @@ describe('User API Router:', function() { describe('DELETE /api/users/:id', function() { it('should verify admin role and route to user.controller.destroy', function() { - routerStub.delete.withArgs('/:id', 'authService.hasRole.admin', 'userCtrl.destroy').should.have.been.calledOnce; + routerStub.delete + .withArgs('/:id', 'authService.hasRole.admin', 'userCtrl.destroy') + .should.have.been.calledOnce; }); }); @@ -63,7 +67,9 @@ describe('User API Router:', function() { describe('GET /api/users/me', function() { it('should be authenticated and route to user.controller.me', function() { - routerStub.get.withArgs('/me', 'authService.isAuthenticated', 'userCtrl.me').should.have.been.calledOnce; + routerStub.get + .withArgs('/me', 'authService.isAuthenticated', 'userCtrl.me') + .should.have.been.calledOnce; }); }); @@ -71,7 +77,9 @@ describe('User API Router:', function() { describe('PUT /api/users/:id/password', function() { it('should be authenticated and route to user.controller.changePassword', function() { - routerStub.put.withArgs('/:id/password', 'authService.isAuthenticated', 'userCtrl.changePassword').should.have.been.calledOnce; + routerStub.put + .withArgs('/:id/password', 'authService.isAuthenticated', 'userCtrl.changePassword') + .should.have.been.calledOnce; }); }); @@ -79,7 +87,9 @@ describe('User API Router:', function() { describe('GET /api/users/:id', function() { it('should be authenticated and route to user.controller.show', function() { - routerStub.get.withArgs('/:id', 'authService.isAuthenticated', 'userCtrl.show').should.have.been.calledOnce; + routerStub.get + .withArgs('/:id', 'authService.isAuthenticated', 'userCtrl.show') + .should.have.been.calledOnce; }); }); @@ -87,7 +97,9 @@ describe('User API Router:', function() { describe('POST /api/users', function() { it('should route to user.controller.create', function() { - routerStub.post.withArgs('/', 'userCtrl.create').should.have.been.calledOnce; + routerStub.post + .withArgs('/', 'userCtrl.create') + .should.have.been.calledOnce; }); }); diff --git a/app/templates/server/api/user(auth)/user.controller.js b/app/templates/server/api/user(auth)/user.controller.js index ed0b1749c..793da0255 100644 --- a/app/templates/server/api/user(auth)/user.controller.js +++ b/app/templates/server/api/user(auth)/user.controller.js @@ -7,21 +7,21 @@ var jwt = require('jsonwebtoken'); var validationError = function(res, statusCode) { statusCode = statusCode || 422; - return function(err){ + return function(err) { res.json(statusCode, err); }; }; -function handleError(res, statusCode){ +function handleError(res, statusCode) { statusCode = statusCode || 500; - return function(err){ + return function(err) { res.send(statusCode, err); }; } -function respondWith(res, statusCode){ +function respondWith(res, statusCode) { statusCode = statusCode || 200; - return function(){ + return function() { res.send(statusCode); }; } @@ -32,7 +32,7 @@ function respondWith(res, statusCode){ */ exports.index = function(req, res) { User.findAsync({}, '-salt -hashedPassword') - .then(function (users) { + .then(function(users) { res.json(200, users); }) .catch(handleError(res)); @@ -41,13 +41,15 @@ exports.index = function(req, res) { /** * Creates a new user */ -exports.create = function (req, res, next) { +exports.create = function(req, res, next) { var newUser = new User(req.body); newUser.provider = 'local'; newUser.role = 'user'; newUser.saveAsync() .spread(function(user) { - var token = jwt.sign({_id: user._id }, config.secrets.session, { expiresInMinutes: 60*5 }); + var token = jwt.sign({ _id: user._id }, config.secrets.session, { + expiresInMinutes: 60 * 5 + }); res.json({ token: token }); }) .catch(validationError(res)); @@ -56,17 +58,17 @@ exports.create = function (req, res, next) { /** * Get a single user */ -exports.show = function (req, res, next) { +exports.show = function(req, res, next) { var userId = req.params.id; User.findByIdAsync(userId) - .then(function (user) { - if(!user) { + .then(function(user) { + if (!user) { return res.send(401); } res.json(user.profile); }) - .catch(function(err){ + .catch(function(err) { return next(err); }); }; @@ -91,7 +93,7 @@ exports.changePassword = function(req, res, next) { User.findByIdAsync(userId) .then(function(user) { - if(user.authenticate(oldPass)) { + if (user.authenticate(oldPass)) { user.password = newPass; return user.saveAsync() .spread(respondWith(res, 200)) @@ -113,7 +115,9 @@ exports.me = function(req, res, next) { if (!user) { return res.json(401); } res.json(user); }) - .catch(function(err){ return next(err); }); + .catch(function(err) { + return next(err); + }); }; /** diff --git a/app/templates/server/api/user(auth)/user.integration.js b/app/templates/server/api/user(auth)/user.integration.js index 917acedd9..ee97d3051 100644 --- a/app/templates/server/api/user(auth)/user.integration.js +++ b/app/templates/server/api/user(auth)/user.integration.js @@ -17,7 +17,9 @@ describe('User API:', function() { }); user.save(function(err) { - if (err) return done(err); + if (err) { + return done(err); + } done(); }); }); diff --git a/app/templates/server/api/user(auth)/user.model.js b/app/templates/server/api/user(auth)/user.model.js index b90386886..201afb4bc 100644 --- a/app/templates/server/api/user(auth)/user.model.js +++ b/app/templates/server/api/user(auth)/user.model.js @@ -2,12 +2,15 @@ var mongoose = require('mongoose-bird')(); var Schema = mongoose.Schema; -var crypto = require('crypto');<% if(filters.oauth) { %> +var crypto = require('crypto');<% if (filters.oauth) { %> var authTypes = ['github', 'twitter', 'facebook', 'google'];<% } %> var UserSchema = new Schema({ name: String, - email: { type: String, lowercase: true }, + email: { + type: String, + lowercase: true + }, role: { type: String, default: 'user' @@ -53,9 +56,9 @@ UserSchema UserSchema .path('email') .validate(function(email) {<% if (filters.oauth) { %> - if (authTypes.indexOf(this.provider) !== -1){ + if (authTypes.indexOf(this.provider) !== -1) { return true; - } <% } %> + }<% } %> return email.length; }, 'Email cannot be blank'); @@ -63,9 +66,9 @@ UserSchema UserSchema .path('password') .validate(function(password) {<% if (filters.oauth) { %> - if (authTypes.indexOf(this.provider) !== -1){ + if (authTypes.indexOf(this.provider) !== -1) { return true; - } <% } %> + }<% } %> return password.length; }, 'Password cannot be blank'); @@ -74,20 +77,20 @@ UserSchema .path('email') .validate(function(value, respond) { var self = this; - return this.constructor.findOneAsync({email: value}) + return this.constructor.findOneAsync({ email: value }) .then(function(user) { - if(user) { - if(self.id === user.id) { + if (user) { + if (self.id === user.id) { return respond(true); } return respond(false); } return respond(true); }) - .catch(function(err){ + .catch(function(err) { throw err; }); -}, 'The specified email address is already in use.'); + }, 'The specified email address is already in use.'); var validatePresenceOf = function(value) { return value && value.length; @@ -100,17 +103,21 @@ UserSchema .pre('save', function(next) { // Handle new/update passwords if (this.password) { - if (!validatePresenceOf(this.password)<% if (filters.oauth) { %> && authTypes.indexOf(this.provider) === -1<% } %>) + if (!validatePresenceOf(this.password)<% if (filters.oauth) { %> && authTypes.indexOf(this.provider) === -1<% } %>) { next(new Error('Invalid password')); + } // Make salt with a callback var _this = this; this.makeSalt(function(saltErr, salt) { - if (saltErr) next(saltErr); + if (saltErr) { + next(saltErr); + } _this.salt = salt; - // Async hash _this.encryptPassword(_this.password, function(encryptErr, hashedPassword) { - if (encryptErr) next(encryptErr); + if (encryptErr) { + next(encryptErr); + } _this.password = hashedPassword; next(); }); @@ -127,22 +134,26 @@ UserSchema.methods = { /** * Authenticate - check if the passwords are the same * - * @param {String} plainText - * @callback {callback} Optional callback + * @param {String} password + * @param {Function} callback * @return {Boolean} * @api public */ authenticate: function(password, callback) { - if (!callback) + if (!callback) { return this.password === this.encryptPassword(password); + } var _this = this; this.encryptPassword(password, function(err, pwdGen) { - if (err) callback(err); + if (err) { + callback(err); + } if (_this.password === pwdGen) { callback(null, true); - } else { + } + else { callback(null, false); } }); @@ -152,7 +163,7 @@ UserSchema.methods = { * Make salt * * @param {Number} byteSize Optional salt byte size, default to 16 - * @callback {callback} Optional callback + * @param {Function} callback * @return {String} * @api public */ @@ -162,7 +173,8 @@ UserSchema.methods = { if (typeof arguments[0] === 'function') { callback = arguments[0]; byteSize = defaultByteSize; - } else if (typeof arguments[1] === 'function') { + } + else if (typeof arguments[1] === 'function') { callback = arguments[1]; } @@ -175,7 +187,9 @@ UserSchema.methods = { } return crypto.randomBytes(byteSize, function(err, salt) { - if (err) callback(err); + if (err) { + callback(err); + } return callback(null, salt.toString('base64')); }); }, @@ -184,7 +198,7 @@ UserSchema.methods = { * Encrypt password * * @param {String} password - * @callback {callback} Optional callback + * @param {Function} callback * @return {String} * @api public */ @@ -197,11 +211,15 @@ UserSchema.methods = { var defaultKeyLength = 64; var salt = new Buffer(this.salt, 'base64'); - if (!callback) - return crypto.pbkdf2Sync(password, salt, defaultIterations, defaultKeyLength).toString('base64'); + if (!callback) { + return crypto.pbkdf2Sync(password, salt, defaultIterations, defaultKeyLength) + .toString('base64'); + } return crypto.pbkdf2(password, salt, defaultIterations, defaultKeyLength, function(err, key) { - if (err) callback(err); + if (err) { + callback(err); + } return callback(null, key.toString('base64')); }); } diff --git a/app/templates/server/api/user(auth)/user.model.spec.js b/app/templates/server/api/user(auth)/user.model.spec.js index fe51ea2ea..16de6e2de 100644 --- a/app/templates/server/api/user(auth)/user.model.spec.js +++ b/app/templates/server/api/user(auth)/user.model.spec.js @@ -38,11 +38,11 @@ describe('User Model', function() { return user.saveAsync().should.be.rejected; }); - it("should authenticate user if password is valid", function() { + it('should authenticate user if password is valid', function() { user.authenticate('password').should.be.true; }); - it("should not authenticate user if password is invalid", function() { + it('should not authenticate user if password is invalid', function() { user.authenticate('blah').should.not.be.true; }); }); diff --git a/app/templates/server/app.js b/app/templates/server/app.js index 5369e94ef..632dd134f 100644 --- a/app/templates/server/app.js +++ b/app/templates/server/app.js @@ -15,7 +15,7 @@ var config = require('./config/environment'); mongoose.connect(config.mongo.uri, config.mongo.options); // Populate DB with sample data -if(config.seedDB) { require('./config/seed'); } +if (config.seedDB) { require('./config/seed'); } <% } %>// Setup server var app = express(); @@ -29,7 +29,7 @@ require('./config/express')(app); require('./routes')(app); // Start server -server.listen(config.port, config.ip, function () { +server.listen(config.port, config.ip, function() { console.log('Express server listening on %d, in %s mode', config.port, app.get('env')); }); diff --git a/app/templates/server/auth(auth)/auth.service.js b/app/templates/server/auth(auth)/auth.service.js index 101dcc5c5..43ea2851f 100644 --- a/app/templates/server/auth(auth)/auth.service.js +++ b/app/templates/server/auth(auth)/auth.service.js @@ -7,7 +7,9 @@ var jwt = require('jsonwebtoken'); var expressJwt = require('express-jwt'); var compose = require('composable-middleware'); var User = require('../api/user/user.model'); -var validateJwt = expressJwt({ secret: config.secrets.session }); +var validateJwt = expressJwt({ + secret: config.secrets.session +}); /** * Attaches the user object to the request if authenticated @@ -18,7 +20,7 @@ function isAuthenticated() { // Validate jwt .use(function(req, res, next) { // allow access_token to be passed through query parameter as well - if(req.query && req.query.hasOwnProperty('access_token')) { + if (req.query && req.query.hasOwnProperty('access_token')) { req.headers.authorization = 'Bearer ' + req.query.access_token; } validateJwt(req, res, next); @@ -26,14 +28,14 @@ function isAuthenticated() { // Attach user to request .use(function(req, res, next) { User.findByIdAsync(req.user._id) - .then(function (user) { + .then(function(user) { if (!user) { return res.send(401); } req.user = user; next(); }) - .catch(function(err){ + .catch(function(err) { return next(err); }); }); @@ -50,7 +52,8 @@ function hasRole(roleRequired) { return compose() .use(isAuthenticated()) .use(function meetsRequirements(req, res, next) { - if (config.userRoles.indexOf(req.user.role) >= config.userRoles.indexOf(roleRequired)) { + if (config.userRoles.indexOf(req.user.role) >= + config.userRoles.indexOf(roleRequired)) { next(); } else { @@ -63,7 +66,9 @@ function hasRole(roleRequired) { * Returns a jwt token signed by the app secret */ function signToken(id) { - return jwt.sign({ _id: id }, config.secrets.session, { expiresInMinutes: 60*5 }); + return jwt.sign({ _id: id }, config.secrets.session, { + expiresInMinutes: 60 * 5 + }); } /** @@ -71,7 +76,9 @@ function signToken(id) { */ function setTokenCookie(req, res) { if (!req.user) { - return res.json(404, { message: 'Something went wrong, please try again.'}); + return res.json(404, { + message: 'Something went wrong, please try again.' + }); } var token = signToken(req.user._id, req.user.role); res.cookie('token', JSON.stringify(token)); diff --git a/app/templates/server/auth(auth)/facebook(facebookAuth)/index.js b/app/templates/server/auth(auth)/facebook(facebookAuth)/index.js index 4a6f87886..f13d463e1 100644 --- a/app/templates/server/auth(auth)/facebook(facebookAuth)/index.js +++ b/app/templates/server/auth(auth)/facebook(facebookAuth)/index.js @@ -18,4 +18,4 @@ router session: false }), auth.setTokenCookie); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/app/templates/server/auth(auth)/facebook(facebookAuth)/passport.js b/app/templates/server/auth(auth)/facebook(facebookAuth)/passport.js index 90ae48939..b29d74c69 100644 --- a/app/templates/server/auth(auth)/facebook(facebookAuth)/passport.js +++ b/app/templates/server/auth(auth)/facebook(facebookAuth)/passport.js @@ -1,37 +1,38 @@ var passport = require('passport'); var FacebookStrategy = require('passport-facebook').Strategy; -exports.setup = function (User, config) { +exports.setup = function(User, config) { passport.use(new FacebookStrategy({ - clientID: config.facebook.clientID, - clientSecret: config.facebook.clientSecret, - callbackURL: config.facebook.callbackURL + clientID: config.facebook.clientID, + clientSecret: config.facebook.clientSecret, + callbackURL: config.facebook.callbackURL + }, + function(accessToken, refreshToken, profile, done) { + User.findOne({ + 'facebook.id': profile.id }, - function(accessToken, refreshToken, profile, done) { - User.findOne({ - 'facebook.id': profile.id - }, - function(err, user) { - if (err) { - return done(err); - } - if (!user) { - user = new User({ - name: profile.displayName, - email: profile.emails[0].value, - role: 'user', - username: profile.username, - provider: 'facebook', - facebook: profile._json - }); - user.save(function(err) { - if (err) done(err); - return done(err, user); - }); - } else { + function(err, user) { + if (err) { + return done(err); + } + if (!user) { + user = new User({ + name: profile.displayName, + email: profile.emails[0].value, + role: 'user', + username: profile.username, + provider: 'facebook', + facebook: profile._json + }); + user.save(function(err) { + if (err) { + done(err); + } return done(err, user); - } - }) - } - )); -}; \ No newline at end of file + }); + } else { + return done(err, user); + } + }) + })); +}; diff --git a/app/templates/server/auth(auth)/google(googleAuth)/index.js b/app/templates/server/auth(auth)/google(googleAuth)/index.js index 9b1ce39fe..673c22db4 100644 --- a/app/templates/server/auth(auth)/google(googleAuth)/index.js +++ b/app/templates/server/auth(auth)/google(googleAuth)/index.js @@ -21,4 +21,4 @@ router session: false }), auth.setTokenCookie); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/app/templates/server/auth(auth)/google(googleAuth)/passport.js b/app/templates/server/auth(auth)/google(googleAuth)/passport.js index d304e8ac9..0e7a3602d 100644 --- a/app/templates/server/auth(auth)/google(googleAuth)/passport.js +++ b/app/templates/server/auth(auth)/google(googleAuth)/passport.js @@ -1,33 +1,34 @@ var passport = require('passport'); var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy; -exports.setup = function (User, config) { +exports.setup = function(User, config) { passport.use(new GoogleStrategy({ - clientID: config.google.clientID, - clientSecret: config.google.clientSecret, - callbackURL: config.google.callbackURL - }, - function(accessToken, refreshToken, profile, done) { - User.findOne({ - 'google.id': profile.id - }, function(err, user) { - if (!user) { - user = new User({ - name: profile.displayName, - email: profile.emails[0].value, - role: 'user', - username: profile.username, - provider: 'google', - google: profile._json - }); - user.save(function(err) { - if (err) done(err); - return done(err, user); - }); - } else { + clientID: config.google.clientID, + clientSecret: config.google.clientSecret, + callbackURL: config.google.callbackURL + }, + function(accessToken, refreshToken, profile, done) { + User.findOne({ + 'google.id': profile.id + }, function(err, user) { + if (!user) { + user = new User({ + name: profile.displayName, + email: profile.emails[0].value, + role: 'user', + username: profile.username, + provider: 'google', + google: profile._json + }); + user.save(function(err) { + if (err) { + done(err); + } return done(err, user); - } - }); - } - )); + }); + } else { + return done(err, user); + } + }); + })); }; diff --git a/app/templates/server/auth(auth)/index.js b/app/templates/server/auth(auth)/index.js index e3e6c87ad..b930ad93f 100644 --- a/app/templates/server/auth(auth)/index.js +++ b/app/templates/server/auth(auth)/index.js @@ -18,4 +18,4 @@ router.use('/facebook', require('./facebook'));<% } %><% if (filters.twitterAuth router.use('/twitter', require('./twitter'));<% } %><% if (filters.googleAuth) { %> router.use('/google', require('./google'));<% } %> -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/app/templates/server/auth(auth)/local/index.js b/app/templates/server/auth(auth)/local/index.js index 8bf88a046..3a9198d11 100644 --- a/app/templates/server/auth(auth)/local/index.js +++ b/app/templates/server/auth(auth)/local/index.js @@ -7,14 +7,20 @@ var auth = require('../auth.service'); var router = express.Router(); router.post('/', function(req, res, next) { - passport.authenticate('local', function (err, user, info) { + passport.authenticate('local', function(err, user, info) { var error = err || info; - if (error) return res.json(401, error); - if (!user) return res.json(404, {message: 'Something went wrong, please try again.'}); + if (error) { + return res.json(401, error); + } + if (!user) { + return res.json(404, { + message: 'Something went wrong, please try again.' + }); + } var token = auth.signToken(user._id, user.role); - res.json({token: token}); + res.json({ token: token }); })(req, res, next) }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/app/templates/server/auth(auth)/local/passport.js b/app/templates/server/auth(auth)/local/passport.js index 4221f8786..f4898b73a 100644 --- a/app/templates/server/auth(auth)/local/passport.js +++ b/app/templates/server/auth(auth)/local/passport.js @@ -1,29 +1,35 @@ var passport = require('passport'); var LocalStrategy = require('passport-local').Strategy; -exports.setup = function (User, config) { +exports.setup = function(User, config) { passport.use(new LocalStrategy({ - usernameField: 'email', - passwordField: 'password' // this is the virtual field on the model - }, - function(email, password, done) { - User.findOne({ - email: email.toLowerCase() - }, function(err, user) { - if (err) return done(err); + usernameField: 'email', + passwordField: 'password' // this is the virtual field on the model + }, function(email, password, done) { + User.findOne({ + email: email.toLowerCase() + }, function(err, user) { + if (err) { + return done(err); + } - if (!user) { - return done(null, false, { message: 'This email is not registered.' }); - } - user.authenticate(password, function(authError, authenticated) { - if (authError) return done(authError); - if (!authenticated) { - return done(null, false, { message: 'This password is not correct.' }); - } else { - return done(null, user); - } + if (!user) { + return done(null, false, { + message: 'This email is not registered.' }); + } + user.authenticate(password, function(authError, authenticated) { + if (authError) { + return done(authError); + } + if (!authenticated) { + return done(null, false, { + message: 'This password is not correct.' + }); + } else { + return done(null, user); + } }); - } - )); + }); + })); }; diff --git a/app/templates/server/auth(auth)/twitter(twitterAuth)/index.js b/app/templates/server/auth(auth)/twitter(twitterAuth)/index.js index 8360247b8..8e6f32b5d 100644 --- a/app/templates/server/auth(auth)/twitter(twitterAuth)/index.js +++ b/app/templates/server/auth(auth)/twitter(twitterAuth)/index.js @@ -17,4 +17,4 @@ router session: false }), auth.setTokenCookie); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/app/templates/server/auth(auth)/twitter(twitterAuth)/passport.js b/app/templates/server/auth(auth)/twitter(twitterAuth)/passport.js index a2eb4a537..7428e8afd 100644 --- a/app/templates/server/auth(auth)/twitter(twitterAuth)/passport.js +++ b/app/templates/server/auth(auth)/twitter(twitterAuth)/passport.js @@ -1,4 +1,4 @@ -exports.setup = function (User, config) { +exports.setup = function(User, config) { var passport = require('passport'); var TwitterStrategy = require('passport-twitter').Strategy; @@ -23,13 +23,14 @@ exports.setup = function (User, config) { twitter: profile._json }); user.save(function(err) { - if (err) return done(err); + if (err) { + return done(err); + } return done(err, user); }); } else { return done(err, user); } }); - } - )); -}; \ No newline at end of file + })); +}; diff --git a/app/templates/server/components/errors/index.js b/app/templates/server/components/errors/index.js index 4c5a57c99..52dba3749 100644 --- a/app/templates/server/components/errors/index.js +++ b/app/templates/server/components/errors/index.js @@ -12,8 +12,10 @@ module.exports[404] = function pageNotFound(req, res) { }; res.status(result.status); - res.render(viewFilePath, function (err) { - if (err) { return res.json(result, result.status); } + res.render(viewFilePath, function(err) { + if (err) { + return res.json(result, result.status); + } res.render(viewFilePath); }); diff --git a/app/templates/server/config/_local.env.js b/app/templates/server/config/_local.env.js index c24fffd3a..12b78192e 100644 --- a/app/templates/server/config/_local.env.js +++ b/app/templates/server/config/_local.env.js @@ -7,7 +7,7 @@ module.exports = { DOMAIN: 'http://localhost:9000', - SESSION_SECRET: "<%= _.slugify(appname) + '-secret' %>",<% if (filters.facebookAuth) { %> + SESSION_SECRET: '<%= _.slugify(appname) + "-secret" %>',<% if (filters.facebookAuth) { %> FACEBOOK_ID: 'app-id', FACEBOOK_SECRET: 'secret',<% } if (filters.twitterAuth) { %> diff --git a/app/templates/server/config/environment/index.js b/app/templates/server/config/environment/index.js index 11d85f4de..2476a7e8c 100644 --- a/app/templates/server/config/environment/index.js +++ b/app/templates/server/config/environment/index.js @@ -4,7 +4,7 @@ var path = require('path'); var _ = require('lodash'); function requiredProcessEnv(name) { - if(!process.env[name]) { + if (!process.env[name]) { throw new Error('You must set the ' + name + ' environment variable'); } return process.env[name]; @@ -40,19 +40,19 @@ var all = { } } }, -<% if(filters.facebookAuth) { %> +<% if (filters.facebookAuth) { %> facebook: { clientID: process.env.FACEBOOK_ID || 'id', clientSecret: process.env.FACEBOOK_SECRET || 'secret', callbackURL: (process.env.DOMAIN || '') + '/auth/facebook/callback' }, -<% } %><% if(filters.twitterAuth) { %> +<% } %><% if (filters.twitterAuth) { %> twitter: { clientID: process.env.TWITTER_ID || 'id', clientSecret: process.env.TWITTER_SECRET || 'secret', callbackURL: (process.env.DOMAIN || '') + '/auth/twitter/callback' }, -<% } %><% if(filters.googleAuth) { %> +<% } %><% if (filters.googleAuth) { %> google: { clientID: process.env.GOOGLE_ID || 'id', clientSecret: process.env.GOOGLE_SECRET || 'secret', @@ -64,4 +64,4 @@ var all = { // ============================================== module.exports = _.merge( all, - require('./' + process.env.NODE_ENV + '.js') || {}); \ No newline at end of file + require('./' + process.env.NODE_ENV + '.js') || {}); diff --git a/app/templates/server/config/environment/production.js b/app/templates/server/config/environment/production.js index 1704df619..e0b77bf97 100644 --- a/app/templates/server/config/environment/production.js +++ b/app/templates/server/config/environment/production.js @@ -17,7 +17,8 @@ module.exports = { mongo: { uri: process.env.MONGOLAB_URI || process.env.MONGOHQ_URL || - process.env.OPENSHIFT_MONGODB_DB_URL+process.env.OPENSHIFT_APP_NAME || + process.env.OPENSHIFT_MONGODB_DB_URL + + process.env.OPENSHIFT_APP_NAME || 'mongodb://localhost/<%= _.slugify(appname) %>' } -}; \ No newline at end of file +}; diff --git a/app/templates/server/config/environment/test.js b/app/templates/server/config/environment/test.js index 711c98660..6ead6e1aa 100644 --- a/app/templates/server/config/environment/test.js +++ b/app/templates/server/config/environment/test.js @@ -7,4 +7,4 @@ module.exports = { mongo: { uri: 'mongodb://localhost/<%= _.slugify(appname) %>-test' } -}; \ No newline at end of file +}; diff --git a/app/templates/server/config/express.js b/app/templates/server/config/express.js index 2403780f1..463f637fd 100644 --- a/app/templates/server/config/express.js +++ b/app/templates/server/config/express.js @@ -30,8 +30,8 @@ module.exports = function(app) { app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); app.use(methodOverride()); - app.use(cookieParser()); - <% if (filters.auth) { %>app.use(passport.initialize());<% } %><% if (filters.twitterAuth) { %> + app.use(cookieParser());<% if (filters.auth) { %> + app.use(passport.initialize());<% } %><% if (filters.twitterAuth) { %> // Persist sessions with mongoStore // We need to enable sessions for passport twitter because its an oauth 1.0 strategy @@ -41,7 +41,7 @@ module.exports = function(app) { saveUninitialized: true, store: new mongoStore({ mongoose_connection: mongoose.connection }) })); - <% } %> +<% } %> if ('production' === env) { app.use(favicon(path.join(config.root, 'public', 'favicon.ico'))); app.use(express.static(path.join(config.root, 'public'))); diff --git a/app/templates/server/config/seed(mongoose).js b/app/templates/server/config/seed(mongoose).js index 6d56010b6..1f6945d8d 100644 --- a/app/templates/server/config/seed(mongoose).js +++ b/app/templates/server/config/seed(mongoose).js @@ -6,35 +6,40 @@ 'use strict'; var Thing = require('../api/thing/thing.model'); -<% if (filters.auth) { %> -var User = require('../api/user/user.model'); -<% } %> +<% if (filters.auth) { %>var User = require('../api/user/user.model');<% } %> Thing.find({}).removeAsync() .then(function() { Thing.create({ name : 'Development Tools', - info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' + info : 'Integration with popular tools such as Bower, Grunt, Karma, ' + + 'Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, ' + + 'Stylus, Sass, CoffeeScript, and Less.' }, { name : 'Server and Client integration', - info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' + info : 'Built with a powerful and fun stack: MongoDB, Express, ' + + 'AngularJS, and Node.' }, { name : 'Smart Build System', - info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of scripts and styles into your index.html' - }, { + info : 'Build system ignores `spec` files, allowing you to keep ' + + 'tests alongside code. Automatic injection of scripts and ' + + 'styles into your index.html' + }, { name : 'Modular Structure', - info : 'Best practice client and server structures allow for more code reusability and maximum scalability' - }, { + info : 'Best practice client and server structures allow for more ' + + 'code reusability and maximum scalability' + }, { name : 'Optimized Build', - info : 'Build process packs up your templates as a single JavaScript payload, minifies your scripts/css/images, and rewrites asset names for caching.' - },{ + info : 'Build process packs up your templates as a single JavaScript ' + + 'payload, minifies your scripts/css/images, and rewrites asset ' + + 'names for caching.' + }, { name : 'Deployment Ready', - info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' + info : 'Easily deploy your app to Heroku or Openshift with the heroku ' + + 'and openshift subgenerators' }); }); - <% if (filters.auth) { %> - User.find({}).removeAsync() .then(function() { User.create({ @@ -49,8 +54,7 @@ User.find({}).removeAsync() email: 'admin@admin.com', password: 'admin' }, function() { - console.log('finished populating users'); - } - ); + console.log('finished populating users'); + }); }); -<% } %> +<% } %> \ No newline at end of file diff --git a/app/templates/server/config/socketio(socketio).js b/app/templates/server/config/socketio(socketio).js index 2fbbc07d6..446b49dff 100644 --- a/app/templates/server/config/socketio(socketio).js +++ b/app/templates/server/config/socketio(socketio).js @@ -13,7 +13,7 @@ function onDisconnect(socket) { // When the user connects.. perform this function onConnect(socket) { // When the client emits 'info', this listens and executes - socket.on('info', function (data) { + socket.on('info', function(data) { console.info('[%s] %s', socket.address, JSON.stringify(data, null, 2)); }); @@ -21,7 +21,7 @@ function onConnect(socket) { require('../api/thing/thing.socket').register(socket); } -module.exports = function (socketio) { +module.exports = function(socketio) { // socket.io (v1.x.x) is powered by debug. // In order to see all the debug output, set DEBUG (in server/config/local.env.js) to including the desired scope. // @@ -37,15 +37,16 @@ module.exports = function (socketio) { // handshake: true // })); - socketio.on('connection', function (socket) { - socket.address = socket.handshake.address !== null ? - socket.handshake.address.address + ':' + socket.handshake.address.port : - process.env.DOMAIN; + socketio.on('connection', function(socket) { + socket.address = + socket.handshake.address !== null ? + socket.handshake.address.address + ':' + socket.handshake.address.port : + process.env.DOMAIN; socket.connectedAt = new Date(); // Call onDisconnect. - socket.on('disconnect', function () { + socket.on('disconnect', function() { onDisconnect(socket); console.info('[%s] DISCONNECTED', socket.address); }); @@ -54,4 +55,4 @@ module.exports = function (socketio) { onConnect(socket); console.info('[%s] CONNECTED', socket.address); }); -}; \ No newline at end of file +}; diff --git a/app/templates/server/routes.js b/app/templates/server/routes.js index c2b5acb2e..fff0e5416 100644 --- a/app/templates/server/routes.js +++ b/app/templates/server/routes.js @@ -9,11 +9,11 @@ var errors = require('./components/errors'); module.exports = function(app) { // Insert routes below - app.use('/api/things', require('./api/thing')); - <% if (filters.auth) { %>app.use('/api/users', require('./api/user')); + app.use('/api/things', require('./api/thing'));<% if (filters.auth) { %> + app.use('/api/users', require('./api/user')); app.use('/auth', require('./auth')); - <% } %> +<% } %> // All undefined asset or api routes should return a 404 app.route('/:url(api|auth|components|app|bower_components|assets)/*') .get(errors[404]); diff --git a/endpoint/templates/index.js b/endpoint/templates/index.js index 03fdc9779..3f8c592a6 100644 --- a/endpoint/templates/index.js +++ b/endpoint/templates/index.js @@ -12,4 +12,4 @@ router.put('/:id', controller.update); router.patch('/:id', controller.update); router.delete('/:id', controller.destroy);<% } %> -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/endpoint/templates/index.spec.js b/endpoint/templates/index.spec.js index fd1086b15..ccd15ec7e 100644 --- a/endpoint/templates/index.spec.js +++ b/endpoint/templates/index.spec.js @@ -37,7 +37,9 @@ describe('<%= classedName %> API Router:', function() { describe('GET <%= route %>', function() { it('should route to <%= name %>.controller.index', function() { - routerStub.get.withArgs('/', '<%= name %>Ctrl.index').should.have.been.calledOnce; + routerStub.get + .withArgs('/', '<%= name %>Ctrl.index') + .should.have.been.calledOnce; }); });<% if(filters.mongoose) { %> @@ -45,7 +47,9 @@ describe('<%= classedName %> API Router:', function() { describe('GET <%= route %>/:id', function() { it('should route to <%= name %>.controller.show', function() { - routerStub.get.withArgs('/:id', '<%= name %>Ctrl.show').should.have.been.calledOnce; + routerStub.get + .withArgs('/:id', '<%= name %>Ctrl.show') + .should.have.been.calledOnce; }); }); @@ -53,7 +57,9 @@ describe('<%= classedName %> API Router:', function() { describe('POST <%= route %>', function() { it('should route to <%= name %>.controller.create', function() { - routerStub.post.withArgs('/', '<%= name %>Ctrl.create').should.have.been.calledOnce; + routerStub.post + .withArgs('/', '<%= name %>Ctrl.create') + .should.have.been.calledOnce; }); }); @@ -61,7 +67,9 @@ describe('<%= classedName %> API Router:', function() { describe('PUT <%= route %>/:id', function() { it('should route to <%= name %>.controller.update', function() { - routerStub.put.withArgs('/:id', '<%= name %>Ctrl.update').should.have.been.calledOnce; + routerStub.put + .withArgs('/:id', '<%= name %>Ctrl.update') + .should.have.been.calledOnce; }); }); @@ -69,7 +77,9 @@ describe('<%= classedName %> API Router:', function() { describe('PATCH <%= route %>/:id', function() { it('should route to <%= name %>.controller.update', function() { - routerStub.patch.withArgs('/:id', '<%= name %>Ctrl.update').should.have.been.calledOnce; + routerStub.patch + .withArgs('/:id', '<%= name %>Ctrl.update') + .should.have.been.calledOnce; }); }); @@ -77,7 +87,9 @@ describe('<%= classedName %> API Router:', function() { describe('DELETE <%= route %>/:id', function() { it('should route to <%= name %>.controller.destroy', function() { - routerStub.delete.withArgs('/:id', '<%= name %>Ctrl.destroy').should.have.been.calledOnce; + routerStub.delete + .withArgs('/:id', '<%= name %>Ctrl.destroy') + .should.have.been.calledOnce; }); });<% } %> diff --git a/endpoint/templates/name.controller.js b/endpoint/templates/name.controller.js index 87dd641de..9cdfb0fb0 100644 --- a/endpoint/templates/name.controller.js +++ b/endpoint/templates/name.controller.js @@ -1,27 +1,27 @@ -'use strict';<% if (filters.mongoose) { %> +'use strict';<% if(filters.mongoose) { %> var _ = require('lodash'); var <%= classedName %> = require('./<%= name %>.model'); -function handleError(res, statusCode){ +function handleError(res, statusCode) { statusCode = statusCode || 500; - return function(err){ + return function(err) { res.send(statusCode, err); }; } -function responseWithResult(res, statusCode){ +function responseWithResult(res, statusCode) { statusCode = statusCode || 200; - return function(entity){ - if(entity){ + return function(entity) { + if (entity) { return res.json(statusCode, entity); } }; } -function handleEntityNotFound(res){ - return function(entity){ - if(!entity){ +function handleEntityNotFound(res) { + return function(entity) { + if (!entity) { res.send(404); return null; } @@ -29,19 +29,19 @@ function handleEntityNotFound(res){ }; } -function saveUpdates(updates){ - return function(entity){ +function saveUpdates(updates) { + return function(entity) { var updated = _.merge(entity, updates); return updated.saveAsync() - .spread(function (updated) { + .spread(function(updated) { return updated; }); }; } -function removeEntity(res){ - return function (entity) { - if(entity){ +function removeEntity(res) { + return function(entity) { + if (entity) { return entity.removeAsync() .then(function() { return res.send(204); @@ -51,12 +51,12 @@ function removeEntity(res){ }<% } %> // Gets list of <%= name %>s from the DB. -exports.index = function(req, res) {<% if (!filters.mongoose) { %> +exports.index = function(req, res) {<% if(!filters.mongoose) { %> res.json([]);<% } if (filters.mongoose) { %> <%= classedName %>.findAsync() .then(responseWithResult(res)) .catch(handleError(res));<% } %> -};<% if (filters.mongoose) { %> +};<% if(filters.mongoose) { %> // Gets a single <%= name %> from the DB. exports.show = function(req, res) { @@ -75,7 +75,7 @@ exports.create = function(req, res) { // Updates an existing <%= name %> in the DB. exports.update = function(req, res) { - if(req.body._id) { + if (req.body._id) { delete req.body._id; } <%= classedName %>.findByIdAsync(req.params.id) diff --git a/endpoint/templates/name.integration.js b/endpoint/templates/name.integration.js index f168f1ca9..a377456a4 100644 --- a/endpoint/templates/name.integration.js +++ b/endpoint/templates/name.integration.js @@ -16,7 +16,9 @@ describe('<%= classedName %> API:', function() { .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { - if (err) return done(err); + if (err) { + return done(err); + } <%= cameledName %>s = res.body; done(); }); @@ -39,7 +41,9 @@ describe('<%= classedName %> API:', function() { .expect(201) .expect('Content-Type', /json/) .end(function(err, res) { - if (err) return done(err); + if (err) { + return done(err); + } new<%= classedName %> = res.body; done(); }); @@ -61,7 +65,9 @@ describe('<%= classedName %> API:', function() { .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { - if (err) return done(err); + if (err) { + return done(err); + } <%= cameledName %> = res.body; done(); }); @@ -91,7 +97,9 @@ describe('<%= classedName %> API:', function() { .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { - if (err) return done(err); + if (err) { + return done(err); + } updated<%= classedName %> = res.body; done(); }); @@ -115,7 +123,9 @@ describe('<%= classedName %> API:', function() { .delete('<%= route %>/' + new<%= classedName %>._id) .expect(204) .end(function(err, res) { - if (err) return done(err); + if (err) { + return done(err); + } done(); }); }); @@ -125,7 +135,9 @@ describe('<%= classedName %> API:', function() { .delete('<%= route %>/' + new<%= classedName %>._id) .expect(404) .end(function(err, res) { - if (err) return done(err); + if (err) { + return done(err); + } done(); }); }); diff --git a/endpoint/templates/name.model(mongoose).js b/endpoint/templates/name.model(mongoose).js index 9b23d9d41..ac7f7f72a 100644 --- a/endpoint/templates/name.model(mongoose).js +++ b/endpoint/templates/name.model(mongoose).js @@ -1,7 +1,7 @@ 'use strict'; -var mongoose = require('mongoose-bird')(), - Schema = mongoose.Schema; +var mongoose = require('mongoose-bird')(); +var Schema = mongoose.Schema; var <%= classedName %>Schema = new Schema({ name: String, diff --git a/endpoint/templates/name.socket(socketio).js b/endpoint/templates/name.socket(socketio).js index aabdadeb8..260c80720 100644 --- a/endpoint/templates/name.socket(socketio).js +++ b/endpoint/templates/name.socket(socketio).js @@ -7,10 +7,10 @@ var <%= classedName %> = require('./<%= name %>.model'); exports.register = function(socket) { - <%= classedName %>.schema.post('save', function (doc) { + <%= classedName %>.schema.post('save', function(doc) { onSave(socket, doc); }); - <%= classedName %>.schema.post('remove', function (doc) { + <%= classedName %>.schema.post('remove', function(doc) { onRemove(socket, doc); }); }; @@ -21,4 +21,4 @@ function onSave(socket, doc, cb) { function onRemove(socket, doc, cb) { socket.emit('<%= name %>:remove', doc); -} \ No newline at end of file +} diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 7db1d4afe..9301c9332 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -206,6 +206,7 @@ describe('angular-fullstack generator', function () { '.gitattributes', '.gitignore', '.travis.yml', + '.jscs.json', '.yo-rc.json', 'Gruntfile.js', 'package.json', @@ -339,6 +340,10 @@ describe('angular-fullstack generator', function () { 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); }); @@ -347,6 +352,10 @@ describe('angular-fullstack generator', function () { 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'); }); @@ -427,6 +436,10 @@ describe('angular-fullstack generator', function () { 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); }); @@ -435,6 +448,10 @@ describe('angular-fullstack generator', function () { 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'); }); @@ -486,6 +503,10 @@ describe('angular-fullstack generator', function () { 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); }); @@ -494,6 +515,10 @@ describe('angular-fullstack generator', function () { 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'); }); @@ -544,6 +569,10 @@ describe('angular-fullstack generator', function () { 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); }); From 9cdcc90a19dcef98bd30a1b80ab96c4f47684f2d Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 28 Sep 2014 16:08:58 -0600 Subject: [PATCH 046/171] fix(server): server should launch in dev mode if production env var is not specified fixes #590 --- app/templates/Gruntfile.js | 30 +++++++++++++------------- app/templates/server/config/express.js | 6 +++--- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index 224e84c66..274aacbb9 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -287,10 +287,10 @@ module.exports = function (grunt) { dist: { files: { src: [ - '<%%= yeoman.dist %>/public/{,*/}*.js', - '<%%= yeoman.dist %>/public/{,*/}*.css', - '<%%= yeoman.dist %>/public/assets/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', - '<%%= yeoman.dist %>/public/assets/fonts/*' + '<%%= yeoman.dist %>/client/{,*/}*.js', + '<%%= yeoman.dist %>/client/{,*/}*.css', + '<%%= yeoman.dist %>/client/assets/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', + '<%%= yeoman.dist %>/client/assets/fonts/*' ] } } @@ -302,19 +302,19 @@ module.exports = function (grunt) { useminPrepare: { html: ['<%%= yeoman.client %>/index.html'], options: { - dest: '<%%= yeoman.dist %>/public' + dest: '<%%= yeoman.dist %>/client' } }, // Performs rewrites based on rev and the useminPrepare configuration usemin: { - html: ['<%%= yeoman.dist %>/public/{,*/}*.html'], - css: ['<%%= yeoman.dist %>/public/{,*/}*.css'], - js: ['<%%= yeoman.dist %>/public/{,*/}*.js'], + html: ['<%%= yeoman.dist %>/client/{,*/}*.html'], + css: ['<%%= yeoman.dist %>/client/{,*/}*.css'], + js: ['<%%= yeoman.dist %>/client/{,*/}*.js'], options: { assetsDirs: [ - '<%%= yeoman.dist %>/public', - '<%%= yeoman.dist %>/public/assets/images' + '<%%= yeoman.dist %>/client', + '<%%= yeoman.dist %>/client/assets/images' ], // This is so we update image references in our ng-templates patterns: { @@ -332,7 +332,7 @@ module.exports = function (grunt) { expand: true, cwd: '<%%= yeoman.client %>/assets/images', src: '{,*/}*.{png,jpg,jpeg,gif}', - dest: '<%%= yeoman.dist %>/public/assets/images' + dest: '<%%= yeoman.dist %>/client/assets/images' }] } }, @@ -343,7 +343,7 @@ module.exports = function (grunt) { expand: true, cwd: '<%%= yeoman.client %>/assets/images', src: '{,*/}*.svg', - dest: '<%%= yeoman.dist %>/public/assets/images' + dest: '<%%= yeoman.dist %>/client/assets/images' }] } }, @@ -392,7 +392,7 @@ module.exports = function (grunt) { // Replace Google CDN references cdnify: { dist: { - html: ['<%%= yeoman.dist %>/public/*.html'] + html: ['<%%= yeoman.dist %>/client/*.html'] } }, @@ -403,7 +403,7 @@ module.exports = function (grunt) { expand: true, dot: true, cwd: '<%%= yeoman.client %>', - dest: '<%%= yeoman.dist %>/public', + dest: '<%%= yeoman.dist %>/client', src: [ '*.{ico,png,txt}', '.htaccess', @@ -415,7 +415,7 @@ module.exports = function (grunt) { }, { expand: true, cwd: '.tmp/images', - dest: '<%%= yeoman.dist %>/public/assets/images', + dest: '<%%= yeoman.dist %>/client/assets/images', src: ['generated/*'] }, { expand: true, diff --git a/app/templates/server/config/express.js b/app/templates/server/config/express.js index 463f637fd..67e5398f9 100644 --- a/app/templates/server/config/express.js +++ b/app/templates/server/config/express.js @@ -43,9 +43,9 @@ module.exports = function(app) { })); <% } %> if ('production' === env) { - app.use(favicon(path.join(config.root, 'public', 'favicon.ico'))); - app.use(express.static(path.join(config.root, 'public'))); - app.set('appPath', config.root + '/public'); + app.use(favicon(path.join(config.root, 'client', 'favicon.ico'))); + app.use(express.static(path.join(config.root, 'client'))); + app.set('appPath', config.root + '/client'); app.use(morgan('dev')); } From a5edbcdf1a9a7cbb13511e908fdf8df1abbdacb2 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 28 Sep 2014 16:54:34 -0600 Subject: [PATCH 047/171] fix test framework options when scaffolding with existing project --- Gruntfile.js | 1 + app/index.js | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 8edbe3ccd..f204733b5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -139,6 +139,7 @@ module.exports = function (grunt) { bootstrap: true, uibootstrap: true, mongoose: true, + testing: 'jasmine', auth: true, oauth: ['googleAuth', 'twitterAuth'], socketio: true diff --git a/app/index.js b/app/index.js index 7dcee4d55..81c9e40b7 100644 --- a/app/index.js +++ b/app/index.js @@ -52,15 +52,23 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ }], function (answers) { this.skipConfig = answers.skipConfig; + this.filters = this._.defaults(this.config.get('filters'), { + bootstrap: true, + uibootstrap: true, + jasmine: true + }); + // NOTE: temp(?) fix for #403 - if(typeof this.oauth==='undefined') { + if(typeof this.filters.oauth==='undefined') { var strategies = Object.keys(this.filters).filter(function(key) { return key.match(/Auth$/) && key; }); - if(strategies.length) this.config.set('oauth', true); + if(strategies.length) this.filters.oauth = true; } + this.config.forceSave(); + cb(); }.bind(this)); } else { @@ -228,9 +236,17 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ } }], function (answers) { this.filters[answers.testing] = true; - if (this.filters.mocha) { + if (answers.testing === 'mocha') { + this.filters.jasmine = false; + this.filters.should = false; + this.filters.expect = false; this.filters[answers.chai] = true; } + if (answers.testing === 'jasmine') { + this.filters.mocha = false; + this.filters.should = false; + this.filters.expect = false; + } cb(); }.bind(this)); @@ -289,11 +305,6 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ }, ngModules: function() { - this.filters = this._.defaults(this.config.get('filters'), { - bootstrap: true, - uibootstrap: true - }); - var angModules = [ "'ngCookies'", "'ngResource'", From 639131387ce08032d51d1698c86baab709d1f8a7 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 28 Sep 2014 17:21:53 -0600 Subject: [PATCH 048/171] add test for #403 --- test/fixtures/.yo-rc.json | 3 ++- test/test-file-creation.js | 24 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/test/fixtures/.yo-rc.json b/test/fixtures/.yo-rc.json index b4b338ac4..9ab655e54 100644 --- a/test/fixtures/.yo-rc.json +++ b/test/fixtures/.yo-rc.json @@ -13,7 +13,8 @@ "uirouter": true, "socketio": true, "mongoose": true, - "auth": true + "auth": true, + "googleAuth": true } } } \ No newline at end of file diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 9301c9332..c8db89456 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -389,12 +389,34 @@ describe('angular-fullstack generator', function () { gen.run({}, function () { helpers.assertFile([ 'client/app/main/main.less', - 'client/app/main/main.coffee' + 'client/app/main/main.coffee', + 'server/auth/google/passport.js' ]); done(); }); }); + it('should add oauth option if existing config had oauth strategy selected', function(done) { + this.timeout(60000); + fs.copySync(__dirname + '/fixtures/.yo-rc.json', __dirname + '/temp/.yo-rc.json'); + var gen = helpers.createGenerator('angular-fullstack:app', [ + '../../app', + [ + helpers.createDummyGenerator(), + 'ng-component:app' + ] + ]); + gen.options['skip-install'] = true; + helpers.mockPrompt(gen, { + skipConfig: true + }); + gen.run({}, function () { + var yoConfig = require(__dirname + '/temp/.yo-rc.json'); + expect(yoConfig['generator-angular-fullstack'].filters.oauth).to.be.true; + done(); + }); + }); + it('should generate expected files', function (done) { gen.run({}, function () { helpers.assertFile(genFiles(defaultOptions)); From 92858565aaa67b95ebd0d082aad308b2956f934c Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 28 Sep 2014 17:46:02 -0600 Subject: [PATCH 049/171] test(gen): add tests for running the e2e tests in production mode --- app/templates/Gruntfile.js | 41 +++++++++++++------- app/templates/e2e/main/main.spec(jasmine).js | 2 +- app/templates/e2e/main/main.spec(mocha).js | 2 +- test/test-file-creation.js | 16 ++++++++ 4 files changed, 45 insertions(+), 16 deletions(-) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index 224e84c66..5adde704c 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -856,20 +856,33 @@ module.exports = function (grunt) { } else if (target === 'e2e') { - return grunt.task.run([ - 'clean:server', - 'env:all', - 'env:test',<% if (filters.stylus) { %> - 'injector:stylus', <% } %><% if (filters.less) { %> - 'injector:less', <% } %><% if (filters.sass) { %> - 'injector:sass', <% } %> - 'concurrent:test', - 'injector', - 'wiredep', - 'autoprefixer', - 'express:dev', - 'protractor' - ]); + + if (option === 'prod') { + return grunt.task.run([ + 'build', + 'env:all', + 'env:prod', + 'express:prod', + 'protractor' + ]); + } + + else { + return grunt.task.run([ + 'clean:server', + 'env:all', + 'env:test',<% if (filters.stylus) { %> + 'injector:stylus', <% } %><% if (filters.less) { %> + 'injector:less', <% } %><% if (filters.sass) { %> + 'injector:sass', <% } %> + 'concurrent:test', + 'injector', + 'wiredep', + 'autoprefixer', + 'express:dev', + 'protractor' + ]); + } } else if (target === 'coverage') { diff --git a/app/templates/e2e/main/main.spec(jasmine).js b/app/templates/e2e/main/main.spec(jasmine).js index 61745a8de..59456e4e8 100644 --- a/app/templates/e2e/main/main.spec(jasmine).js +++ b/app/templates/e2e/main/main.spec(jasmine).js @@ -10,7 +10,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(/assets\/images\/yeoman.png$/); + expect(page.imgEl.getAttribute('src')).toMatch(/yeoman.png$/); expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); }); }); diff --git a/app/templates/e2e/main/main.spec(mocha).js b/app/templates/e2e/main/main.spec(mocha).js index 1962ba765..e734af3a0 100644 --- a/app/templates/e2e/main/main.spec(mocha).js +++ b/app/templates/e2e/main/main.spec(mocha).js @@ -10,7 +10,7 @@ describe('Main View', function() { it('should include jumbotron with correct data', function() { <%= does("page.h1El.getText()") %>.eventually.equal('\'Allo, \'Allo!'); - <%= does("page.imgEl.getAttribute('src')") %>.eventually.match(/assets\/images\/yeoman.png$/); + <%= does("page.imgEl.getAttribute('src')") %>.eventually.match(/yeoman.png$/); <%= does("page.imgEl.getAttribute('alt')") %>.eventually.equal('I\'m Yeoman'); }); }); diff --git a/test/test-file-creation.js b/test/test-file-creation.js index c8db89456..c47173f4a 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -433,6 +433,10 @@ describe('angular-fullstack generator', function () { 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() { @@ -498,6 +502,10 @@ describe('angular-fullstack generator', function () { 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() { @@ -565,6 +573,10 @@ describe('angular-fullstack generator', function () { 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 no preprocessors and no server options', function() { @@ -619,6 +631,10 @@ describe('angular-fullstack generator', function () { 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); + }); }); }); }); From add986f584c0081843db4d6c6966eaebf631e871 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Sun, 28 Sep 2014 18:59:54 -0600 Subject: [PATCH 050/171] remove production e2e tests because issues with node 0.11.x --- test/test-file-creation.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/test/test-file-creation.js b/test/test-file-creation.js index c47173f4a..5a239790f 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -434,9 +434,9 @@ describe('angular-fullstack generator', function () { 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); - }); + //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() { @@ -503,9 +503,6 @@ describe('angular-fullstack generator', function () { 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() { @@ -574,9 +571,6 @@ describe('angular-fullstack generator', function () { 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 no preprocessors and no server options', function() { @@ -632,9 +626,6 @@ describe('angular-fullstack generator', function () { 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); - }); }); }); }); From 68b34f3e9333760a7cbdaa8659d43ac3feb6aee2 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Wed, 1 Oct 2014 19:22:05 -0600 Subject: [PATCH 051/171] add production e2e tests back in, only test against node 0.10.x closes #604 --- .travis.yml | 1 - test/test-file-creation.js | 18 +++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 598a4b2ae..6646824d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: node_js node_js: - '0.10' - - '0.11.13' env: global: - SAUCE_USERNAME=fullstack_ci diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 5a239790f..bdb479829 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -434,9 +434,9 @@ describe('angular-fullstack generator', function () { 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); - //}); + 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() { @@ -503,6 +503,10 @@ describe('angular-fullstack generator', function () { 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() { @@ -571,6 +575,10 @@ describe('angular-fullstack generator', function () { 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 no preprocessors and no server options', function() { @@ -626,6 +634,10 @@ describe('angular-fullstack generator', function () { 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); + }); + }); }); }); From a84f644dd95b32287e7904efbee3c6cdb624c427 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Wed, 1 Oct 2014 20:34:47 -0600 Subject: [PATCH 052/171] test(gen): fix grunt test false positives #closes 606 --- test/test-file-creation.js | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/test/test-file-creation.js b/test/test-file-creation.js index bdb479829..e2fdd0e42 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -90,25 +90,17 @@ describe('angular-fullstack generator', function () { self.timeout(timeout || 60000); var execFn = function() { - exec(cmd, function(error, stdout, stderr) { - switch(cmd) { - case 'grunt jshint': - case 'grunt jscs': - expect(stdout).to.contain('Done, without errors.'); - break; - case 'grunt test:client': - case 'grunt test:e2e': - expect(stdout, 'Client tests failed \n' + stdout).to.contain('Done, without errors.'); - break; - case 'grunt test:server': - expect(stdout, 'Server tests failed (do you have mongoDB running?) \n' + stdout).to.contain('Done, without errors.'); - break; - default: - expect(stderr).to.be.empty; + 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) { From 95152ceb2f20762e2f3a8840f31efb2aa64f81b5 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Wed, 1 Oct 2014 21:01:00 -0600 Subject: [PATCH 053/171] test(gen): add grunt test:fast task to generator, for skipping e2e tests --- Gruntfile.js | 18 ++++++++++++ package.json | 1 + test/test-file-creation.js | 56 ++++++++++++++++++++++---------------- 3 files changed, 51 insertions(+), 24 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index f204733b5..c81bffa5c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -58,6 +58,11 @@ module.exports = function (grunt) { }, all: ['Gruntfile.js', '*/index.js'] }, + env: { + fast: { + SKIP_E2E: true + } + }, mochaTest: { test: { src: [ @@ -252,6 +257,19 @@ module.exports = function (grunt) { 'installFixtures', 'mochaTest' ]); + grunt.registerTask('test', function(target, option) { + if (target === 'fast') { + grunt.task.run([ + 'env:fast' + ]); + } + + return grunt.task.run([ + 'updateFixtures', + 'installFixtures', + 'mochaTest' + ]) + }); grunt.registerTask('demo', [ 'clean:demo', diff --git a/package.json b/package.json index ec61432c7..c9e55b21b 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "grunt-contrib-clean": "^0.6.0", "grunt-contrib-jshint": "^0.10.0", "grunt-conventional-changelog": "~1.0.0", + "grunt-env": "^0.4.1", "grunt-mocha-test": "^0.11.0", "grunt-release": "~0.6.0", "load-grunt-tasks": "~0.2.0", diff --git a/test/test-file-creation.js b/test/test-file-creation.js index e2fdd0e42..bd3a3b146 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -422,13 +422,15 @@ describe('angular-fullstack generator', function () { }); }); - it('should run e2e tests successfully', function(done) { - runTest('grunt test:e2e', this, done, 240000); - }); + 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); - }); + 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() { @@ -491,13 +493,15 @@ describe('angular-fullstack generator', function () { }); }); - it('should run e2e tests successfully', function(done) { - runTest('grunt test:e2e', this, done, 240000); - }); + 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); - }); + it('should run e2e tests successfully for production app', function (done) { + runTest('grunt test:e2e:prod', this, done, 240000); + }); + } }); @@ -563,13 +567,15 @@ describe('angular-fullstack generator', function () { }); }); - it('should run e2e tests successfully', function(done) { - runTest('grunt test:e2e', this, done, 240000); - }); + 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); - }); + it('should run e2e tests successfully for production app', function (done) { + runTest('grunt test:e2e:prod', this, done, 240000); + }); + } }); @@ -622,13 +628,15 @@ describe('angular-fullstack generator', function () { }); }); - it('should run e2e tests successfully', function(done) { - runTest('grunt test:e2e', this, done, 240000); - }); + 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); - }); + it('should run e2e tests successfully for production app', function (done) { + runTest('grunt test:e2e:prod', this, done, 240000); + }); + } }); }); From 09b4b601b8ddae9d9cd66e0f10ae285210642a56 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Wed, 1 Oct 2014 21:18:18 -0600 Subject: [PATCH 054/171] comment out e2e production tests again because of flaky results --- test/test-file-creation.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/test-file-creation.js b/test/test-file-creation.js index bd3a3b146..f9ab7183b 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -498,9 +498,9 @@ describe('angular-fullstack generator', function () { 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); - }); + //it('should run e2e tests successfully for production app', function (done) { + // runTest('grunt test:e2e:prod', this, done, 240000); + //}); } }); @@ -572,9 +572,9 @@ describe('angular-fullstack generator', function () { 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); - }); + //it('should run e2e tests successfully for production app', function (done) { + // runTest('grunt test:e2e:prod', this, done, 240000); + //}); } }); @@ -633,9 +633,9 @@ describe('angular-fullstack generator', function () { 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); - }); + //it('should run e2e tests successfully for production app', function (done) { + // runTest('grunt test:e2e:prod', this, done, 240000); + //}); } }); From 499bc8db0117e8866b021f43b632936f63410b87 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Wed, 1 Oct 2014 21:39:55 -0600 Subject: [PATCH 055/171] missed one test that should have been commented out --- test/test-file-creation.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test-file-creation.js b/test/test-file-creation.js index f9ab7183b..99f5c28f8 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -427,9 +427,9 @@ describe('angular-fullstack generator', function () { 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); - }); + //it('should run e2e tests successfully for production app', function(done) { + // runTest('grunt test:e2e:prod', this, done, 240000); + //}); } }); From f29e907d4388e8a615b61077984c00c9f202820a Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Thu, 2 Oct 2014 21:25:04 -0600 Subject: [PATCH 056/171] fix appPath for serving index.html --- app/templates/server/config/express.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/templates/server/config/express.js b/app/templates/server/config/express.js index 67e5398f9..f44999c1e 100644 --- a/app/templates/server/config/express.js +++ b/app/templates/server/config/express.js @@ -42,18 +42,18 @@ module.exports = function(app) { store: new mongoStore({ mongoose_connection: mongoose.connection }) })); <% } %> + app.set('appPath', path.join(config.root, 'client')); + if ('production' === env) { app.use(favicon(path.join(config.root, 'client', 'favicon.ico'))); - app.use(express.static(path.join(config.root, 'client'))); - app.set('appPath', config.root + '/client'); + app.use(express.static(app.get('appPath'))); app.use(morgan('dev')); } if ('development' === env || 'test' === env) { app.use(require('connect-livereload')()); app.use(express.static(path.join(config.root, '.tmp'))); - app.use(express.static(path.join(config.root, 'client'))); - app.set('appPath', 'client'); + app.use(express.static(app.get('appPath'))); app.use(morgan('dev')); app.use(errorHandler()); // Error handler - has to be last } From 8c9f0f934b4567b646ef88578de0c6adfea15eb1 Mon Sep 17 00:00:00 2001 From: gintsgints Date: Sun, 28 Sep 2014 12:33:35 +0300 Subject: [PATCH 057/171] test(gen): use path.normalize to be windows aware - change function assertOnlyFiles to use a different local variable from global module name - path. - show expected files when a test fails - use path.normalize before search in the array Closes #601 --- test/test-file-creation.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 99f5c28f8..2fef6a3c9 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -42,20 +42,20 @@ describe('angular-fullstack generator', function () { * * @param {Array} expectedFiles - array of files * @param {Function} done - callback(error{Error}) - * @param {String} path - top level path to assert files at (optional) + * @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, path, skip) { - path = path || './'; + function assertOnlyFiles(expectedFiles, done, topLevelPath, skip) { + topLevelPath = topLevelPath || './'; skip = skip || ['node_modules', 'client/bower_components']; - recursiveReadDir(path, skip, function(err, actualFiles) { + recursiveReadDir(topLevelPath, skip, function(err, actualFiles) { if (err) { return done(err); } var files = actualFiles.concat(); expectedFiles.forEach(function(file, i) { - var index = files.indexOf(file); + var index = files.indexOf(path.normalize(file)); if (index >= 0) { files.splice(index, 1); } @@ -63,7 +63,7 @@ describe('angular-fullstack generator', function () { if (files.length !== 0) { err = new Error('unexpected files found'); - err.expected = ''; + err.expected = expectedFiles.join('\n'); err.actual = files.join('\n'); return done(err); } From 6a196e21036dead1046794a7be4a309832a552bd Mon Sep 17 00:00:00 2001 From: Thiago Sigrist Date: Wed, 22 Oct 2014 20:03:04 -0200 Subject: [PATCH 058/171] fix(deps): use angular ~1.2 before migrated to 1.3 Change angular version spec from ">=1.2.*" to "~1.2" to ensure generator tests run before migration to 1.3 is completed. Closes #654. --- app/templates/_bower.json | 14 +++++++------- test/test-file-creation.js | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/app/templates/_bower.json b/app/templates/_bower.json index 8d2e66a8a..4f9167ed6 100644 --- a/app/templates/_bower.json +++ b/app/templates/_bower.json @@ -2,15 +2,15 @@ "name": "<%= _.slugify(_.humanize(appname)) %>", "version": "0.0.0", "dependencies": { - "angular": ">=1.2.*", + "angular": "~1.2", "json3": "~3.3.1", "es5-shim": "~3.0.1",<% if(filters.bootstrap) { %><% if (filters.sass) { %> "bootstrap-sass-official": "~3.1.1",<% } %> "bootstrap": "~3.1.1",<% } %> - "angular-resource": ">=1.2.*", - "angular-cookies": ">=1.2.*", - "angular-sanitize": ">=1.2.*",<% if (filters.ngroute) { %> - "angular-route": ">=1.2.*",<% } %><% if (filters.uibootstrap) { %> + "angular-resource": "~1.2", + "angular-cookies": "~1.2", + "angular-sanitize": "~1.2",<% if (filters.ngroute) { %> + "angular-route": "~1.2",<% } %><% if (filters.uibootstrap) { %> "angular-bootstrap": "~0.11.0",<% } %> "font-awesome": ">=4.1.0", "lodash": "~2.4.1"<% if (filters.socketio) { %>, @@ -18,7 +18,7 @@ "angular-ui-router": "~0.2.10"<% } %> }, "devDependencies": { - "angular-mocks": ">=1.2.*", - "angular-scenario": ">=1.2.*" + "angular-mocks": "~1.2", + "angular-scenario": "~1.2" } } diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 2fef6a3c9..6164b690b 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -314,6 +314,41 @@ describe('angular-fullstack generator', function () { }.bind(this)); }); + describe('making sure test fixtures are present', function() { + + it('should have package.json in fixtures', function() { + helpers.assertFile([ + path.join(__dirname, 'fixtures', 'package.json') + ]); + }); + + it('should have bower.json in fixtures', function() { + helpers.assertFile([ + 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); + }); + helpers.assertFile(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); + }); + helpers.assertFile(deps); + }); + }); + describe('running app', function() { beforeEach(function() { From dedf46c7a0664d5ea5a9f0686cbfa5593fdbc6d1 Mon Sep 17 00:00:00 2001 From: Vasil Velichkov Date: Wed, 26 Nov 2014 02:05:16 +0200 Subject: [PATCH 059/171] fix(openshift): fix processing of rhc app show output rhc returns the following text when application is not found $ rhc app show test Application 'test' not found. --- openshift/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openshift/index.js b/openshift/index.js index 518806b08..7929c0e09 100644 --- a/openshift/index.js +++ b/openshift/index.js @@ -107,7 +107,7 @@ Generator.prototype.rhcAppShow = function rhcAppShow() { this.abort = true; } // No remote found - else if (stdout.search('not found.') < 0) { + else if (stdout.search('not found.') >= 0) { console.log('No existing app found.'); } // Error From 943120e04c454abf2e0e6afc7965318d75c006b0 Mon Sep 17 00:00:00 2001 From: Thiago Sigrist Date: Wed, 26 Nov 2014 10:50:22 -0200 Subject: [PATCH 060/171] feat(server): add support for sequelize Closes #414. --- app/index.js | 55 +++- app/templates/_package.json | 8 +- .../signup/signup.controller(coffee).coffee | 10 +- .../signup/signup.controller(js).js | 12 +- .../app/main/main.controller(coffee).coffee | 2 +- .../client/app/main/main.controller(js).js | 2 +- .../login/login.spec(jasmine).js | 11 +- .../account(auth)/login/login.spec(mocha).js | 14 +- .../logout/logout.spec(jasmine).js | 11 +- .../logout/logout.spec(mocha).js | 14 +- .../signup/signup.spec(jasmine).js | 8 +- .../signup/signup.spec(mocha).js | 11 +- app/templates/protractor.conf.js | 4 +- app/templates/server/api/thing/index.js | 2 +- app/templates/server/api/thing/index.spec.js | 6 +- ...troller.js => thing.controller(models).js} | 78 +++--- .../api/thing/thing.controller(noModels).js | 38 +++ .../server/api/thing/thing.integration.js | 6 +- ...ose).js => thing.model(mongooseModels).js} | 0 .../api/thing/thing.model(sequelizeModels).js | 15 ++ .../api/thing/thing.socket(socketio).js | 21 +- .../server/api/user(auth)/user.controller.js | 65 ++++- .../server/api/user(auth)/user.integration.js | 25 +- ...model.js => user.model(mongooseModels).js} | 0 .../user(auth)/user.model(sequelizeModels).js | 235 ++++++++++++++++++ ....js => user.model.spec(mongooseModels).js} | 0 .../user.model.spec(sequelizeModels).js | 52 ++++ app/templates/server/app.js | 31 ++- .../server/auth(auth)/auth.service.js | 16 +- .../facebook(facebookAuth)/passport.js | 52 ++-- .../auth(auth)/google(googleAuth)/passport.js | 50 ++-- app/templates/server/auth(auth)/index.js | 5 +- .../server/auth(auth)/local/passport.js | 33 ++- .../twitter(twitterAuth)/passport.js | 49 ++-- .../server/config/environment/development.js | 10 + .../server/config/environment/test.js | 10 + app/templates/server/config/express.js | 10 +- .../{seed(mongoose).js => seed(models).js} | 36 ++- .../server/sqldb(sequelize)/index.js | 35 +++ endpoint/index.js | 42 ++++ endpoint/templates/name.model(sequelize).js | 15 ++ endpoint/templates/name.socket(socketio).js | 21 +- test/test-file-creation.js | 94 ++++++- 43 files changed, 974 insertions(+), 240 deletions(-) rename app/templates/server/api/thing/{thing.controller.js => thing.controller(models).js} (52%) create mode 100644 app/templates/server/api/thing/thing.controller(noModels).js rename app/templates/server/api/thing/{thing.model(mongoose).js => thing.model(mongooseModels).js} (100%) create mode 100644 app/templates/server/api/thing/thing.model(sequelizeModels).js rename app/templates/server/api/user(auth)/{user.model.js => user.model(mongooseModels).js} (100%) create mode 100644 app/templates/server/api/user(auth)/user.model(sequelizeModels).js rename app/templates/server/api/user(auth)/{user.model.spec.js => user.model.spec(mongooseModels).js} (100%) create mode 100644 app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js rename app/templates/server/config/{seed(mongoose).js => seed(models).js} (64%) create mode 100644 app/templates/server/sqldb(sequelize)/index.js create mode 100644 endpoint/templates/name.model(sequelize).js diff --git a/app/index.js b/app/index.js index 81c9e40b7..15aaf737f 100644 --- a/app/index.js +++ b/app/index.js @@ -145,15 +145,38 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ this.log('\n# Server\n'); this.prompt([{ - type: 'confirm', - name: 'mongoose', - message: 'Would you like to use mongoDB with Mongoose for data modeling?' + type: 'checkbox', + name: 'odms', + message: 'What would you like to use for data modeling?', + choices: [ + { + value: 'mongoose', + name: 'Mongoose (MongoDB)', + checked: true + }, + { + value: 'sequelize', + name: 'Sequelize (MySQL, SQLite, MariaDB, PostgreSQL)', + checked: false + } + ] + }, { + type: 'list', + name: 'models', + message: 'What would you like to use for the default models?', + choices: [ 'Mongoose', 'Sequelize' ], + filter: function( val ) { + return val.toLowerCase(); + }, + when: function(answers) { + return answers.odms && answers.odms.length > 1; + } }, { type: 'confirm', name: 'auth', message: 'Would you scaffold out an authentication boilerplate?', when: function (answers) { - return answers.mongoose; + return answers.odms && answers.odms.length !== 0; } }, { type: 'checkbox', @@ -183,15 +206,29 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ type: 'confirm', name: 'socketio', message: 'Would you like to use socket.io?', - // to-do: should not be dependent on mongoose + // to-do: should not be dependent on ODMs when: function (answers) { - return answers.mongoose; + return answers.odms && answers.odms.length !== 0; }, default: true }], function (answers) { if(answers.socketio) this.filters.socketio = true; - if(answers.mongoose) this.filters.mongoose = true; if(answers.auth) this.filters.auth = true; + if(answers.odms.length > 0) { + var models; + if(!answers.models) { + models = answers.odms[0]; + } else { + models = answers.models; + } + this.filters.models = true; + this.filters[models + 'Models'] = true; + answers.odms.forEach(function(odm) { + this.filters[odm] = true; + }.bind(this)); + } else { + this.filters.noModels = true; + } if(answers.oauth) { if(answers.oauth.length) this.filters.oauth = true; answers.oauth.forEach(function(oauthStrategy) { @@ -265,6 +302,10 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ this.config.set('registerSocketsFile', 'server/config/socketio.js'); this.config.set('socketsNeedle', '// Insert sockets below'); + this.config.set('insertModels', true); + this.config.set('registerModelsFile', 'server/sqldb/index.js'); + this.config.set('modelsNeedle', '// Insert models below'); + this.config.set('filters', this.filters); this.config.forceSave(); }, diff --git a/app/templates/_package.json b/app/templates/_package.json index d33133187..1666ea8b0 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -16,7 +16,9 @@ "jade": "~1.2.0",<% } %><% if (filters.html) { %> "ejs": "~0.8.4",<% } %><% if (filters.mongoose) { %> "mongoose": "~3.8.8", - "mongoose-bird": "~0.0.1",<% } %><% if (filters.auth) { %> + "mongoose-bird": "~0.0.1",<% } %><% if (filters.sequelize) { %> + "sequelize": "^2.0.0-rc2", + "sqlite3": "~3.0.2",<% } %><% if (filters.auth) { %> "jsonwebtoken": "^0.3.0", "express-jwt": "^0.1.3", "passport": "~0.2.0", @@ -24,8 +26,8 @@ "passport-facebook": "latest",<% } %><% if (filters.twitterAuth) { %> "passport-twitter": "latest",<% } %><% if (filters.googleAuth) { %> "passport-google-oauth": "latest",<% } %> - "composable-middleware": "^0.3.0", - "connect-mongo": "^0.4.1"<% if (filters.socketio) { %>, + "composable-middleware": "^0.3.0"<% if (filters.mongoose) { %>, + "connect-mongo": "^0.4.1"<% } %><% if (filters.socketio) { %>, "socket.io": "^1.0.6", "socket.io-client": "^1.0.6", "socketio-jwt": "^2.0.2"<% } %> diff --git a/app/templates/client/app/account(auth)/signup/signup.controller(coffee).coffee b/app/templates/client/app/account(auth)/signup/signup.controller(coffee).coffee index 3d96f3bf5..d167b7e30 100644 --- a/app/templates/client/app/account(auth)/signup/signup.controller(coffee).coffee +++ b/app/templates/client/app/account(auth)/signup/signup.controller(coffee).coffee @@ -20,11 +20,17 @@ angular.module '<%= scriptAppName %>' .catch (err) -> err = err.data $scope.errors = {} - +<% if (filters.mongooseModels) { %> # Update validity of form fields that match the mongoose errors angular.forEach err.errors, (error, field) -> form[field].$setValidity 'mongoose', false - $scope.errors[field] = error.message + $scope.errors[field] = error.message<% } + if (filters.sequelizeModels) { %> + # Update validity of form fields that match the sequelize errors + if err.name + angular.forEach err.fields, (field) -> + form[field].$setValidity 'mongoose', false + $scope.errors[field] = err.message<% } %> <% if (filters.oauth) {%> $scope.loginOauth = (provider) -> $window.location.href = '/auth/' + provider<% } %> diff --git a/app/templates/client/app/account(auth)/signup/signup.controller(js).js b/app/templates/client/app/account(auth)/signup/signup.controller(js).js index 7e93a6949..346eb7ea7 100644 --- a/app/templates/client/app/account(auth)/signup/signup.controller(js).js +++ b/app/templates/client/app/account(auth)/signup/signup.controller(js).js @@ -21,12 +21,20 @@ angular.module('<%= scriptAppName %>') .catch(function(err) { err = err.data; $scope.errors = {}; - +<% if (filters.mongooseModels) { %> // Update validity of form fields that match the mongoose errors angular.forEach(err.errors, function(error, field) { form[field].$setValidity('mongoose', false); $scope.errors[field] = error.message; - }); + });<% } + if (filters.sequelizeModels) { %> + // Update validity of form fields that match the sequelize errors + if (err.name) { + angular.forEach(err.fields, function(field) { + form[field].$setValidity('mongoose', false); + $scope.errors[field] = err.message; + }); + }<% } %> }); } }; diff --git a/app/templates/client/app/main/main.controller(coffee).coffee b/app/templates/client/app/main/main.controller(coffee).coffee index 3dffae2f7..4b04a951b 100644 --- a/app/templates/client/app/main/main.controller(coffee).coffee +++ b/app/templates/client/app/main/main.controller(coffee).coffee @@ -7,7 +7,7 @@ angular.module '<%= scriptAppName %>' $http.get('/api/things').success (awesomeThings) -> $scope.awesomeThings = awesomeThings <% if (filters.socketio) { %>socket.syncUpdates 'thing', $scope.awesomeThings<% } %> -<% if (filters.mongoose) { %> +<% if (filters.models) { %> $scope.addThing = -> return if $scope.newThing is '' $http.post '/api/things', diff --git a/app/templates/client/app/main/main.controller(js).js b/app/templates/client/app/main/main.controller(js).js index 8164a90dc..345d9909d 100644 --- a/app/templates/client/app/main/main.controller(js).js +++ b/app/templates/client/app/main/main.controller(js).js @@ -8,7 +8,7 @@ angular.module('<%= scriptAppName %>') $scope.awesomeThings = awesomeThings;<% if (filters.socketio) { %> socket.syncUpdates('thing', $scope.awesomeThings);<% } %> }); -<% if (filters.mongoose) { %> +<% if (filters.models) { %> $scope.addThing = function() { if ($scope.newThing === '') { return; diff --git a/app/templates/e2e/account(auth)/login/login.spec(jasmine).js b/app/templates/e2e/account(auth)/login/login.spec(jasmine).js index d3f0d48ed..e690c2601 100644 --- a/app/templates/e2e/account(auth)/login/login.spec(jasmine).js +++ b/app/templates/e2e/account(auth)/login/login.spec(jasmine).js @@ -1,7 +1,8 @@ 'use strict'; -var config = protractor.getInstance().params; -var UserModel = require(config.serverConfig.root + '/server/api/user/user.model'); +var config = protractor.getInstance().params;<% if (filters.mongooseModels) { %> +var UserModel = require(config.serverConfig.root + '/server/api/user/user.model');<% } %><% if (filters.sequelizeModels) { %> +var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> describe('Login View', function() { var page; @@ -18,9 +19,11 @@ describe('Login View', function() { }; beforeEach(function(done) { - UserModel.removeAsync() + <% if (filters.mongooseModels) { %>UserModel.removeAsync()<% } + if (filters.sequelizeModels) { %>UserModel.destroy()<% } %> .then(function() { - return UserModel.createAsync(testUser); + <% if (filters.mongooseModels) { %>return UserModel.createAsync(testUser);<% } + if (filters.sequelizeModels) { %>return UserModel.create(testUser);<% } %> }) .then(loadPage) .finally(done); diff --git a/app/templates/e2e/account(auth)/login/login.spec(mocha).js b/app/templates/e2e/account(auth)/login/login.spec(mocha).js index a2c986f32..3335cf3b4 100644 --- a/app/templates/e2e/account(auth)/login/login.spec(mocha).js +++ b/app/templates/e2e/account(auth)/login/login.spec(mocha).js @@ -1,7 +1,8 @@ 'use strict'; -var config = protractor.getInstance().params; -var UserModel = require(config.serverConfig.root + '/server/api/user/user.model'); +var config = protractor.getInstance().params;<% if (filters.mongooseModels) { %> +var UserModel = require(config.serverConfig.root + '/server/api/user/user.model');<% } %><% if (filters.sequelizeModels) { %> +var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> describe('Login View', function() { var page; @@ -19,15 +20,18 @@ describe('Login View', function() { before(function() { return UserModel - .removeAsync() + <% if (filters.mongooseModels) { %>.removeAsync()<% } + if (filters.sequelizeModels) { %>.destroy()<% } %> .then(function() { - return UserModel.createAsync(testUser); + <% if (filters.mongooseModels) { %>return UserModel.createAsync(testUser);<% } + if (filters.sequelizeModels) { %>return UserModel.create(testUser);<% } %> }) .then(loadPage); }); after(function() { - return UserModel.removeAsync(); + <% if (filters.mongooseModels) { %>return UserModel.removeAsync();<% } + if (filters.sequelizeModels) { %>return UserModel.destroy();<% } %> }); it('should include login form with correct inputs and submit button', function() { diff --git a/app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js b/app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js index f496459d8..f3149865c 100644 --- a/app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js +++ b/app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js @@ -1,7 +1,8 @@ 'use strict'; -var config = protractor.getInstance().params; -var UserModel = require(config.serverConfig.root + '/server/api/user/user.model'); +var config = protractor.getInstance().params;<% if (filters.mongooseModels) { %> +var UserModel = require(config.serverConfig.root + '/server/api/user/user.model');<% } %><% if (filters.sequelizeModels) { %> +var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> describe('Logout View', function() { var login = function(user) { @@ -16,9 +17,11 @@ describe('Logout View', function() { }; beforeEach(function(done) { - UserModel.removeAsync() + <% if (filters.mongooseModels) { %>UserModel.removeAsync()<% } + if (filters.sequelizeModels) { %>UserModel.destroy()<% } %> .then(function() { - return UserModel.createAsync(testUser); + <% if (filters.mongooseModels) { %>return UserModel.createAsync(testUser);<% } + if (filters.sequelizeModels) { %>return UserModel.create(testUser);<% } %> }) .then(function() { return login(testUser); diff --git a/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js b/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js index 3adba51f8..85d92cf75 100644 --- a/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js +++ b/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js @@ -1,7 +1,8 @@ 'use strict'; -var config = protractor.getInstance().params; -var UserModel = require(config.serverConfig.root + '/server/api/user/user.model'); +var config = protractor.getInstance().params;<% if (filters.mongooseModels) { %> +var UserModel = require(config.serverConfig.root + '/server/api/user/user.model');<% } %><% if (filters.sequelizeModels) { %> +var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> describe('Logout View', function() { var login = function(user) { @@ -17,9 +18,11 @@ describe('Logout View', function() { beforeEach(function() { return UserModel - .removeAsync() + <% if (filters.mongooseModels) { %>.removeAsync()<% } + if (filters.sequelizeModels) { %>.destroy()<% } %> .then(function() { - return UserModel.createAsync(testUser); + <% if (filters.mongooseModels) { %>return UserModel.createAsync(testUser);<% } + if (filters.sequelizeModels) { %>return UserModel.create(testUser);<% } %> }) .then(function() { return login(testUser); @@ -27,7 +30,8 @@ describe('Logout View', function() { }); after(function() { - return UserModel.removeAsync(); + <% if (filters.mongooseModels) { %>return UserModel.removeAsync();<% } + if (filters.sequelizeModels) { %>return UserModel.destroy();<% } %> }) describe('with local auth', function() { diff --git a/app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js b/app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js index 856786e4a..abbfc98a4 100644 --- a/app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js +++ b/app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js @@ -1,7 +1,8 @@ 'use strict'; -var config = protractor.getInstance().params; -var UserModel = require(config.serverConfig.root + '/server/api/user/user.model'); +var config = protractor.getInstance().params;<% if (filters.mongooseModels) { %> +var UserModel = require(config.serverConfig.root + '/server/api/user/user.model');<% } %><% if (filters.sequelizeModels) { %> +var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> describe('Signup View', function() { var page; @@ -35,7 +36,8 @@ describe('Signup View', function() { describe('with local auth', function() { it('should signup a new user, log them in, and redirecting to "/"', function(done) { - UserModel.remove(function() { + <% if (filters.mongooseModels) { %>UserModel.remove(function() {<% } + if (filters.sequelizeModels) { %>UserModel.destroy().then(function() {<% } %> page.signup(testUser); var navbar = require('../../components/navbar/navbar.po'); diff --git a/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js b/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js index 6a6b0a775..5351ea75d 100644 --- a/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js +++ b/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js @@ -1,7 +1,8 @@ 'use strict'; -var config = protractor.getInstance().params; -var UserModel = require(config.serverConfig.root + '/server/api/user/user.model'); +var config = protractor.getInstance().params;<% if (filters.mongooseModels) { %> +var UserModel = require(config.serverConfig.root + '/server/api/user/user.model');<% } %><% if (filters.sequelizeModels) { %> +var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> describe('Signup View', function() { var page; @@ -22,7 +23,8 @@ describe('Signup View', function() { }); after(function() { - return UserModel.removeAsync(); + <% if (filters.mongooseModels) { %>return UserModel.removeAsync();<% } + if (filters.sequelizeModels) { %>return UserModel.destroy();<% } %> }); it('should include signup form with correct inputs and submit button', function() { @@ -39,7 +41,8 @@ describe('Signup View', function() { describe('with local auth', function() { it('should signup a new user, log them in, and redirecting to "/"', function(done) { - UserModel.remove(function() { + <% if (filters.mongooseModels) { %>UserModel.remove(function() {<% } + if (filters.sequelizeModels) { %>UserModel.destroy().then(function() {<% } %> page.signup(testUser); var navbar = require('../../components/navbar/navbar.po'); diff --git a/app/templates/protractor.conf.js b/app/templates/protractor.conf.js index f0061ff86..a5c42a390 100644 --- a/app/templates/protractor.conf.js +++ b/app/templates/protractor.conf.js @@ -75,10 +75,10 @@ var config = { ); <% } %> var serverConfig = config.params.serverConfig; - +<% if (filters.mongoose) { %> // Setup mongo for tests var mongoose = require('mongoose-bird')(); - mongoose.connect(serverConfig.mongo.uri, serverConfig.mongo.options); // Connect to database + mongoose.connect(serverConfig.mongo.uri, serverConfig.mongo.options); // Connect to database<% } %> } }; diff --git a/app/templates/server/api/thing/index.js b/app/templates/server/api/thing/index.js index ea697d38b..fde8f3968 100644 --- a/app/templates/server/api/thing/index.js +++ b/app/templates/server/api/thing/index.js @@ -5,7 +5,7 @@ var controller = require('./thing.controller'); var router = express.Router(); -router.get('/', controller.index);<% if (filters.mongoose) { %> +router.get('/', controller.index);<% if (filters.models) { %> router.get('/:id', controller.show); router.post('/', controller.create); router.put('/:id', controller.update); diff --git a/app/templates/server/api/thing/index.spec.js b/app/templates/server/api/thing/index.spec.js index 899b22d79..91f02f6df 100644 --- a/app/templates/server/api/thing/index.spec.js +++ b/app/templates/server/api/thing/index.spec.js @@ -3,7 +3,7 @@ var proxyquire = require('proxyquire').noPreserveCache(); var thingCtrlStub = { - index: 'thingCtrl.index'<% if (filters.mongoose) { %>, + index: 'thingCtrl.index'<% if (filters.models) { %>, show: 'thingCtrl.show', create: 'thingCtrl.create', update: 'thingCtrl.update', @@ -11,7 +11,7 @@ var thingCtrlStub = { }; var routerStub = { - get: sinon.spy()<% if (filters.mongoose) { %>, + get: sinon.spy()<% if (filters.models) { %>, put: sinon.spy(), patch: sinon.spy(), post: sinon.spy(), @@ -42,7 +42,7 @@ describe('Thing API Router:', function() { .should.have.been.calledOnce; }); - });<% if (filters.mongoose) { %> + });<% if (filters.models) { %> describe('GET /api/things/:id', function() { diff --git a/app/templates/server/api/thing/thing.controller.js b/app/templates/server/api/thing/thing.controller(models).js similarity index 52% rename from app/templates/server/api/thing/thing.controller.js rename to app/templates/server/api/thing/thing.controller(models).js index 710c7cd38..a14fbd4b3 100644 --- a/app/templates/server/api/thing/thing.controller.js +++ b/app/templates/server/api/thing/thing.controller(models).js @@ -7,10 +7,12 @@ * DELETE /things/:id -> destroy */ -'use strict';<% if (filters.mongoose) { %> +'use strict'; -var _ = require('lodash'); -var Thing = require('./thing.model'); +var _ = require('lodash');<% if (filters.mongooseModels) { %> +var Thing = require('./thing.model');<% } %><% if (filters.sequelizeModels) { %> +var sqldb = require('../../sqldb') +var Thing = sqldb.Thing;<% } %> function handleError(res, statusCode) { statusCode = statusCode || 500; @@ -40,9 +42,11 @@ function handleEntityNotFound(res) { function saveUpdates(updates) { return function(entity) { - var updated = _.merge(entity, updates); + <% if (filters.mongooseModels) { %>var updated = _.merge(entity, updates); return updated.saveAsync() - .spread(function(updated) { + .spread(function(updated) {<% } + if (filters.sequelizeModels) { %>return entity.updateAttributes(updates) + .then(function(updated) {<% } %> return updated; }); }; @@ -51,48 +55,31 @@ function saveUpdates(updates) { function removeEntity(res) { return function(entity) { if (entity) { - return entity.removeAsync() + <% if (filters.mongooseModels) { %>return entity.removeAsync()<% } + if (filters.sequelizeModels) { %>return entity.destroy()<% } %> .then(function() { return res.send(204); }); } }; -}<% } %> +} // Get list of things -exports.index = function(req, res) {<% if(!filters.mongoose) { %> - res.json([{ - name : 'Development Tools', - info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, ' + - 'Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' - }, { - name : 'Server and Client integration', - info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' - }, { - name : 'Smart Build System', - info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. ' + - 'Automatic injection of scripts and styles into your index.html' - }, { - name : 'Modular Structure', - info : 'Best practice client and server structures allow for more code reusability and ' + - 'maximum scalability' - }, { - name : 'Optimized Build', - info : 'Build process packs up your templates as a single JavaScript payload, minifies ' + - 'your scripts/css/images, and rewrites asset names for caching.' - }, { - name : 'Deployment Ready', - info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift ' + - 'subgenerators' - }]);<% } if (filters.mongoose) { %> - Thing.findAsync() +exports.index = function(req, res) { + <% if (filters.mongooseModels) { %>Thing.findAsync()<% } + if (filters.sequelizeModels) { %>Thing.findAll()<% } %> .then(responseWithResult(res)) - .catch(handleError(res));<% } %> -};<% if (filters.mongoose) { %> + .catch(handleError(res)); +}; // Get a single thing exports.show = function(req, res) { - Thing.findByIdAsync(req.params.id) + <% if (filters.mongooseModels) { %>Thing.findByIdAsync(req.params.id)<% } + if (filters.sequelizeModels) { %>Thing.find({ + where: { + _id: req.params.id + } + })<% } %> .then(handleEntityNotFound(res)) .then(responseWithResult(res)) .catch(handleError(res)); @@ -100,7 +87,8 @@ exports.show = function(req, res) { // Creates a new thing in the DB. exports.create = function(req, res) { - Thing.createAsync(req.body) + <% if (filters.mongooseModels) { %>Thing.createAsync(req.body)<% } + if (filters.sequelizeModels) { %>Thing.create(req.body)<% } %> .then(responseWithResult(res, 201)) .catch(handleError(res)); }; @@ -110,7 +98,12 @@ exports.update = function(req, res) { if (req.body._id) { delete req.body._id; } - Thing.findByIdAsync(req.params.id) + <% if (filters.mongooseModels) { %>Thing.findByIdAsync(req.params.id)<% } + if (filters.sequelizeModels) { %>Thing.find({ + where: { + _id: req.params.id + } + })<% } %> .then(handleEntityNotFound(res)) .then(saveUpdates(req.body)) .then(responseWithResult(res)) @@ -119,8 +112,13 @@ exports.update = function(req, res) { // Deletes a thing from the DB. exports.destroy = function(req, res) { - Thing.findByIdAsync(req.params.id) + <% if (filters.mongooseModels) { %>Thing.findByIdAsync(req.params.id)<% } + if (filters.sequelizeModels) { %>Thing.find({ + where: { + _id: req.params.id + } + })<% } %> .then(handleEntityNotFound(res)) .then(removeEntity(res)) .catch(handleError(res)); -};<% } %> +}; diff --git a/app/templates/server/api/thing/thing.controller(noModels).js b/app/templates/server/api/thing/thing.controller(noModels).js new file mode 100644 index 000000000..02b2410c5 --- /dev/null +++ b/app/templates/server/api/thing/thing.controller(noModels).js @@ -0,0 +1,38 @@ +/** + * Using Rails-like standard naming convention for endpoints. + * GET /things -> index + * POST /things -> create + * GET /things/:id -> show + * PUT /things/:id -> update + * DELETE /things/:id -> destroy + */ + +'use strict'; + +// Get list of things +exports.index = function(req, res) { + res.json([{ + name : 'Development Tools', + info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, ' + + 'Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' + }, { + name : 'Server and Client integration', + info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' + }, { + name : 'Smart Build System', + info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. ' + + 'Automatic injection of scripts and styles into your index.html' + }, { + name : 'Modular Structure', + info : 'Best practice client and server structures allow for more code reusability and ' + + 'maximum scalability' + }, { + name : 'Optimized Build', + info : 'Build process packs up your templates as a single JavaScript payload, minifies ' + + 'your scripts/css/images, and rewrites asset names for caching.' + }, { + name : 'Deployment Ready', + info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift ' + + 'subgenerators' + }]); +}; diff --git a/app/templates/server/api/thing/thing.integration.js b/app/templates/server/api/thing/thing.integration.js index de7ed7ff5..3eb5d05d8 100644 --- a/app/templates/server/api/thing/thing.integration.js +++ b/app/templates/server/api/thing/thing.integration.js @@ -1,7 +1,7 @@ 'use strict'; var app = require('../../app'); -var request = require('supertest');<% if (filters.mongoose) { %> +var request = require('supertest');<% if (filters.models) { %> var newThing;<% } %> @@ -28,7 +28,7 @@ describe('Thing API:', function() { things.should.be.instanceOf(Array); }); - });<% if (filters.mongoose) { %> + });<% if (filters.models) { %> describe('POST /api/things', function() { beforeEach(function(done) { @@ -130,7 +130,7 @@ describe('Thing API:', function() { }); }); - it('should respond with 404 when thing does not exsist', function(done) { + it('should respond with 404 when thing does not exist', function(done) { request(app) .delete('/api/things/' + newThing._id) .expect(404) diff --git a/app/templates/server/api/thing/thing.model(mongoose).js b/app/templates/server/api/thing/thing.model(mongooseModels).js similarity index 100% rename from app/templates/server/api/thing/thing.model(mongoose).js rename to app/templates/server/api/thing/thing.model(mongooseModels).js diff --git a/app/templates/server/api/thing/thing.model(sequelizeModels).js b/app/templates/server/api/thing/thing.model(sequelizeModels).js new file mode 100644 index 000000000..8e8072da4 --- /dev/null +++ b/app/templates/server/api/thing/thing.model(sequelizeModels).js @@ -0,0 +1,15 @@ +'use strict'; + +module.exports = function(sequelize, DataTypes) { + return sequelize.define('Thing', { + _id: { + type: DataTypes.INTEGER, + allowNull: false, + primaryKey: true, + autoIncrement: true + }, + name: DataTypes.STRING, + info: DataTypes.STRING, + active: DataTypes.BOOLEAN + }); +}; diff --git a/app/templates/server/api/thing/thing.socket(socketio).js b/app/templates/server/api/thing/thing.socket(socketio).js index 2d0171cdc..d0d98a6ca 100644 --- a/app/templates/server/api/thing/thing.socket(socketio).js +++ b/app/templates/server/api/thing/thing.socket(socketio).js @@ -3,15 +3,24 @@ */ 'use strict'; +<% if (filters.mongooseModels) { %> +var thing = require('./thing.model');<% } %><% if (filters.sequelizeModels) { %> +var thing = require('../../sqldb').Thing;<% } %> -var thing = require('./thing.model'); - -exports.register = function(socket) { - thing.schema.post('save', function(doc) { +exports.register = function(socket) {<% if (filters.sequelizeModels) { %> + thing.hook('afterCreate', function(doc, fields, fn) { onSave(socket, doc); + fn(null); + });<% } %> + <% if (filters.mongooseModels) { %>thing.schema.post('save', function(doc) {<% } + if (filters.sequelizeModels) { %>thing.hook('afterUpdate', function(doc, fields, fn) {<% } %> + onSave(socket, doc);<% if (filters.sequelizeModels) { %> + fn(null);<% } %> }); - thing.schema.post('remove', function(doc) { - onRemove(socket, doc); + <% if (filters.mongooseModels) { %>thing.schema.post('remove', function(doc) {<% } + if (filters.sequelizeModels) { %>thing.hook('afterDestroy', function(doc, fields, fn) {<% } %> + onRemove(socket, doc);<% if (filters.sequelizeModels) { %> + fn(null);<% } %> }); }; diff --git a/app/templates/server/api/user(auth)/user.controller.js b/app/templates/server/api/user(auth)/user.controller.js index 793da0255..8b7f4e0b8 100644 --- a/app/templates/server/api/user(auth)/user.controller.js +++ b/app/templates/server/api/user(auth)/user.controller.js @@ -1,6 +1,9 @@ 'use strict'; - -var User = require('./user.model'); +<% if (filters.mongooseModels) { %> +var User = require('./user.model');<% } %><% if (filters.sequelizeModels) { %> +var _ = require('lodash'); +var sqldb = require('../../sqldb'); +var User = sqldb.User;<% } %> var passport = require('passport'); var config = require('../../config/environment'); var jwt = require('jsonwebtoken'); @@ -31,7 +34,16 @@ function respondWith(res, statusCode) { * restriction: 'admin' */ exports.index = function(req, res) { - User.findAsync({}, '-salt -hashedPassword') + <% if (filters.mongooseModels) { %>User.findAsync({}, '-salt -hashedPassword')<% } + if (filters.sequelizeModels) { %>User.findAll({ + attributes: [ + '_id', + 'name', + 'email', + 'role', + 'provider' + ] + })<% } %> .then(function(users) { res.json(200, users); }) @@ -42,11 +54,16 @@ exports.index = function(req, res) { * Creates a new user */ exports.create = function(req, res, next) { - var newUser = new User(req.body); + <% if (filters.mongooseModels) { %>var newUser = new User(req.body); newUser.provider = 'local'; newUser.role = 'user'; - newUser.saveAsync() - .spread(function(user) { + newUser.saveAsync()<% } + if (filters.sequelizeModels) { %>var newUser = User.build(req.body); + newUser.setDataValue('provider', 'local'); + newUser.setDataValue('role', 'user'); + newUser.save()<% } %> + <% if (filters.mongooseModels) { %>.spread(function(user) {<% } + if (filters.sequelizeModels) { %>.then(function(user) {<% } %> var token = jwt.sign({ _id: user._id }, config.secrets.session, { expiresInMinutes: 60 * 5 }); @@ -61,7 +78,12 @@ exports.create = function(req, res, next) { exports.show = function(req, res, next) { var userId = req.params.id; - User.findByIdAsync(userId) + <% if (filters.mongooseModels) { %>User.findByIdAsync(userId)<% } + if (filters.sequelizeModels) { %>User.find({ + where: { + _id: userId + } + })<% } %> .then(function(user) { if (!user) { return res.send(401); @@ -78,7 +100,8 @@ exports.show = function(req, res, next) { * restriction: 'admin' */ exports.destroy = function(req, res) { - User.findByIdAndRemoveAsync(req.params.id) + <% if (filters.mongooseModels) { %>User.findByIdAndRemoveAsync(req.params.id)<% } + if (filters.sequelizeModels) { %>User.destroy({ _id: req.params.id })<% } %> .then(respondWith(res, 204)) .catch(handleError(res)); }; @@ -91,12 +114,18 @@ exports.changePassword = function(req, res, next) { var oldPass = String(req.body.oldPassword); var newPass = String(req.body.newPassword); - User.findByIdAsync(userId) + <% if (filters.mongooseModels) { %>User.findByIdAsync(userId)<% } + if (filters.sequelizeModels) { %>User.find({ + where: { + _id: userId + } + })<% } %> .then(function(user) { if (user.authenticate(oldPass)) { user.password = newPass; - return user.saveAsync() - .spread(respondWith(res, 200)) + <% if (filters.mongooseModels) { %>return user.saveAsync()<% } + if (filters.sequelizeModels) { %>return user.save()<% } %> + .then(respondWith(res, 200)) .catch(validationError(res)); } else { return res.send(403); @@ -110,7 +139,19 @@ exports.changePassword = function(req, res, next) { exports.me = function(req, res, next) { var userId = req.user._id; - User.findOneAsync({ _id: userId }, '-salt -hashedPassword') + <% if (filters.mongooseModels) { %>User.findOneAsync({ _id: userId }, '-salt -hashedPassword')<% } + if (filters.sequelizeModels) { %>User.find({ + where: { + _id: userId + }, + attributes: [ + '_id', + 'name', + 'email', + 'role', + 'provider' + ] + })<% } %> .then(function(user) { // don't ever give out the password or salt if (!user) { return res.json(401); } res.json(user); diff --git a/app/templates/server/api/user(auth)/user.integration.js b/app/templates/server/api/user(auth)/user.integration.js index ee97d3051..35bcdb573 100644 --- a/app/templates/server/api/user(auth)/user.integration.js +++ b/app/templates/server/api/user(auth)/user.integration.js @@ -1,7 +1,8 @@ 'use strict'; -var app = require('../../app'); -var User = require('./user.model'); +var app = require('../../app');<% if (filters.mongooseModels) { %> +var User = require('./user.model');<% } %><% if (filters.sequelizeModels) { %> +var User = require('../../sqldb').User;<% } %> var request = require('supertest'); describe('User API:', function() { @@ -9,25 +10,33 @@ describe('User API:', function() { // Clear users before testing before(function(done) { - User.remove(function() { - user = new User({ + <% if (filters.mongooseModels) { %>User.remove(function() {<% } + if (filters.sequelizeModels) { %>User.destroy().then(function() {<% } %> + <% if (filters.mongooseModels) { %>user = new User({<% } + if (filters.sequelizeModels) { %>user = User.build({<% } %> name: 'Fake User', email: 'test@test.com', password: 'password' }); - user.save(function(err) { + <% if (filters.mongooseModels) { %>user.save(function(err) { if (err) { return done(err); } done(); - }); + });<% } + if (filters.sequelizeModels) { %>user.save().then(function() { + done(); + }, function(err) { + return done(err); + });<% } %> }); }); // Clear users after testing after(function() { - return User.remove().exec(); + <% if (filters.mongooseModels) { %>return User.remove().exec();<% } + if (filters.sequelizeModels) { %>return User.destroy();<% } %> }); describe('GET /api/users/me', function() { @@ -55,7 +64,7 @@ describe('User API:', function() { .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { - res.body._id.should.equal(user._id.toString()); + res.body._id.toString().should.equal(user._id.toString()); done(); }); }); diff --git a/app/templates/server/api/user(auth)/user.model.js b/app/templates/server/api/user(auth)/user.model(mongooseModels).js similarity index 100% rename from app/templates/server/api/user(auth)/user.model.js rename to app/templates/server/api/user(auth)/user.model(mongooseModels).js diff --git a/app/templates/server/api/user(auth)/user.model(sequelizeModels).js b/app/templates/server/api/user(auth)/user.model(sequelizeModels).js new file mode 100644 index 000000000..af593157a --- /dev/null +++ b/app/templates/server/api/user(auth)/user.model(sequelizeModels).js @@ -0,0 +1,235 @@ +'use strict'; + +var crypto = require('crypto');<% if (filters.oauth) { %> +var authTypes = ['github', 'twitter', 'facebook', 'google'];<% } %> + +var validatePresenceOf = function(value) { + return value && value.length; +}; + +module.exports = function(sequelize, DataTypes) { + var User = sequelize.define('User', { + + _id: { + type: DataTypes.INTEGER, + allowNull: false, + primaryKey: true, + autoIncrement: true + }, + name: DataTypes.STRING, + email: { + type: DataTypes.STRING, + unique: { + msg: 'The specified email address is already in use.' + }, + validate: { + isEmail: true + } + }, + role: { + type: DataTypes.STRING, + defaultValue: 'user' + }, + password: { + type: DataTypes.STRING, + validate: { + notEmpty: true + } + }, + provider: DataTypes.STRING, + salt: DataTypes.STRING<% if (filters.oauth) { %>,<% if (filters.facebookAuth) { %> + facebook: DataTypes.TEXT,<% } %><% if (filters.twitterAuth) { %> + twitter: DataTypes.TEXT,<% } %><% if (filters.googleAuth) { %> + google: DataTypes.TEXT,<% } %> + github: DataTypes.TEXT<% } %> + + }, { + + /** + * Virtual Getters + */ + getterMethods: { + // Public profile information + profile: function() { + return { + 'name': this.name, + 'role': this.role + }; + }, + + // Non-sensitive info we'll be putting in the token + token: function() { + return { + '_id': this._id, + 'role': this.role + }; + } + }, + + /** + * Pre-save hooks + */ + hooks: { + beforeBulkCreate: function(users, fields, fn) { + var totalUpdated = 0; + users.forEach(function(user) { + user.updatePassword(function(err) { + if (err) { + return fn(err); + } + totalUpdated += 1; + if (totalUpdated === users.length) { + return fn(); + } + }); + }); + }, + beforeCreate: function(user, fields, fn) { + user.updatePassword(fn); + }, + beforeUpdate: function(user, fields, fn) { + if (user.changed('password')) { + user.updatePassword(fn); + } + } + }, + + /** + * Instance Methods + */ + instanceMethods: { + /** + * Authenticate - check if the passwords are the same + * + * @param {String} password + * @param {Function} callback + * @return {Boolean} + * @api public + */ + authenticate: function(password, callback) { + if (!callback) { + return this.password === this.encryptPassword(password); + } + + var _this = this; + this.encryptPassword(password, function(err, pwdGen) { + if (err) { + callback(err); + } + + if (_this.password === pwdGen) { + callback(null, true); + } + else { + callback(null, false); + } + }); + }, + + /** + * Make salt + * + * @param {Number} byteSize Optional salt byte size, default to 16 + * @param {Function} callback + * @return {String} + * @api public + */ + makeSalt: function(byteSize, callback) { + var defaultByteSize = 16; + + if (typeof arguments[0] === 'function') { + callback = arguments[0]; + byteSize = defaultByteSize; + } + else if (typeof arguments[1] === 'function') { + callback = arguments[1]; + } + + if (!byteSize) { + byteSize = defaultByteSize; + } + + if (!callback) { + return crypto.randomBytes(byteSize).toString('base64'); + } + + return crypto.randomBytes(byteSize, function(err, salt) { + if (err) { + callback(err); + } + return callback(null, salt.toString('base64')); + }); + }, + + /** + * Encrypt password + * + * @param {String} password + * @param {Function} callback + * @return {String} + * @api public + */ + encryptPassword: function(password, callback) { + if (!password || !this.salt) { + if (!callback) { + return null; + } + return callback(null); + } + + var defaultIterations = 10000; + var defaultKeyLength = 64; + var salt = new Buffer(this.salt, 'base64'); + + if (!callback) { + return crypto.pbkdf2Sync(password, salt, defaultIterations, defaultKeyLength) + .toString('base64'); + } + + return crypto.pbkdf2(password, salt, defaultIterations, defaultKeyLength, + function(err, key) { + if (err) { + callback(err); + } + return callback(null, key.toString('base64')); + }); + }, + + /** + * Update password field + * + * @param {Function} fn + * @return {String} + * @api public + */ + updatePassword: function(fn) { + // Handle new/update passwords + if (this.password) { + if (!validatePresenceOf(this.password)<% if (filters.oauth) { %> && authTypes.indexOf(this.provider) === -1<% } %>) { + fn(new Error('Invalid password')); + } + + // Make salt with a callback + var _this = this; + this.makeSalt(function(saltErr, salt) { + if (saltErr) { + fn(saltErr); + } + _this.salt = salt; + _this.encryptPassword(_this.password, function(encryptErr, hashedPassword) { + if (encryptErr) { + fn(encryptErr); + } + _this.password = hashedPassword; + fn(null); + }); + }); + } else { + fn(null); + } + } + } + }); + + return User; +}; diff --git a/app/templates/server/api/user(auth)/user.model.spec.js b/app/templates/server/api/user(auth)/user.model.spec(mongooseModels).js similarity index 100% rename from app/templates/server/api/user(auth)/user.model.spec.js rename to app/templates/server/api/user(auth)/user.model.spec(mongooseModels).js diff --git a/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js b/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js new file mode 100644 index 000000000..88156151b --- /dev/null +++ b/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js @@ -0,0 +1,52 @@ +'use strict'; + +var app = require('../../app'); +var User = require('../../sqldb').User; + +var userTemplate = { + provider: 'local', + name: 'Fake User', + email: 'test@test.com', + password: 'password' +}; + +var user = User.build(userTemplate); + +describe('User Model', function() { + before(function() { + // Sync and clear users before testing + User.sync().then(function() { + return User.destroy(); + }); + }); + + afterEach(function() { + return User.destroy(); + }); + + it('should begin with no users', function() { + return User.findAll() + .should.eventually.have.length(0); + }); + + it('should fail when saving a duplicate user', function() { + return user.save() + .then(function() { + var userDup = User.build(userTemplate); + return userDup.save(); + }).should.be.rejected; + }); + + it('should fail when saving without an email', function() { + user.email = ''; + return user.save().should.be.rejected; + }); + + it('should authenticate user if password is valid', function() { + user.authenticate('password').should.be.true; + }); + + it('should not authenticate user if password is invalid', function() { + user.authenticate('blah').should.not.be.true; + }); +}); diff --git a/app/templates/server/app.js b/app/templates/server/app.js index 632dd134f..7c668a881 100644 --- a/app/templates/server/app.js +++ b/app/templates/server/app.js @@ -8,16 +8,17 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development'; var express = require('express');<% if (filters.mongoose) { %> -var mongoose = require('mongoose-bird')();<% } %> +var mongoose = require('mongoose-bird')();<% } %><% if (filters.sequelize) { %> +var sqldb = require('./sqldb');<% } %> var config = require('./config/environment'); <% if (filters.mongoose) { %> -// Connect to database +// Connect to MongoDB mongoose.connect(config.mongo.uri, config.mongo.options); - -// Populate DB with sample data +<% } %><% if (filters.models) { %> +// Populate databases with sample data if (config.seedDB) { require('./config/seed'); } - -<% } %>// Setup server +<% } %> +// Setup server var app = express(); var server = require('http').createServer(app);<% if (filters.socketio) { %> var socketio = require('socket.io')(server, { @@ -29,9 +30,19 @@ require('./config/express')(app); require('./routes')(app); // Start server -server.listen(config.port, config.ip, function() { - console.log('Express server listening on %d, in %s mode', config.port, app.get('env')); -}); - +function startServer() { + server.listen(config.port, config.ip, function() { + console.log('Express server listening on %d, in %s mode', config.port, app.get('env')); + }); +} +<% if (filters.sequelize) { %> +sqldb.sequelize.sync() + .then(startServer) + .catch(function(err) { + console.log('Server failed to start due to error: %s', err); + }); +<% } else { %> +setImmediate(startServer); +<% } %> // Expose app exports = module.exports = app; diff --git a/app/templates/server/auth(auth)/auth.service.js b/app/templates/server/auth(auth)/auth.service.js index 43ea2851f..5b9a6d66f 100644 --- a/app/templates/server/auth(auth)/auth.service.js +++ b/app/templates/server/auth(auth)/auth.service.js @@ -1,12 +1,13 @@ 'use strict'; - -var mongoose = require('mongoose-bird')(); +<% if (filters.mongooseModels) { %> +var mongoose = require('mongoose-bird')();<% } %> var passport = require('passport'); var config = require('../config/environment'); var jwt = require('jsonwebtoken'); var expressJwt = require('express-jwt'); -var compose = require('composable-middleware'); -var User = require('../api/user/user.model'); +var compose = require('composable-middleware');<% if (filters.mongooseModels) { %> +var User = require('../api/user/user.model');<% } %><% if (filters.sequelizeModels) { %> +var User = require('../sqldb').User;<% } %> var validateJwt = expressJwt({ secret: config.secrets.session }); @@ -27,7 +28,12 @@ function isAuthenticated() { }) // Attach user to request .use(function(req, res, next) { - User.findByIdAsync(req.user._id) + <% if (filters.mongooseModels) { %>User.findByIdAsync(req.user._id)<% } + if (filters.sequelizeModels) { %>User.find({ + where: { + _id: req.user._id + } + })<% } %> .then(function(user) { if (!user) { return res.send(401); diff --git a/app/templates/server/auth(auth)/facebook(facebookAuth)/passport.js b/app/templates/server/auth(auth)/facebook(facebookAuth)/passport.js index b29d74c69..92911b22f 100644 --- a/app/templates/server/auth(auth)/facebook(facebookAuth)/passport.js +++ b/app/templates/server/auth(auth)/facebook(facebookAuth)/passport.js @@ -8,31 +8,35 @@ exports.setup = function(User, config) { callbackURL: config.facebook.callbackURL }, function(accessToken, refreshToken, profile, done) { - User.findOne({ + <% if (filters.mongooseModels) { %>User.findOneAsync({<% } + if (filters.sequelizeModels) { %>User.find({<% } %> 'facebook.id': profile.id - }, - function(err, user) { - if (err) { - return done(err); - } - if (!user) { - user = new User({ - name: profile.displayName, - email: profile.emails[0].value, - role: 'user', - username: profile.username, - provider: 'facebook', - facebook: profile._json - }); - user.save(function(err) { - if (err) { - done(err); - } - return done(err, user); - }); - } else { - return done(err, user); - } }) + .then(function(user) { + if (!user) { + <% if (filters.mongooseModels) { %>user = new User({<% } + if (filters.sequelizeModels) { %>user = User.build({<% } %> + name: profile.displayName, + email: profile.emails[0].value, + role: 'user', + username: profile.username, + provider: 'facebook', + facebook: profile._json + }); + <% if (filters.mongooseModels) { %>user.saveAsync()<% } + if (filters.sequelizeModels) { %>user.save()<% } %> + .then(function(user) { + return done(null, user); + }) + .catch(function(err) { + return done(err); + }); + } else { + return done(null, user); + } + }) + .catch(function(err) { + return done(err); + }); })); }; diff --git a/app/templates/server/auth(auth)/google(googleAuth)/passport.js b/app/templates/server/auth(auth)/google(googleAuth)/passport.js index 0e7a3602d..0c9462a8d 100644 --- a/app/templates/server/auth(auth)/google(googleAuth)/passport.js +++ b/app/templates/server/auth(auth)/google(googleAuth)/passport.js @@ -8,27 +8,35 @@ exports.setup = function(User, config) { callbackURL: config.google.callbackURL }, function(accessToken, refreshToken, profile, done) { - User.findOne({ + <% if (filters.mongooseModels) { %>User.findOneAsync({<% } + if (filters.sequelizeModels) { %>User.find({<% } %> 'google.id': profile.id - }, function(err, user) { - if (!user) { - user = new User({ - name: profile.displayName, - email: profile.emails[0].value, - role: 'user', - username: profile.username, - provider: 'google', - google: profile._json - }); - user.save(function(err) { - if (err) { - done(err); - } - return done(err, user); - }); - } else { - return done(err, user); - } - }); + }) + .then(function(user) { + if (!user) { + <% if (filters.mongooseModels) { %>user = new User({<% } + if (filters.sequelizeModels) { %>user = User.build({<% } %> + name: profile.displayName, + email: profile.emails[0].value, + role: 'user', + username: profile.username, + provider: 'google', + google: profile._json + }); + <% if (filters.mongooseModels) { %>user.saveAsync()<% } + if (filters.sequelizeModels) { %>user.save()<% } %> + .then(function(user) { + return done(null, user); + }) + .catch(function(err) { + return done(err); + }); + } else { + return done(null, user); + } + }) + .catch(function(err) { + return done(err); + }); })); }; diff --git a/app/templates/server/auth(auth)/index.js b/app/templates/server/auth(auth)/index.js index b930ad93f..75ddfdcb8 100644 --- a/app/templates/server/auth(auth)/index.js +++ b/app/templates/server/auth(auth)/index.js @@ -2,8 +2,9 @@ var express = require('express'); var passport = require('passport'); -var config = require('../config/environment'); -var User = require('../api/user/user.model'); +var config = require('../config/environment');<% if (filters.mongooseModels) { %> +var User = require('../api/user/user.model');<% } %><% if (filters.sequelizeModels) { %> +var User = require('../sqldb').User;<% } %> // Passport Configuration require('./local/passport').setup(User, config);<% if (filters.facebookAuth) { %> diff --git a/app/templates/server/auth(auth)/local/passport.js b/app/templates/server/auth(auth)/local/passport.js index f4898b73a..2bd3366f8 100644 --- a/app/templates/server/auth(auth)/local/passport.js +++ b/app/templates/server/auth(auth)/local/passport.js @@ -1,18 +1,16 @@ var passport = require('passport'); var LocalStrategy = require('passport-local').Strategy; -exports.setup = function(User, config) { - passport.use(new LocalStrategy({ - usernameField: 'email', - passwordField: 'password' // this is the virtual field on the model - }, function(email, password, done) { - User.findOne({ +function localAuthenticate(User, email, password, done) { + <% if (filters.mongooseModels) { %>User.findOneAsync({ + email: email.toLowerCase() + })<% } + if (filters.sequelizeModels) { %>User.find({ + where: { email: email.toLowerCase() - }, function(err, user) { - if (err) { - return done(err); - } - + } + })<% } %> + .then(function(user) { if (!user) { return done(null, false, { message: 'This email is not registered.' @@ -30,6 +28,17 @@ exports.setup = function(User, config) { return done(null, user); } }); + }) + .catch(function(err) { + return done(err); }); - })); +} + +exports.setup = function(User, config) { + passport.use(new LocalStrategy({ + usernameField: 'email', + passwordField: 'password' // this is the virtual field on the model + }, function(email, password, done) {<% if (filters.models) { %> + return localAuthenticate(User, email, password, done); +<% } %> })); }; diff --git a/app/templates/server/auth(auth)/twitter(twitterAuth)/passport.js b/app/templates/server/auth(auth)/twitter(twitterAuth)/passport.js index 7428e8afd..bf23bd3ba 100644 --- a/app/templates/server/auth(auth)/twitter(twitterAuth)/passport.js +++ b/app/templates/server/auth(auth)/twitter(twitterAuth)/passport.js @@ -8,29 +8,34 @@ exports.setup = function(User, config) { callbackURL: config.twitter.callbackURL }, function(token, tokenSecret, profile, done) { - User.findOne({ + <% if (filters.mongooseModels) { %>User.findOneAsync({<% } + if (filters.sequelizeModels) { %>User.find({<% } %> 'twitter.id_str': profile.id - }, function(err, user) { - if (err) { + }) + .then(function(user) { + if (!user) { + <% if (filters.mongooseModels) { %>user = new User({<% } + if (filters.sequelizeModels) { %>user = User.build({<% } %> + name: profile.displayName, + username: profile.username, + role: 'user', + provider: 'twitter', + twitter: profile._json + }); + <% if (filters.mongooseModels) { %>user.saveAsync()<% } + if (filters.sequelizeModels) { %>user.save()<% } %> + .then(function(user) { + return done(null, user); + }) + .catch(function(err) { + return done(err); + }); + } else { + return done(null, user); + } + }) + .catch(function(err) { return done(err); - } - if (!user) { - user = new User({ - name: profile.displayName, - username: profile.username, - role: 'user', - provider: 'twitter', - twitter: profile._json - }); - user.save(function(err) { - if (err) { - return done(err); - } - return done(err, user); - }); - } else { - return done(err, user); - } - }); + }); })); }; diff --git a/app/templates/server/config/environment/development.js b/app/templates/server/config/environment/development.js index fb33d6eab..20656595b 100644 --- a/app/templates/server/config/environment/development.js +++ b/app/templates/server/config/environment/development.js @@ -7,6 +7,16 @@ module.exports = { mongo: { uri: 'mongodb://localhost/<%= _.slugify(appname) %>-dev' }, + sequelize: { + uri: 'sqlite://', + options: { + logging: false, + storage: 'dev.sqlite', + define: { + timestamps: false + } + } + }, seedDB: true }; diff --git a/app/templates/server/config/environment/test.js b/app/templates/server/config/environment/test.js index 6ead6e1aa..021938424 100644 --- a/app/templates/server/config/environment/test.js +++ b/app/templates/server/config/environment/test.js @@ -6,5 +6,15 @@ module.exports = { // MongoDB connection options mongo: { uri: 'mongodb://localhost/<%= _.slugify(appname) %>-test' + }, + sequelize: { + uri: 'sqlite://', + options: { + logging: false, + storage: 'test.sqlite', + define: { + timestamps: false + } + } } }; diff --git a/app/templates/server/config/express.js b/app/templates/server/config/express.js index f44999c1e..9a2018263 100644 --- a/app/templates/server/config/express.js +++ b/app/templates/server/config/express.js @@ -15,9 +15,9 @@ var errorHandler = require('errorhandler'); var path = require('path'); var config = require('./environment');<% if (filters.auth) { %> var passport = require('passport');<% } %><% if (filters.twitterAuth) { %> -var session = require('express-session'); +var session = require('express-session');<% if (filters.mongoose) { %> var mongoStore = require('connect-mongo')(session); -var mongoose = require('mongoose-bird')();<% } %> +var mongoose = require('mongoose-bird')();<% } %><% } %> module.exports = function(app) { var env = app.get('env'); @@ -33,13 +33,13 @@ module.exports = function(app) { app.use(cookieParser());<% if (filters.auth) { %> app.use(passport.initialize());<% } %><% if (filters.twitterAuth) { %> - // Persist sessions with mongoStore + // Persist sessions with mongoStore / sequelizeStore // We need to enable sessions for passport twitter because its an oauth 1.0 strategy app.use(session({ secret: config.secrets.session, resave: true, - saveUninitialized: true, - store: new mongoStore({ mongoose_connection: mongoose.connection }) + saveUninitialized: true<% if (filters.mongoose) { %>, + store: new mongoStore({ mongoose_connection: mongoose.connection })<% } %> })); <% } %> app.set('appPath', path.join(config.root, 'client')); diff --git a/app/templates/server/config/seed(mongoose).js b/app/templates/server/config/seed(models).js similarity index 64% rename from app/templates/server/config/seed(mongoose).js rename to app/templates/server/config/seed(models).js index 1f6945d8d..635658010 100644 --- a/app/templates/server/config/seed(mongoose).js +++ b/app/templates/server/config/seed(models).js @@ -4,13 +4,22 @@ */ 'use strict'; - +<% if (filters.mongooseModels) { %> var Thing = require('../api/thing/thing.model'); <% if (filters.auth) { %>var User = require('../api/user/user.model');<% } %> - -Thing.find({}).removeAsync() +<% } %><% if (filters.sequelizeModels) { %> +var sqldb = require('../sqldb'); +var Thing = sqldb.Thing; +<% if (filters.auth) { %>var User = sqldb.User;<% } %> +<% } %> +<% if (filters.mongooseModels) { %>Thing.find({}).removeAsync()<% } + if (filters.sequelizeModels) { %>Thing.sync() + .then(function() { + return Thing.destroy(); + })<% } %> .then(function() { - Thing.create({ + <% if (filters.mongooseModels) { %>Thing.create({<% } + if (filters.sequelizeModels) { %>Thing.bulkCreate([{<% } %> name : 'Development Tools', info : 'Integration with popular tools such as Bower, Grunt, Karma, ' + 'Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, ' + @@ -37,12 +46,18 @@ Thing.find({}).removeAsync() name : 'Deployment Ready', info : 'Easily deploy your app to Heroku or Openshift with the heroku ' + 'and openshift subgenerators' - }); + <% if (filters.mongooseModels) { %>});<% } + if (filters.sequelizeModels) { %>}]);<% } %> }); <% if (filters.auth) { %> -User.find({}).removeAsync() +<% if (filters.mongooseModels) { %>User.find({}).removeAsync()<% } + if (filters.sequelizeModels) { %>User.sync() + .then(function() { + User.destroy(); + })<% } %> .then(function() { - User.create({ + <% if (filters.mongooseModels) { %>User.createAsync({<% } + if (filters.sequelizeModels) { %>User.bulkCreate([{<% } %> provider: 'local', name: 'Test User', email: 'test@test.com', @@ -53,8 +68,9 @@ User.find({}).removeAsync() name: 'Admin', email: 'admin@admin.com', password: 'admin' - }, function() { + <% if (filters.mongooseModels) { %>})<% } + if (filters.sequelizeModels) { %>}])<% } %> + .then(function() { console.log('finished populating users'); }); - }); -<% } %> \ No newline at end of file + });<% } %> diff --git a/app/templates/server/sqldb(sequelize)/index.js b/app/templates/server/sqldb(sequelize)/index.js new file mode 100644 index 000000000..231db9823 --- /dev/null +++ b/app/templates/server/sqldb(sequelize)/index.js @@ -0,0 +1,35 @@ +/** + * Sequelize initialization module + */ + +'use strict'; + +var path = require('path'); +var config = require('../config/environment'); + +var Sequelize = require('sequelize'); + +var db = { + Sequelize: Sequelize, + sequelize: new Sequelize(config.sequelize.uri, config.sequelize.options) +}; +<% if (filters.sequelizeModels) { %> +db.Thing = db.sequelize.import(path.join( + config.root, + 'server', + 'api', + 'thing', + 'thing.model' +)); +<% if (filters.auth) { %> +db.User = db.sequelize.import(path.join( + config.root, + 'server', + 'api', + 'user', + 'user.model' +)); +<% } %><% } %> +// Insert models below + +module.exports = db; diff --git a/endpoint/index.js b/endpoint/index.js index d67f78e51..f870c265f 100644 --- a/endpoint/index.js +++ b/endpoint/index.js @@ -25,11 +25,24 @@ Generator.prototype.askFor = function askFor() { name = name + 's'; } + var self = this; var prompts = [ { name: 'route', message: 'What will the url of your endpoint to be?', default: base + name + }, + { + type: 'list', + name: 'models', + message: 'What would you like to use for the endpoint\'s models?', + choices: [ 'Mongoose', 'Sequelize' ], + filter: function( val ) { + return val.toLowerCase(); + }, + when: function() { + return self.filters.mongoose && self.filters.sequelize; + } } ]; @@ -39,6 +52,16 @@ Generator.prototype.askFor = function askFor() { } this.route = props.route; + + if (props.models) { + delete this.filters.mongoose; + delete this.filters.mongooseModels; + delete this.filters.sequelize; + delete this.filters.sequelizeModels; + + this.filters[props.models] = true; + this.filters[props.models + 'Models'] = true; + } done(); }.bind(this)); }; @@ -67,6 +90,25 @@ Generator.prototype.registerEndpoint = function registerEndpoint() { ngUtil.rewriteFile(socketConfig); } } + + if (this.filters.sequelize) { + if (this.config.get('insertModels')) { + var modelConfig = { + file: this.config.get('registerModelsFile'), + needle: this.config.get('modelsNeedle'), + splicable: [ + "db." + this.classedName + " = db.sequelize.import(path.join(\n" + + " config.root,\n" + + " 'server',\n" + + " 'api',\n" + + " '" + this.name + "',\n" + + " '" + this.name + ".model'\n" + + "));" + ] + }; + ngUtil.rewriteFile(modelConfig); + } + } }; Generator.prototype.createFiles = function createFiles() { diff --git a/endpoint/templates/name.model(sequelize).js b/endpoint/templates/name.model(sequelize).js new file mode 100644 index 000000000..051c5daf2 --- /dev/null +++ b/endpoint/templates/name.model(sequelize).js @@ -0,0 +1,15 @@ +'use strict'; + +module.exports = function(sequelize, DataTypes) { + return sequelize.define('<%= classedName %>', { + _id: { + type: DataTypes.INTEGER, + allowNull: false, + primaryKey: true, + autoIncrement: true + }, + name: DataTypes.STRING, + info: DataTypes.STRING, + active: DataTypes.BOOLEAN + }); +}; diff --git a/endpoint/templates/name.socket(socketio).js b/endpoint/templates/name.socket(socketio).js index 260c80720..e55dfcac0 100644 --- a/endpoint/templates/name.socket(socketio).js +++ b/endpoint/templates/name.socket(socketio).js @@ -3,15 +3,24 @@ */ 'use strict'; +<% if (filters.mongoose) { %> +var <%= classedName %> = require('./<%= name %>.model');<% } %><% if (filters.sequelize) { %> +var <%= classedName %> = require('../../sqldb').<%= classedName %>;<% } %> -var <%= classedName %> = require('./<%= name %>.model'); - -exports.register = function(socket) { - <%= classedName %>.schema.post('save', function(doc) { +exports.register = function(socket) {<% if (filters.sequelize) { %> + <%= classedName %>.hook('afterCreate', function(doc, fields, fn) { onSave(socket, doc); + fn(null); + });<% } %> + <% if (filters.mongoose) { %><%= classedName %>.schema.post('save', function(doc) {<% } + if (filters.sequelize) { %><%= classedName %>.hook('afterUpdate', function(doc, fields, fn) {<% } %> + onSave(socket, doc);<% if (filters.sequelize) { %> + fn(null);<% } %> }); - <%= classedName %>.schema.post('remove', function(doc) { - onRemove(socket, doc); + <% if (filters.mongoose) { %><%= classedName %>.schema.post('remove', function(doc) {<% } + if (filters.sequelize) { %><%= classedName %>.hook('afterDestroy', function(doc, fields, fn) {<% } %> + onRemove(socket, doc);<% if (filters.sequelize) { %> + fn(null);<% } %> }); }; diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 6164b690b..0e227efef 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -18,7 +18,7 @@ describe('angular-fullstack generator', function () { chai: 'expect', bootstrap: true, uibootstrap: true, - mongoose: true, + odms: [ 'mongoose' ], auth: true, oauth: [], socketio: true @@ -153,7 +153,8 @@ describe('angular-fullstack generator', function () { var script = mapping.script[ops.script], markup = mapping.markup[ops.markup], - stylesheet = mapping.stylesheet[ops.stylesheet]; + stylesheet = mapping.stylesheet[ops.stylesheet], + models = ops.models ? ops.models : ops.odms[0]; /* Core Files */ files = files.concat([ @@ -224,14 +225,21 @@ describe('angular-fullstack generator', function () { ]); } - /* Mongoose */ - if (ops.mongoose) { + /* Models - Mongoose or Sequelize */ + if (models) { files = files.concat([ 'server/api/thing/thing.model.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([ @@ -475,7 +483,79 @@ describe('angular-fullstack generator', function () { stylesheet: 'less', router: 'uirouter', testing: 'jasmine', - mongoose: true, + 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 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 () { + helpers.assertFile(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 = { + script: 'js', + markup: 'jade', + stylesheet: 'stylus', + router: 'uirouter', + testing: 'jasmine', + odms: [ 'sequelize' ], auth: true, oauth: ['twitterAuth', 'facebookAuth', 'googleAuth'], socketio: true, @@ -548,7 +628,7 @@ describe('angular-fullstack generator', function () { router: 'ngroute', testing: 'mocha', chai: 'should', - mongoose: false, + odms: [], auth: false, oauth: [], socketio: false, @@ -621,7 +701,7 @@ describe('angular-fullstack generator', function () { stylesheet: 'css', router: 'ngroute', testing: 'jasmine', - mongoose: false, + odms: [], auth: false, oauth: [], socketio: false, From 38c0d7cf68a74bc7f2bcf8b43697a30b695e2066 Mon Sep 17 00:00:00 2001 From: dotch Date: Thu, 27 Nov 2014 14:07:02 +0100 Subject: [PATCH 061/171] fix: return a 404 when no user is found fixes #711 --- app/templates/server/api/user(auth)/user.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/server/api/user(auth)/user.controller.js b/app/templates/server/api/user(auth)/user.controller.js index 793da0255..8c25c85f6 100644 --- a/app/templates/server/api/user(auth)/user.controller.js +++ b/app/templates/server/api/user(auth)/user.controller.js @@ -64,7 +64,7 @@ exports.show = function(req, res, next) { User.findByIdAsync(userId) .then(function(user) { if (!user) { - return res.send(401); + return res.send(404); } res.json(user.profile); }) From bb4d92e3050c2ca08a571f32632dab50ac430b5b Mon Sep 17 00:00:00 2001 From: Mark Chapman Date: Wed, 31 Dec 2014 16:31:11 +0000 Subject: [PATCH 062/171] fix(gen:build): Adds missing slash fix(gen:build): Adds missing slash Adds missing slash --- app/templates/Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index 6755db958..6cf80f394 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -137,7 +137,7 @@ module.exports = function (grunt) { '{.tmp,<%%= yeoman.client %>}/{app,components}/**/*.css', '{.tmp,<%%= yeoman.client %>}/{app,components}/**/*.html', '{.tmp,<%%= yeoman.client %>}/{app,components}/**/*.js', - '!{.tmp,<%%= yeoman.client %>}{app,components}/**/*.spec.js', + '!{.tmp,<%%= yeoman.client %>}/{app,components}/**/*.spec.js', '!{.tmp,<%%= yeoman.client %>}/{app,components}/**/*.mock.js', '<%%= yeoman.client %>/assets/images/{,*//*}*.{png,jpg,jpeg,gif,webp,svg}' ], From af69a682f60daf9842df388877f4876d4fae9178 Mon Sep 17 00:00:00 2001 From: Tyler Henkel Date: Fri, 23 Jan 2015 00:10:58 -0700 Subject: [PATCH 063/171] style(jscs): update jscs definition to use settings closer to the angular repo --- app/templates/.jscs.json | 42 ++++++++++++++++--- .../api/thing/thing.controller(noModels).js | 34 +++++++-------- .../server/config/environment/index.js | 12 +++--- app/templates/server/config/seed(models).js | 24 +++++------ 4 files changed, 72 insertions(+), 40 deletions(-) diff --git a/app/templates/.jscs.json b/app/templates/.jscs.json index 1c278d0c4..31df3b6cb 100644 --- a/app/templates/.jscs.json +++ b/app/templates/.jscs.json @@ -1,12 +1,44 @@ { - "preset": "google", "maximumLineLength": { "value": 100, "allowComments": true, "allowRegex": true }, - "requireCamelCaseOrUpperCaseIdentifiers": "ignoreProperties", - "requireSpacesInsideObjectBrackets": "all", - "disallowSpacesInsideObjectBrackets": "nested", - "requireSpacesInConditionalExpression": true + "disallowMixedSpacesAndTabs": true, + "disallowMultipleLineStrings": true, + "disallowNewlineBeforeBlockStatements": true, + "disallowSpaceAfterObjectKeys": true, + "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], + "disallowSpaceBeforeBinaryOperators": [","], + "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], + "disallowSpacesInAnonymousFunctionExpression": { + "beforeOpeningRoundBrace": true + }, + "disallowSpacesInFunctionDeclaration": { + "beforeOpeningRoundBrace": true + }, + "disallowSpacesInNamedFunctionExpression": { + "beforeOpeningRoundBrace": true + }, + "disallowSpacesInsideArrayBrackets": true, + "disallowSpacesInsideParentheses": true, + "disallowTrailingComma": true, + "disallowTrailingWhitespace": true, + "requireCommaBeforeLineBreak": true, + "requireLineFeedAtFileEnd": true, + "requireSpaceAfterBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"], + "requireSpaceBeforeBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"], + "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"], + "requireSpaceBeforeBlockStatements": true, + "requireSpacesInConditionalExpression": { + "afterTest": true, + "beforeConsequent": true, + "afterConsequent": true, + "beforeAlternate": true + }, + "requireSpacesInFunction": { + "beforeOpeningCurlyBrace": true + }, + "validateLineBreaks": "LF", + "validateParameterSeparator": ", " } \ No newline at end of file diff --git a/app/templates/server/api/thing/thing.controller(noModels).js b/app/templates/server/api/thing/thing.controller(noModels).js index 02b2410c5..a39041258 100644 --- a/app/templates/server/api/thing/thing.controller(noModels).js +++ b/app/templates/server/api/thing/thing.controller(noModels).js @@ -12,27 +12,27 @@ // Get list of things exports.index = function(req, res) { res.json([{ - name : 'Development Tools', - info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, ' + - 'Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' + name: 'Development Tools', + info: 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, ' + + 'Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' }, { - name : 'Server and Client integration', - info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' + name: 'Server and Client integration', + info: 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' }, { - name : 'Smart Build System', - info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. ' + - 'Automatic injection of scripts and styles into your index.html' + name: 'Smart Build System', + info: 'Build system ignores `spec` files, allowing you to keep tests alongside code. ' + + 'Automatic injection of scripts and styles into your index.html' }, { - name : 'Modular Structure', - info : 'Best practice client and server structures allow for more code reusability and ' + - 'maximum scalability' + name: 'Modular Structure', + info: 'Best practice client and server structures allow for more code reusability and ' + + 'maximum scalability' }, { - name : 'Optimized Build', - info : 'Build process packs up your templates as a single JavaScript payload, minifies ' + - 'your scripts/css/images, and rewrites asset names for caching.' + name: 'Optimized Build', + info: 'Build process packs up your templates as a single JavaScript payload, minifies ' + + 'your scripts/css/images, and rewrites asset names for caching.' }, { - name : 'Deployment Ready', - info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift ' + - 'subgenerators' + name: 'Deployment Ready', + info: 'Easily deploy your app to Heroku or Openshift with the heroku and openshift ' + + 'sub-generators' }]); }; diff --git a/app/templates/server/config/environment/index.js b/app/templates/server/config/environment/index.js index 2476a7e8c..fa98bbe03 100644 --- a/app/templates/server/config/environment/index.js +++ b/app/templates/server/config/environment/index.js @@ -39,20 +39,20 @@ var all = { safe: true } } - }, -<% if (filters.facebookAuth) { %> + }<% if (filters.facebookAuth) { %>, + facebook: { clientID: process.env.FACEBOOK_ID || 'id', clientSecret: process.env.FACEBOOK_SECRET || 'secret', callbackURL: (process.env.DOMAIN || '') + '/auth/facebook/callback' - }, -<% } %><% if (filters.twitterAuth) { %> + }<% } %><% if (filters.twitterAuth) { %>, + twitter: { clientID: process.env.TWITTER_ID || 'id', clientSecret: process.env.TWITTER_SECRET || 'secret', callbackURL: (process.env.DOMAIN || '') + '/auth/twitter/callback' - }, -<% } %><% if (filters.googleAuth) { %> + }<% } %><% if (filters.googleAuth) { %>, + google: { clientID: process.env.GOOGLE_ID || 'id', clientSecret: process.env.GOOGLE_SECRET || 'secret', diff --git a/app/templates/server/config/seed(models).js b/app/templates/server/config/seed(models).js index 635658010..6591f16bc 100644 --- a/app/templates/server/config/seed(models).js +++ b/app/templates/server/config/seed(models).js @@ -20,31 +20,31 @@ var Thing = sqldb.Thing; .then(function() { <% if (filters.mongooseModels) { %>Thing.create({<% } if (filters.sequelizeModels) { %>Thing.bulkCreate([{<% } %> - name : 'Development Tools', - info : 'Integration with popular tools such as Bower, Grunt, Karma, ' + + name: 'Development Tools', + info: 'Integration with popular tools such as Bower, Grunt, Karma, ' + 'Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, ' + 'Stylus, Sass, CoffeeScript, and Less.' }, { - name : 'Server and Client integration', - info : 'Built with a powerful and fun stack: MongoDB, Express, ' + + name: 'Server and Client integration', + info: 'Built with a powerful and fun stack: MongoDB, Express, ' + 'AngularJS, and Node.' }, { - name : 'Smart Build System', - info : 'Build system ignores `spec` files, allowing you to keep ' + + name: 'Smart Build System', + info: 'Build system ignores `spec` files, allowing you to keep ' + 'tests alongside code. Automatic injection of scripts and ' + 'styles into your index.html' }, { - name : 'Modular Structure', - info : 'Best practice client and server structures allow for more ' + + name: 'Modular Structure', + info: 'Best practice client and server structures allow for more ' + 'code reusability and maximum scalability' }, { - name : 'Optimized Build', - info : 'Build process packs up your templates as a single JavaScript ' + + name: 'Optimized Build', + info: 'Build process packs up your templates as a single JavaScript ' + 'payload, minifies your scripts/css/images, and rewrites asset ' + 'names for caching.' }, { - name : 'Deployment Ready', - info : 'Easily deploy your app to Heroku or Openshift with the heroku ' + + name: 'Deployment Ready', + info: 'Easily deploy your app to Heroku or Openshift with the heroku ' + 'and openshift subgenerators' <% if (filters.mongooseModels) { %>});<% } if (filters.sequelizeModels) { %>}]);<% } %> From 061c2d148819dff66da9abe8f8f25dd476cefe05 Mon Sep 17 00:00:00 2001 From: Kevin Woo Date: Sat, 11 Apr 2015 00:31:12 -0700 Subject: [PATCH 064/171] feat:app: grunt-build-control to v0.4.0 Update grunt-build-control to v0.4.0 --- app/templates/_package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/_package.json b/app/templates/_package.json index 1666ea8b0..98cd616ac 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -64,7 +64,7 @@ "grunt-protractor-runner": "^1.1.0", "grunt-injector": "~0.5.4", "grunt-karma": "~0.8.2", - "grunt-build-control": "DaftMonk/grunt-build-control",<% if (filters.sass) { %> + "grunt-build-control": "~0.4.0",<% if (filters.sass) { %> "grunt-contrib-sass": "^0.7.3",<% } %><% if (filters.stylus) { %> "grunt-contrib-stylus": "latest",<% } %> "jit-grunt": "^0.5.0", From 366d03297e3cc76928a7fa7aaac0e209baad2221 Mon Sep 17 00:00:00 2001 From: Denis Ciccale Date: Tue, 2 Jun 2015 11:28:46 +0200 Subject: [PATCH 065/171] chore(admin): splice user without iteration --- .../client/app/admin(auth)/admin.controller(coffee).coffee | 2 +- .../client/app/admin(auth)/admin.controller(js).js | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/templates/client/app/admin(auth)/admin.controller(coffee).coffee b/app/templates/client/app/admin(auth)/admin.controller(coffee).coffee index 7a16032da..5183df059 100644 --- a/app/templates/client/app/admin(auth)/admin.controller(coffee).coffee +++ b/app/templates/client/app/admin(auth)/admin.controller(coffee).coffee @@ -9,4 +9,4 @@ angular.module '<%= scriptAppName %>' $scope.delete = (user) -> User.remove id: user._id - _.remove $scope.users, user \ No newline at end of file + $scope.users.splice @$index, 1 diff --git a/app/templates/client/app/admin(auth)/admin.controller(js).js b/app/templates/client/app/admin(auth)/admin.controller(js).js index 51a52b759..3cbfd4b7f 100644 --- a/app/templates/client/app/admin(auth)/admin.controller(js).js +++ b/app/templates/client/app/admin(auth)/admin.controller(js).js @@ -8,10 +8,6 @@ angular.module('<%= scriptAppName %>') $scope.delete = function(user) { User.remove({ id: user._id }); - angular.forEach($scope.users, function(u, i) { - if (u === user) { - $scope.users.splice(i, 1); - } - }); + $scope.users.splice(this.$index, 1); }; }); From d2811661fbaba81e9c33a9837f7c1828c225a5af Mon Sep 17 00:00:00 2001 From: kingcody Date: Sat, 20 Jun 2015 04:57:05 -0400 Subject: [PATCH 066/171] chore(test): update chai related test libs --- app/templates/_package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/templates/_package.json b/app/templates/_package.json index 98cd616ac..8549daba0 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -76,11 +76,11 @@ "connect-livereload": "~0.4.0", "grunt-mocha-test": "~0.10.2", "grunt-mocha-istanbul": "^2.0.0", - "chai-as-promised": "^4.1.1", + "chai-as-promised": "^5.1.0", "chai-things": "^0.2.0", - "sinon-chai": "^2.5.0",<% if (filters.mocha) { %> + "sinon-chai": "^2.8.0",<% if (filters.mocha) { %> "karma-mocha": "^0.1.9", - "karma-chai-plugins": "^0.2.3",<% } if (filters.jasmine) { %> + "karma-chai-plugins": "^0.6.0",<% } if (filters.jasmine) { %> "karma-jasmine": "~0.1.5",<% } %> "karma-ng-scenario": "~0.1.0", "karma-firefox-launcher": "~0.1.3", From 8df999229c5984b33fcccddd5335f2aeffc4e8c9 Mon Sep 17 00:00:00 2001 From: kingcody Date: Sat, 20 Jun 2015 05:42:40 -0400 Subject: [PATCH 067/171] fix(test): update sequelize destroy usage --- .../login/login.spec(jasmine).js | 2 +- .../account(auth)/login/login.spec(mocha).js | 4 ++-- .../logout/logout.spec(jasmine).js | 2 +- .../logout/logout.spec(mocha).js | 4 ++-- .../signup/signup.spec(jasmine).js | 2 +- .../signup/signup.spec(mocha).js | 4 ++-- .../server/api/user(auth)/user.integration.js | 23 ++++++------------- .../user.model.spec(sequelizeModels).js | 6 ++--- 8 files changed, 19 insertions(+), 28 deletions(-) diff --git a/app/templates/e2e/account(auth)/login/login.spec(jasmine).js b/app/templates/e2e/account(auth)/login/login.spec(jasmine).js index e690c2601..c738f9370 100644 --- a/app/templates/e2e/account(auth)/login/login.spec(jasmine).js +++ b/app/templates/e2e/account(auth)/login/login.spec(jasmine).js @@ -20,7 +20,7 @@ describe('Login View', function() { beforeEach(function(done) { <% if (filters.mongooseModels) { %>UserModel.removeAsync()<% } - if (filters.sequelizeModels) { %>UserModel.destroy()<% } %> + if (filters.sequelizeModels) { %>UserModel.destroy({ where: {} })<% } %> .then(function() { <% if (filters.mongooseModels) { %>return UserModel.createAsync(testUser);<% } if (filters.sequelizeModels) { %>return UserModel.create(testUser);<% } %> diff --git a/app/templates/e2e/account(auth)/login/login.spec(mocha).js b/app/templates/e2e/account(auth)/login/login.spec(mocha).js index 3335cf3b4..6b3158726 100644 --- a/app/templates/e2e/account(auth)/login/login.spec(mocha).js +++ b/app/templates/e2e/account(auth)/login/login.spec(mocha).js @@ -21,7 +21,7 @@ describe('Login View', function() { before(function() { return UserModel <% if (filters.mongooseModels) { %>.removeAsync()<% } - if (filters.sequelizeModels) { %>.destroy()<% } %> + if (filters.sequelizeModels) { %>.destroy({ where: {} })<% } %> .then(function() { <% if (filters.mongooseModels) { %>return UserModel.createAsync(testUser);<% } if (filters.sequelizeModels) { %>return UserModel.create(testUser);<% } %> @@ -31,7 +31,7 @@ describe('Login View', function() { after(function() { <% if (filters.mongooseModels) { %>return UserModel.removeAsync();<% } - if (filters.sequelizeModels) { %>return UserModel.destroy();<% } %> + if (filters.sequelizeModels) { %>return UserModel.destroy({ where: {} });<% } %> }); it('should include login form with correct inputs and submit button', function() { diff --git a/app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js b/app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js index f3149865c..ae4bb23a0 100644 --- a/app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js +++ b/app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js @@ -18,7 +18,7 @@ describe('Logout View', function() { beforeEach(function(done) { <% if (filters.mongooseModels) { %>UserModel.removeAsync()<% } - if (filters.sequelizeModels) { %>UserModel.destroy()<% } %> + if (filters.sequelizeModels) { %>UserModel.destroy({ where: {} })<% } %> .then(function() { <% if (filters.mongooseModels) { %>return UserModel.createAsync(testUser);<% } if (filters.sequelizeModels) { %>return UserModel.create(testUser);<% } %> diff --git a/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js b/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js index 85d92cf75..ef9f6d4da 100644 --- a/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js +++ b/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js @@ -19,7 +19,7 @@ describe('Logout View', function() { beforeEach(function() { return UserModel <% if (filters.mongooseModels) { %>.removeAsync()<% } - if (filters.sequelizeModels) { %>.destroy()<% } %> + if (filters.sequelizeModels) { %>.destroy({ where: {} })<% } %> .then(function() { <% if (filters.mongooseModels) { %>return UserModel.createAsync(testUser);<% } if (filters.sequelizeModels) { %>return UserModel.create(testUser);<% } %> @@ -31,7 +31,7 @@ describe('Logout View', function() { after(function() { <% if (filters.mongooseModels) { %>return UserModel.removeAsync();<% } - if (filters.sequelizeModels) { %>return UserModel.destroy();<% } %> + if (filters.sequelizeModels) { %>return UserModel.destroy({ where: {} });<% } %> }) describe('with local auth', function() { diff --git a/app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js b/app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js index abbfc98a4..511a84933 100644 --- a/app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js +++ b/app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js @@ -37,7 +37,7 @@ describe('Signup View', function() { it('should signup a new user, log them in, and redirecting to "/"', function(done) { <% if (filters.mongooseModels) { %>UserModel.remove(function() {<% } - if (filters.sequelizeModels) { %>UserModel.destroy().then(function() {<% } %> + if (filters.sequelizeModels) { %>UserModel.destroy({ where: {} }).then(function() {<% } %> page.signup(testUser); var navbar = require('../../components/navbar/navbar.po'); diff --git a/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js b/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js index 5351ea75d..852a3cb71 100644 --- a/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js +++ b/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js @@ -24,7 +24,7 @@ describe('Signup View', function() { after(function() { <% if (filters.mongooseModels) { %>return UserModel.removeAsync();<% } - if (filters.sequelizeModels) { %>return UserModel.destroy();<% } %> + if (filters.sequelizeModels) { %>return UserModel.destroy({ where: {} });<% } %> }); it('should include signup form with correct inputs and submit button', function() { @@ -42,7 +42,7 @@ describe('Signup View', function() { it('should signup a new user, log them in, and redirecting to "/"', function(done) { <% if (filters.mongooseModels) { %>UserModel.remove(function() {<% } - if (filters.sequelizeModels) { %>UserModel.destroy().then(function() {<% } %> + if (filters.sequelizeModels) { %>UserModel.destroy({ where: {} }).then(function() {<% } %> page.signup(testUser); var navbar = require('../../components/navbar/navbar.po'); diff --git a/app/templates/server/api/user(auth)/user.integration.js b/app/templates/server/api/user(auth)/user.integration.js index 35bcdb573..5273be72a 100644 --- a/app/templates/server/api/user(auth)/user.integration.js +++ b/app/templates/server/api/user(auth)/user.integration.js @@ -9,9 +9,9 @@ describe('User API:', function() { var user; // Clear users before testing - before(function(done) { - <% if (filters.mongooseModels) { %>User.remove(function() {<% } - if (filters.sequelizeModels) { %>User.destroy().then(function() {<% } %> + before(function() { + return <% if (filters.mongooseModels) { %>User.removeAsync().then(function() {<% } + if (filters.sequelizeModels) { %>User.destroy({ where: {} }).then(function() {<% } %> <% if (filters.mongooseModels) { %>user = new User({<% } if (filters.sequelizeModels) { %>user = User.build({<% } %> name: 'Fake User', @@ -19,24 +19,15 @@ describe('User API:', function() { password: 'password' }); - <% if (filters.mongooseModels) { %>user.save(function(err) { - if (err) { - return done(err); - } - done(); - });<% } - if (filters.sequelizeModels) { %>user.save().then(function() { - done(); - }, function(err) { - return done(err); - });<% } %> + return <% if (filters.mongooseModels) { %>user.saveAsync();<% } + if (filters.sequelizeModels) { %>user.save();<% } %> }); }); // Clear users after testing after(function() { - <% if (filters.mongooseModels) { %>return User.remove().exec();<% } - if (filters.sequelizeModels) { %>return User.destroy();<% } %> + <% if (filters.mongooseModels) { %>return User.removeAsync();<% } + if (filters.sequelizeModels) { %>return User.destroy({ where: {} });<% } %> }); describe('GET /api/users/me', function() { diff --git a/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js b/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js index 88156151b..f499667cd 100644 --- a/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js +++ b/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js @@ -15,13 +15,13 @@ var user = User.build(userTemplate); describe('User Model', function() { before(function() { // Sync and clear users before testing - User.sync().then(function() { - return User.destroy(); + return User.sync().then(function() { + return User.destroy({ where: {} }); }); }); afterEach(function() { - return User.destroy(); + return User.destroy({ where: {} }); }); it('should begin with no users', function() { From c7f6c360dca99c7741695158a5b6cfda969e3594 Mon Sep 17 00:00:00 2001 From: kingcody Date: Sat, 20 Jun 2015 06:41:12 -0400 Subject: [PATCH 068/171] fix(test): change `protractor.getInstance` to `browser` --- app/templates/e2e/account(auth)/login/login.spec(jasmine).js | 2 +- app/templates/e2e/account(auth)/login/login.spec(mocha).js | 2 +- app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js | 2 +- app/templates/e2e/account(auth)/logout/logout.spec(mocha).js | 2 +- app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js | 2 +- app/templates/e2e/account(auth)/signup/signup.spec(mocha).js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/templates/e2e/account(auth)/login/login.spec(jasmine).js b/app/templates/e2e/account(auth)/login/login.spec(jasmine).js index c738f9370..8b31d4d6b 100644 --- a/app/templates/e2e/account(auth)/login/login.spec(jasmine).js +++ b/app/templates/e2e/account(auth)/login/login.spec(jasmine).js @@ -1,6 +1,6 @@ 'use strict'; -var config = protractor.getInstance().params;<% if (filters.mongooseModels) { %> +var config = browser.params;<% if (filters.mongooseModels) { %> var UserModel = require(config.serverConfig.root + '/server/api/user/user.model');<% } %><% if (filters.sequelizeModels) { %> var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> diff --git a/app/templates/e2e/account(auth)/login/login.spec(mocha).js b/app/templates/e2e/account(auth)/login/login.spec(mocha).js index 6b3158726..57222c1e4 100644 --- a/app/templates/e2e/account(auth)/login/login.spec(mocha).js +++ b/app/templates/e2e/account(auth)/login/login.spec(mocha).js @@ -1,6 +1,6 @@ 'use strict'; -var config = protractor.getInstance().params;<% if (filters.mongooseModels) { %> +var config = browser.params;<% if (filters.mongooseModels) { %> var UserModel = require(config.serverConfig.root + '/server/api/user/user.model');<% } %><% if (filters.sequelizeModels) { %> var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> diff --git a/app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js b/app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js index ae4bb23a0..fc10a1684 100644 --- a/app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js +++ b/app/templates/e2e/account(auth)/logout/logout.spec(jasmine).js @@ -1,6 +1,6 @@ 'use strict'; -var config = protractor.getInstance().params;<% if (filters.mongooseModels) { %> +var config = browser.params;<% if (filters.mongooseModels) { %> var UserModel = require(config.serverConfig.root + '/server/api/user/user.model');<% } %><% if (filters.sequelizeModels) { %> var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> diff --git a/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js b/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js index ef9f6d4da..5268456de 100644 --- a/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js +++ b/app/templates/e2e/account(auth)/logout/logout.spec(mocha).js @@ -1,6 +1,6 @@ 'use strict'; -var config = protractor.getInstance().params;<% if (filters.mongooseModels) { %> +var config = browser.params;<% if (filters.mongooseModels) { %> var UserModel = require(config.serverConfig.root + '/server/api/user/user.model');<% } %><% if (filters.sequelizeModels) { %> var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> diff --git a/app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js b/app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js index 511a84933..1613d3ae8 100644 --- a/app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js +++ b/app/templates/e2e/account(auth)/signup/signup.spec(jasmine).js @@ -1,6 +1,6 @@ 'use strict'; -var config = protractor.getInstance().params;<% if (filters.mongooseModels) { %> +var config = browser.params;<% if (filters.mongooseModels) { %> var UserModel = require(config.serverConfig.root + '/server/api/user/user.model');<% } %><% if (filters.sequelizeModels) { %> var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> diff --git a/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js b/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js index 852a3cb71..e4855cc11 100644 --- a/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js +++ b/app/templates/e2e/account(auth)/signup/signup.spec(mocha).js @@ -1,6 +1,6 @@ 'use strict'; -var config = protractor.getInstance().params;<% if (filters.mongooseModels) { %> +var config = browser.params;<% if (filters.mongooseModels) { %> var UserModel = require(config.serverConfig.root + '/server/api/user/user.model');<% } %><% if (filters.sequelizeModels) { %> var UserModel = require(config.serverConfig.root + '/server/sqldb').User;<% } %> From df481888dd9300762c7a817ec29d47f24b924cdd Mon Sep 17 00:00:00 2001 From: kingcody Date: Sat, 20 Jun 2015 01:53:27 -0400 Subject: [PATCH 069/171] test(model): refactor tests to expose model update bugs Both mongoose and sequelize user model implementations have undesired effects when performing an update. --- .../user.model.spec(mongooseModels).js | 60 +++++++++++++------ .../user.model.spec(sequelizeModels).js | 56 +++++++++++------ 2 files changed, 81 insertions(+), 35 deletions(-) diff --git a/app/templates/server/api/user(auth)/user.model.spec(mongooseModels).js b/app/templates/server/api/user(auth)/user.model.spec(mongooseModels).js index 16de6e2de..1aad3b25e 100644 --- a/app/templates/server/api/user(auth)/user.model.spec(mongooseModels).js +++ b/app/templates/server/api/user(auth)/user.model.spec(mongooseModels).js @@ -2,22 +2,29 @@ var app = require('../../app'); var User = require('./user.model'); - -var user = new User({ - provider: 'local', - name: 'Fake User', - email: 'test@test.com', - password: 'password' -}); +var user; +var genUser = function() { + user = new User({ + provider: 'local', + name: 'Fake User', + email: 'test@test.com', + password: 'password' + }); + return user; +}; describe('User Model', function() { before(function() { // Clear users before testing - return User.remove().exec(); + return User.removeAsync(); + }); + + beforeEach(function() { + genUser(); }); afterEach(function() { - return User.remove().exec(); + return User.removeAsync(); }); it('should begin with no users', function() { @@ -28,21 +35,38 @@ describe('User Model', function() { it('should fail when saving a duplicate user', function() { return user.saveAsync() .then(function() { - var userDup = new User(user); + var userDup = genUser(); return userDup.saveAsync(); }).should.be.rejected; }); - it('should fail when saving without an email', function() { - user.email = ''; - return user.saveAsync().should.be.rejected; + describe('#email', function() { + it('should fail when saving without an email', function() { + user.email = ''; + return user.saveAsync().should.be.rejected; + }); }); - it('should authenticate user if password is valid', function() { - user.authenticate('password').should.be.true; - }); + describe('#password', function() { + beforeEach(function() { + return user.saveAsync(); + }); + + it('should authenticate user if valid', function() { + user.authenticate('password').should.be.true; + }); - it('should not authenticate user if password is invalid', function() { - user.authenticate('blah').should.not.be.true; + it('should not authenticate user if invalid', function() { + user.authenticate('blah').should.not.be.true; + }); + + it('should remain the same hash unless the password is updated', function() { + user.name = 'Test User'; + return user.saveAsync() + .spread(function(u) { + return u.authenticate('password'); + }).should.eventually.be.true; + }); }); + }); diff --git a/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js b/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js index f499667cd..a7af1bd38 100644 --- a/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js +++ b/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js @@ -2,16 +2,17 @@ var app = require('../../app'); var User = require('../../sqldb').User; - -var userTemplate = { - provider: 'local', - name: 'Fake User', - email: 'test@test.com', - password: 'password' +var user; +var genUser = function() { + user = User.build({ + provider: 'local', + name: 'Fake User', + email: 'test@test.com', + password: 'password' + }); + return user; }; -var user = User.build(userTemplate); - describe('User Model', function() { before(function() { // Sync and clear users before testing @@ -20,6 +21,10 @@ describe('User Model', function() { }); }); + beforeEach(function() { + genUser(); + }); + afterEach(function() { return User.destroy({ where: {} }); }); @@ -32,21 +37,38 @@ describe('User Model', function() { it('should fail when saving a duplicate user', function() { return user.save() .then(function() { - var userDup = User.build(userTemplate); + var userDup = genUser(); return userDup.save(); }).should.be.rejected; }); - it('should fail when saving without an email', function() { - user.email = ''; - return user.save().should.be.rejected; + describe('#email', function() { + it('should fail when saving without an email', function() { + user.email = ''; + return user.save().should.be.rejected; + }); }); - it('should authenticate user if password is valid', function() { - user.authenticate('password').should.be.true; - }); + describe('#password', function() { + beforeEach(function() { + return user.save(); + }); + + it('should authenticate user if valid', function() { + user.authenticate('password').should.be.true; + }); - it('should not authenticate user if password is invalid', function() { - user.authenticate('blah').should.not.be.true; + it('should not authenticate user if invalid', function() { + user.authenticate('blah').should.not.be.true; + }); + + it('should remain the same hash unless the password is updated', function() { + user.name = 'Test User'; + return user.save() + .then(function(u) { + return u.authenticate('password'); + }).should.eventually.be.true; + }); }); + }); From 1805975d3a7bdd1b9a409813d3fc0d2777f492bf Mon Sep 17 00:00:00 2001 From: kingcody Date: Mon, 22 Jun 2015 02:04:38 -0400 Subject: [PATCH 070/171] fix(model): fix update bugs with mongoose and sequelize --- .../server/api/user(auth)/user.model(mongooseModels).js | 2 +- .../server/api/user(auth)/user.model(sequelizeModels).js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/templates/server/api/user(auth)/user.model(mongooseModels).js b/app/templates/server/api/user(auth)/user.model(mongooseModels).js index 201afb4bc..008412eaf 100644 --- a/app/templates/server/api/user(auth)/user.model(mongooseModels).js +++ b/app/templates/server/api/user(auth)/user.model(mongooseModels).js @@ -102,7 +102,7 @@ var validatePresenceOf = function(value) { UserSchema .pre('save', function(next) { // Handle new/update passwords - if (this.password) { + if (this.isModified('password')) { if (!validatePresenceOf(this.password)<% if (filters.oauth) { %> && authTypes.indexOf(this.provider) === -1<% } %>) { next(new Error('Invalid password')); } diff --git a/app/templates/server/api/user(auth)/user.model(sequelizeModels).js b/app/templates/server/api/user(auth)/user.model(sequelizeModels).js index af593157a..776eafc3e 100644 --- a/app/templates/server/api/user(auth)/user.model(sequelizeModels).js +++ b/app/templates/server/api/user(auth)/user.model(sequelizeModels).js @@ -89,8 +89,9 @@ module.exports = function(sequelize, DataTypes) { }, beforeUpdate: function(user, fields, fn) { if (user.changed('password')) { - user.updatePassword(fn); + return user.updatePassword(fn); } + fn(); } }, From 349b6d3e5ca01e2d44342f0d952953f30dd322e9 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Mon, 22 Jun 2015 13:43:15 -0400 Subject: [PATCH 071/171] fix(npm): Remove git diff comment Crap.... --- app/templates/_package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/app/templates/_package.json b/app/templates/_package.json index 870408f92..346ec2078 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -45,7 +45,6 @@ "grunt-contrib-imagemin": "~0.7.1", "grunt-contrib-jshint": "~0.10.0", "grunt-contrib-uglify": "~0.4.0", -<<<<<<< HEAD "grunt-contrib-watch": "~0.6.1",<% if (filters.coffee) { %> "grunt-contrib-coffee": "^0.10.1",<% } %><% if (filters.jade) { %> "grunt-contrib-jade": "^0.11.0",<% } %><% if (filters.less) { %> From 1bcffd674c776453732a242e8106f51090a9447c Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Mon, 22 Jun 2015 14:14:40 -0400 Subject: [PATCH 072/171] fix(gen:endpoint): Fix JSCS stuff and use Express 4 syntax --- endpoint/templates/name.controller.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/endpoint/templates/name.controller.js b/endpoint/templates/name.controller.js index 6a63a7ba8..4b5d985e4 100644 --- a/endpoint/templates/name.controller.js +++ b/endpoint/templates/name.controller.js @@ -11,7 +11,7 @@ function responseWithResult(res, statusCode) { statusCode = statusCode || 200; return function(entity) { if (entity) { - return res.json(statusCode, entity); + return res.status(statusCode).json(entity); } }; } @@ -41,7 +41,7 @@ function removeEntity(res) { if (entity) { return entity.removeAsync() .then(function() { - return res.send(204); + return res.status(204); }); } }; @@ -50,8 +50,8 @@ function removeEntity(res) { // Get list of <%= name %>s exports.index = function(req, res) {<% if (!filters.mongoose) { %> res.json([]);<% } %><% if (filters.mongoose) { %> - <%= classedName %>.find(function (err, <%= name %>s) { - if(err) { return handleError(res, err); } + <%= classedName %>.find(function(err, <%= name %>s) { + if (err) { return handleError(res, err); } return res.status(200).json(<%= name %>s); });<% } %> };<% if (filters.mongoose) { %> From 0af7c3ebb95edb41b1b82014d5e1501d8f4164ce Mon Sep 17 00:00:00 2001 From: kingcody Date: Tue, 23 Jun 2015 06:40:39 -0400 Subject: [PATCH 073/171] fix(endpoint): refactor handleError for promise use --- endpoint/templates/name.controller.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/endpoint/templates/name.controller.js b/endpoint/templates/name.controller.js index 4b5d985e4..7d03a42ee 100644 --- a/endpoint/templates/name.controller.js +++ b/endpoint/templates/name.controller.js @@ -3,8 +3,11 @@ var _ = require('lodash'); var <%= classedName %> = require('./<%= name %>.model'); -function handleError(res, err) { - return res.status(500).send(err); +function handleError(res, statusCode) { + statusCode = statusCode || 500; + return function(err) { + res.status(statusCode).send(err); + }; } function responseWithResult(res, statusCode) { @@ -19,7 +22,7 @@ function responseWithResult(res, statusCode) { function handleEntityNotFound(res) { return function(entity) { if (!entity) { - res.send(404); + res.status(404).end(); return null; } return entity; @@ -41,7 +44,7 @@ function removeEntity(res) { if (entity) { return entity.removeAsync() .then(function() { - return res.status(204); + res.status(204).end(); }); } }; @@ -50,10 +53,9 @@ function removeEntity(res) { // Get list of <%= name %>s exports.index = function(req, res) {<% if (!filters.mongoose) { %> res.json([]);<% } %><% if (filters.mongoose) { %> - <%= classedName %>.find(function(err, <%= name %>s) { - if (err) { return handleError(res, err); } - return res.status(200).json(<%= name %>s); - });<% } %> + <%= classedName %>.findAsync() + .then(responseWithResult(res)) + .catch(handleError(res));<% } %> };<% if (filters.mongoose) { %> // Gets a single <%= name %> from the DB. From 0ec2e1808ec15755786b7144b99fcdc5704738a6 Mon Sep 17 00:00:00 2001 From: kingcody Date: Tue, 23 Jun 2015 06:47:24 -0400 Subject: [PATCH 074/171] fix(test): remove package.json and bower.json --- test/fixtures/bower.json | 24 --------- test/fixtures/package.json | 102 ------------------------------------- 2 files changed, 126 deletions(-) delete mode 100644 test/fixtures/bower.json delete mode 100644 test/fixtures/package.json diff --git a/test/fixtures/bower.json b/test/fixtures/bower.json deleted file mode 100644 index 10dff6513..000000000 --- a/test/fixtures/bower.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "tempApp", - "version": "0.0.0", - "dependencies": { - "angular": ">=1.2.*", - "json3": "~3.3.1", - "es5-shim": "~3.0.1", - "bootstrap-sass-official": "~3.1.1", - "bootstrap": "~3.1.1", - "angular-resource": ">=1.2.*", - "angular-cookies": ">=1.2.*", - "angular-sanitize": ">=1.2.*", - "angular-route": ">=1.2.*", - "angular-bootstrap": "~0.11.0", - "font-awesome": ">=4.1.0", - "lodash": "~2.4.1", - "angular-socket-io": "~0.6.0", - "angular-ui-router": "~0.2.10" - }, - "devDependencies": { - "angular-mocks": ">=1.2.*", - "angular-scenario": ">=1.2.*" - } -} diff --git a/test/fixtures/package.json b/test/fixtures/package.json deleted file mode 100644 index c110f7838..000000000 --- a/test/fixtures/package.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "name": "tempApp", - "version": "0.0.0", - "main": "server/app.js", - "dependencies": { - "express": "~4.9.0", - "morgan": "~1.0.0", - "body-parser": "~1.5.0", - "method-override": "~1.0.0", - "serve-favicon": "~2.0.1", - "cookie-parser": "~1.0.1", - "express-session": "~1.0.2", - "errorhandler": "~1.0.0", - "compression": "~1.0.1", - "lodash": "~2.4.1", - "jade": "~1.2.0", - "ejs": "~0.8.4", - "mongoose": "~3.8.8", - "jsonwebtoken": "^0.3.0", - "express-jwt": "^0.1.3", - "passport": "~0.2.0", - "passport-local": "~0.1.6", - "passport-facebook": "latest", - "passport-twitter": "latest", - "passport-google-oauth": "latest", - "composable-middleware": "^0.3.0", - "connect-mongo": "^0.4.1", - "socket.io": "^1.0.6", - "socket.io-client": "^1.0.6", - "socketio-jwt": "^2.0.2" - }, - "devDependencies": { - "grunt": "~0.4.4", - "grunt-autoprefixer": "~0.7.2", - "grunt-wiredep": "~1.8.0", - "grunt-concurrent": "~0.5.0", - "grunt-contrib-clean": "~0.5.0", - "grunt-contrib-concat": "~0.4.0", - "grunt-contrib-copy": "~0.5.0", - "grunt-contrib-cssmin": "~0.9.0", - "grunt-contrib-htmlmin": "~0.2.0", - "grunt-contrib-imagemin": "~0.7.1", - "grunt-contrib-jshint": "~0.10.0", - "grunt-contrib-uglify": "~0.4.0", - "grunt-contrib-watch": "~0.6.1", - "grunt-contrib-coffee": "^0.10.1", - "grunt-contrib-jade": "^0.11.0", - "grunt-contrib-less": "^0.11.0", - "karma-babel-preprocessor": "^5.2.1", - "grunt-babel": "~5.0.0", - "grunt-google-cdn": "~0.4.0", - "grunt-newer": "~0.7.0", - "grunt-ng-annotate": "^0.2.3", - "grunt-rev": "~0.1.0", - "grunt-svgmin": "~0.4.0", - "grunt-usemin": "~2.1.1", - "grunt-env": "~0.4.1", - "grunt-node-inspector": "~0.1.5", - "grunt-nodemon": "~0.2.0", - "grunt-angular-templates": "^0.5.4", - "grunt-dom-munger": "^3.4.0", - "grunt-protractor-runner": "^1.1.0", - "grunt-injector": "~0.5.4", - "grunt-karma": "~0.8.2", - "grunt-build-control": "DaftMonk/grunt-build-control", - "grunt-mocha-test": "~0.10.2", - "grunt-contrib-sass": "^0.7.3", - "grunt-contrib-stylus": "latest", - "jit-grunt": "^0.5.0", - "time-grunt": "~0.3.1", - "grunt-express-server": "~0.4.17", - "grunt-open": "~0.2.3", - "open": "~0.0.4", - "jshint-stylish": "~0.1.5", - "connect-livereload": "~0.4.0", - "karma-ng-scenario": "~0.1.0", - "karma-firefox-launcher": "~0.1.3", - "karma-script-launcher": "~0.1.0", - "karma-html2js-preprocessor": "~0.1.0", - "karma-ng-jade2js-preprocessor": "^0.1.2", - "karma-jasmine": "~0.1.5", - "karma-chrome-launcher": "~0.1.3", - "requirejs": "~2.1.11", - "karma-requirejs": "~0.2.1", - "karma-coffee-preprocessor": "~0.2.1", - "karma-jade-preprocessor": "0.0.11", - "karma-phantomjs-launcher": "~0.1.4", - "karma": "~0.12.9", - "karma-ng-html2js-preprocessor": "~0.1.0", - "supertest": "~0.11.0", - "should": "~3.3.1" - }, - "engines": { - "node": ">=0.10.0" - }, - "scripts": { - "start": "node server/app.js", - "test": "grunt test", - "update-webdriver": "node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update" - }, - "private": true -} From 342606c4a3447e4c12561c4b2752a89d47c99527 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Tue, 23 Jun 2015 12:15:25 -0400 Subject: [PATCH 075/171] fix(gen): Check that answers.odms exists --- app/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/index.js b/app/index.js index 29289881e..15f8f3b95 100644 --- a/app/index.js +++ b/app/index.js @@ -224,7 +224,7 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ }], function (answers) { if(answers.socketio) this.filters.socketio = true; if(answers.auth) this.filters.auth = true; - if(answers.odms.length > 0) { + if(answers.odms && answers.odms.length > 0) { var models; if(!answers.models) { models = answers.odms[0]; From dae69cff73b615b92839cc2c7014f1a96d334544 Mon Sep 17 00:00:00 2001 From: kingcody Date: Wed, 24 Jun 2015 06:58:52 -0400 Subject: [PATCH 076/171] fix(travis): remove node v0.11 from testing --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 36b85dcc5..cdaee3885 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: node_js node_js: - '0.10' - - '0.11' - '0.12' env: global: From c98cb5d3606ddd47d44cb8a5b366124bde04328c Mon Sep 17 00:00:00 2001 From: kingcody Date: Sat, 27 Jun 2015 02:13:17 -0400 Subject: [PATCH 077/171] fix(endpoint:user): refactor validationError for promise use --- .../server/api/user(auth)/user.controller.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/templates/server/api/user(auth)/user.controller.js b/app/templates/server/api/user(auth)/user.controller.js index 0286b5415..d074c666b 100644 --- a/app/templates/server/api/user(auth)/user.controller.js +++ b/app/templates/server/api/user(auth)/user.controller.js @@ -8,21 +8,24 @@ var passport = require('passport'); var config = require('../../config/environment'); var jwt = require('jsonwebtoken'); -var validationError = function(res, err) { - return res.status(422).json(err); -}; +function validationError(res, statusCode) { + statusCode = statusCode || 422; + return function(err) { + res.status(statusCode).json(err); + } +} function handleError(res, statusCode) { statusCode = statusCode || 500; return function(err) { - res.send(statusCode, err); + res.status(statusCode).send(err); }; } function respondWith(res, statusCode) { statusCode = statusCode || 200; return function() { - res.send(statusCode); + res.status(statusCode).end(); }; } From c7b48a5b6bb54968ed690d600f496ef032ee7869 Mon Sep 17 00:00:00 2001 From: kingcody Date: Sat, 27 Jun 2015 02:25:22 -0400 Subject: [PATCH 078/171] fix(endpoint:thing): use Express 4 syntax --- .../server/api/thing/thing.controller(models).js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/templates/server/api/thing/thing.controller(models).js b/app/templates/server/api/thing/thing.controller(models).js index a14fbd4b3..c7874b515 100644 --- a/app/templates/server/api/thing/thing.controller(models).js +++ b/app/templates/server/api/thing/thing.controller(models).js @@ -17,7 +17,7 @@ var Thing = sqldb.Thing;<% } %> function handleError(res, statusCode) { statusCode = statusCode || 500; return function(err) { - res.send(statusCode, err); + res.status(statusCode).send(err); }; } @@ -25,7 +25,7 @@ function responseWithResult(res, statusCode) { statusCode = statusCode || 200; return function(entity) { if (entity) { - return res.json(statusCode, entity); + return res.status(statusCode).json(entity); } }; } @@ -33,7 +33,7 @@ function responseWithResult(res, statusCode) { function handleEntityNotFound(res) { return function(entity) { if (!entity) { - res.send(404); + res.status(404).end(); return null; } return entity; @@ -58,7 +58,7 @@ function removeEntity(res) { <% if (filters.mongooseModels) { %>return entity.removeAsync()<% } if (filters.sequelizeModels) { %>return entity.destroy()<% } %> .then(function() { - return res.send(204); + return res.status(204).end(); }); } }; From 60334a8d43c2d53a4e994dad5ba201590982ae91 Mon Sep 17 00:00:00 2001 From: kingcody Date: Wed, 24 Jun 2015 05:41:37 -0400 Subject: [PATCH 079/171] feat(server): implement server-side ES6 via babel --- app/templates/Gruntfile.js | 29 +++++++--------------- app/templates/_package.json | 5 ++-- app/templates/mocha.conf.js | 5 +++- app/templates/server/index.js | 7 ++++++ test/test-file-creation.js | 45 ++--------------------------------- 5 files changed, 25 insertions(+), 66 deletions(-) create mode 100644 app/templates/server/index.js diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index 028300e68..8d2ef6c96 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -39,13 +39,13 @@ module.exports = function (grunt) { }, dev: { options: { - script: 'server/app.js', + script: 'server', debug: true } }, prod: { options: { - script: 'dist/server/app.js' + script: 'dist/server' } } }, @@ -61,7 +61,7 @@ module.exports = function (grunt) { '!<%%= yeoman.client %>/{app,components}/**/*.spec.js', '!<%%= yeoman.client %>/{app,components}/**/*.mock.js', '!<%%= yeoman.client %>/app/app.js'], - tasks: ['injector:scripts'] + tasks: [<% if(filters.babel) { %>'newer:babel:client', <% } %>'injector:scripts'] }, injectCss: { files: [ @@ -128,13 +128,6 @@ module.exports = function (grunt) { '<%%= yeoman.client %>/{app,components}/**/*.spec.{coffee,litcoffee,coffee.md}' ], tasks: ['karma'] - },<% } %><% if(filters.babel) { %> - babel: { - files: [ - '<%%= yeoman.client %>/{app,components}/**/*.js', - '!<%%= yeoman.client %>/{app,components}/**/*.spec.js' - ], - tasks: ['babel'] },<% } %> gruntfile: { files: ['Gruntfile.js'] @@ -143,11 +136,7 @@ module.exports = function (grunt) { files: [ '{.tmp,<%%= yeoman.client %>}/{app,components}/**/*.css', '{.tmp,<%%= yeoman.client %>}/{app,components}/**/*.html', - <% if(filters.babel) { %> - '.tmp/{app,components}/**/*.js', - <% } else { %> '{.tmp,<%%= yeoman.client %>}/{app,components}/**/*.js', - <% } %> '!{.tmp,<%%= yeoman.client %>}{app,components}/**/*.spec.js', '!{.tmp,<%%= yeoman.client %>}/{app,components}/**/*.mock.js', '<%%= yeoman.client %>/assets/images/{,*//*}*.{png,jpg,jpeg,gif,webp,svg}' @@ -261,7 +250,7 @@ module.exports = function (grunt) { // Use nodemon to run server in debug mode with an initial breakpoint nodemon: { debug: { - script: 'server/app.js', + script: 'server', options: { nodeArgs: ['--debug-brk'], env: { @@ -470,7 +459,7 @@ module.exports = function (grunt) { concurrent: { server: [<% if(filters.coffee) { %> 'coffee',<% } %><% if(filters.babel) { %> - 'babel',<% } %><% if(filters.jade) { %> + 'newer:babel:client',<% } %><% if(filters.jade) { %> 'jade',<% } %><% if(filters.stylus) { %> 'stylus',<% } %><% if(filters.sass) { %> 'sass',<% } %><% if(filters.less) { %> @@ -478,7 +467,7 @@ module.exports = function (grunt) { ], test: [<% if(filters.coffee) { %> 'coffee',<% } %><% if(filters.babel) { %> - 'babel',<% } %><% if(filters.jade) { %> + 'newer:babel:client',<% } %><% if(filters.jade) { %> 'jade',<% } %><% if(filters.stylus) { %> 'stylus',<% } %><% if(filters.sass) { %> 'sass',<% } %><% if(filters.less) { %> @@ -495,7 +484,7 @@ module.exports = function (grunt) { }, dist: [<% if(filters.coffee) { %> 'coffee',<% } %><% if(filters.babel) { %> - 'babel',<% } %><% if(filters.jade) { %> + 'newer:babel:client',<% } %><% if(filters.jade) { %> 'jade',<% } %><% if(filters.stylus) { %> 'stylus',<% } %><% if(filters.sass) { %> 'sass',<% } %><% if(filters.less) { %> @@ -639,10 +628,10 @@ module.exports = function (grunt) { options: { sourceMap: true }, - server: { + client: { files: [{ expand: true, - cwd: 'client', + cwd: '<%%= yeoman.client %>', src: [ '{app,components}/**/*.js', '!{app,components}/**/*.spec.js' diff --git a/app/templates/_package.json b/app/templates/_package.json index 346ec2078..1356bab64 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -12,7 +12,8 @@ "express-session": "~1.0.2", "errorhandler": "~1.0.0", "compression": "~1.0.1", - "lodash": "~2.4.1",<% if (filters.jade) { %> + "lodash": "~2.4.1",<% if(filters.babel) { %> + "babel-core": "^5.6.4",<% } %><% if (filters.jade) { %> "jade": "~1.2.0",<% } %><% if (filters.html) { %> "ejs": "~0.8.4",<% } %><% if (filters.mongoose) { %> "mongoose": "~3.8.8", @@ -106,7 +107,7 @@ "node": ">=0.10.0" }, "scripts": { - "start": "node server/app.js", + "start": "node server", "test": "grunt test", "update-webdriver": "node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update" }, diff --git a/app/templates/mocha.conf.js b/app/templates/mocha.conf.js index 54e33bb6f..6b1504e9c 100644 --- a/app/templates/mocha.conf.js +++ b/app/templates/mocha.conf.js @@ -1,4 +1,7 @@ -'use strict'; +'use strict';<% if(filters.babel) { %> + +// Register the Babel require hook +require('babel-core/register');<% } %> var chai = require('chai'); diff --git a/app/templates/server/index.js b/app/templates/server/index.js new file mode 100644 index 000000000..7722a0e6c --- /dev/null +++ b/app/templates/server/index.js @@ -0,0 +1,7 @@ +'use strict';<% if (filters.babel) { %> + +// Register the Babel require hook +require('babel-core/register');<% } %> + +// Export the application +exports = module.exports = require('./app'); diff --git a/test/test-file-creation.js b/test/test-file-creation.js index a863700be..ec794c7ab 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -11,6 +11,7 @@ var recursiveReadDir = require('recursive-readdir'); describe('angular-fullstack generator', function () { var gen, defaultOptions = { script: 'js', + babel: true, markup: 'html', stylesheet: 'sass', router: 'uirouter', @@ -176,6 +177,7 @@ describe('angular-fullstack generator', function () { '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', @@ -476,49 +478,6 @@ describe('angular-fullstack generator', function () { } }); - describe('with Babel ES6 preprocessor', function() { - beforeEach(function() { - helpers.mockPrompt(gen, { - script: 'js', - babel: true, - markup: 'jade', - stylesheet: 'less', - router: 'uirouter' - }); - }); - - it('should run client tests successfully', function(done) { - this.timeout(60000); - gen.run({}, function () { - exec('grunt test:client', function (error, stdout, stderr) { - expect(stdout, 'Client tests failed \n' + stdout ).to.contain('Executed 1 of 1 SUCCESS'); - done(); - }); - }); - }); - - it('should pass jshint', function(done) { - this.timeout(60000); - gen.run({}, function () { - exec('grunt jshint', function (error, stdout, stderr) { - expect(stdout).to.contain('Done, without errors.'); - done(); - }); - }); - }); - - it('should run server tests successfully', function(done) { - this.timeout(60000); - gen.run({}, function () { - exec('grunt test:server', function (error, stdout, stderr) { - expect(stdout, 'Server tests failed (do you have mongoDB running?) \n' + stdout).to.contain('Done, without errors.'); - done(); - }); - }); - }); - }); - - describe('with other preprocessors and oauth', function() { var testOptions = { script: 'coffee', From f87b34c005566026bfce07c9d283719706b675fc Mon Sep 17 00:00:00 2001 From: kingcody Date: Sun, 28 Jun 2015 01:50:32 -0400 Subject: [PATCH 080/171] chore(dependencies): remove unused dependency wiredep --- app/index.js | 1 - package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/app/index.js b/app/index.js index 15f8f3b95..b987d44ce 100644 --- a/app/index.js +++ b/app/index.js @@ -5,7 +5,6 @@ var util = require('util'); var genUtils = require('../util.js'); var yeoman = require('yeoman-generator'); var chalk = require('chalk'); -var wiredep = require('wiredep'); var AngularFullstackGenerator = yeoman.generators.Base.extend({ diff --git a/package.json b/package.json index c9e55b21b..0f35ef723 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "dependencies": { "yeoman-generator": "~0.17.0", "chalk": "~0.4.0", - "wiredep": "~0.4.2", "generator-ng-component": "~0.0.4" }, "peerDependencies": { From df82d171488fcff5fdbc19ba255cc6f942b3e593 Mon Sep 17 00:00:00 2001 From: kingcody Date: Sun, 5 Jul 2015 07:49:10 -0400 Subject: [PATCH 081/171] fix(endpoint): fully support sequelize models --- endpoint/templates/index.js | 2 +- endpoint/templates/index.spec.js | 6 +- endpoint/templates/name.controller.js | 71 +++++++++++++------ endpoint/templates/name.integration.js | 6 +- ...oose).js => name.model(mongooseModels).js} | 0 ...ize).js => name.model(sequelizeModels).js} | 0 6 files changed, 58 insertions(+), 27 deletions(-) rename endpoint/templates/{name.model(mongoose).js => name.model(mongooseModels).js} (100%) rename endpoint/templates/{name.model(sequelize).js => name.model(sequelizeModels).js} (100%) diff --git a/endpoint/templates/index.js b/endpoint/templates/index.js index 3f8c592a6..60198d142 100644 --- a/endpoint/templates/index.js +++ b/endpoint/templates/index.js @@ -5,7 +5,7 @@ var controller = require('./<%= name %>.controller'); var router = express.Router(); -router.get('/', controller.index);<% if(filters.mongoose) { %> +router.get('/', controller.index);<% if (filters.models) { %> router.get('/:id', controller.show); router.post('/', controller.create); router.put('/:id', controller.update); diff --git a/endpoint/templates/index.spec.js b/endpoint/templates/index.spec.js index ccd15ec7e..291a04662 100644 --- a/endpoint/templates/index.spec.js +++ b/endpoint/templates/index.spec.js @@ -3,7 +3,7 @@ var proxyquire = require('proxyquire').noPreserveCache(); var <%= cameledName %>CtrlStub = { - index: '<%= name %>Ctrl.index'<% if(filters.mongoose) { %>, + index: '<%= name %>Ctrl.index'<% if(filters.models) { %>, show: '<%= name %>Ctrl.show', create: '<%= name %>Ctrl.create', update: '<%= name %>Ctrl.update', @@ -11,7 +11,7 @@ var <%= cameledName %>CtrlStub = { }; var routerStub = { - get: sinon.spy()<% if(filters.mongoose) { %>, + get: sinon.spy()<% if(filters.models) { %>, put: sinon.spy(), patch: sinon.spy(), post: sinon.spy(), @@ -42,7 +42,7 @@ describe('<%= classedName %> API Router:', function() { .should.have.been.calledOnce; }); - });<% if(filters.mongoose) { %> + });<% if(filters.models) { %> describe('GET <%= route %>/:id', function() { diff --git a/endpoint/templates/name.controller.js b/endpoint/templates/name.controller.js index 7d03a42ee..abcc5f34b 100644 --- a/endpoint/templates/name.controller.js +++ b/endpoint/templates/name.controller.js @@ -1,7 +1,18 @@ -'use strict';<% if(filters.mongoose) { %> +/** + * Using Rails-like standard naming convention for endpoints. + * GET <%= route %> -> index<% if (filters.models) { %> + * POST <%= route %> -> create + * GET <%= route %>/:id -> show + * PUT <%= route %>/:id -> update + * DELETE <%= route %>/:id -> destroy<% } %> + */ -var _ = require('lodash'); -var <%= classedName %> = require('./<%= name %>.model'); +'use strict';<% if (filters.models) { %> + +var _ = require('lodash');<% if (filters.mongooseModels) { %> +var <%= classedName %> = require('./<%= name %>.model');<% } if (filters.sequelizeModels) { %> +var sqldb = require('../../sqldb'); +var <%= classedName %> = sqldb.<%= classedName %>;<% } %> function handleError(res, statusCode) { statusCode = statusCode || 500; @@ -14,7 +25,7 @@ function responseWithResult(res, statusCode) { statusCode = statusCode || 200; return function(entity) { if (entity) { - return res.status(statusCode).json(entity); + res.status(statusCode).json(entity); } }; } @@ -31,9 +42,11 @@ function handleEntityNotFound(res) { function saveUpdates(updates) { return function(entity) { - var updated = _.merge(entity, updates); + <% if (filters.mongooseModels) { %>var updated = _.merge(entity, updates); return updated.saveAsync() - .spread(function(updated) { + .spread(function(updated) {<% } + if (filters.sequelizeModels) { %>return entity.updateAttributes(updates) + .then(function(updated) {<% } %> return updated; }); }; @@ -42,7 +55,8 @@ function saveUpdates(updates) { function removeEntity(res) { return function(entity) { if (entity) { - return entity.removeAsync() + <% if (filters.mongooseModels) { %>return entity.removeAsync()<% } + if (filters.sequelizeModels) { %>return entity.destroy()<% } %> .then(function() { res.status(204).end(); }); @@ -50,44 +64,61 @@ function removeEntity(res) { }; }<% } %> -// Get list of <%= name %>s -exports.index = function(req, res) {<% if (!filters.mongoose) { %> - res.json([]);<% } %><% if (filters.mongoose) { %> - <%= classedName %>.findAsync() +// Gets a list of <%= name %>s +exports.index = function(req, res) {<% if (!filters.models) { %> + res.json([]);<% } else { %> + <% if (filters.mongooseModels) { %><%= classedName %>.findAsync()<% } + if (filters.sequelizeModels) { %><%= classedName %>.findAll()<% } %> .then(responseWithResult(res)) .catch(handleError(res));<% } %> -};<% if (filters.mongoose) { %> +};<% if (filters.models) { %> -// Gets a single <%= name %> from the DB. +// Gets a single <%= name %> from the DB exports.show = function(req, res) { - <%= classedName %>.findByIdAsync(req.params.id) + <% if (filters.mongooseModels) { %><%= classedName %>.findByIdAsync(req.params.id)<% } + if (filters.sequelizeModels) { %><%= classedName %>.find({ + where: { + _id: req.params.id + } + })<% } %> .then(handleEntityNotFound(res)) .then(responseWithResult(res)) .catch(handleError(res)); }; -// Creates a new <%= name %> in the DB. +// Creates a new <%= name %> in the DB exports.create = function(req, res) { - <%= classedName %>.createAsync(req.body) + <% if (filters.mongooseModels) { %><%= classedName %>.createAsync(req.body)<% } + if (filters.sequelizeModels) { %><%= classedName %>.create(req.body)<% } %> .then(responseWithResult(res, 201)) .catch(handleError(res)); }; -// Updates an existing <%= name %> in the DB. +// Updates an existing <%= name %> in the DB exports.update = function(req, res) { if (req.body._id) { delete req.body._id; } - <%= classedName %>.findByIdAsync(req.params.id) + <% if (filters.mongooseModels) { %><%= classedName %>.findByIdAsync(req.params.id)<% } + if (filters.sequelizeModels) { %><%= classedName %>.find({ + where: { + _id: req.params.id + } + })<% } %> .then(handleEntityNotFound(res)) .then(saveUpdates(req.body)) .then(responseWithResult(res)) .catch(handleError(res)); }; -// Deletes a <%= name %> from the DB. +// Deletes a <%= name %> from the DB exports.destroy = function(req, res) { - <%= classedName %>.findByIdAsync(req.params.id) + <% if (filters.mongooseModels) { %><%= classedName %>.findByIdAsync(req.params.id)<% } + if (filters.sequelizeModels) { %><%= classedName %>.find({ + where: { + _id: req.params.id + } + })<% } %> .then(handleEntityNotFound(res)) .then(removeEntity(res)) .catch(handleError(res)); diff --git a/endpoint/templates/name.integration.js b/endpoint/templates/name.integration.js index a377456a4..160cdedfa 100644 --- a/endpoint/templates/name.integration.js +++ b/endpoint/templates/name.integration.js @@ -1,7 +1,7 @@ 'use strict'; var app = require('../../app'); -var request = require('supertest');<% if(filters.mongoose) { %> +var request = require('supertest');<% if(filters.models) { %> var new<%= classedName %>;<% } %> @@ -28,7 +28,7 @@ describe('<%= classedName %> API:', function() { <%= cameledName %>s.should.be.instanceOf(Array); }); - });<% if(filters.mongoose) { %> + });<% if(filters.models) { %> describe('POST <%= route %>', function() { beforeEach(function(done) { @@ -130,7 +130,7 @@ describe('<%= classedName %> API:', function() { }); }); - it('should respond with 404 when <%= name %> does not exsist', function(done) { + it('should respond with 404 when <%= name %> does not exist', function(done) { request(app) .delete('<%= route %>/' + new<%= classedName %>._id) .expect(404) diff --git a/endpoint/templates/name.model(mongoose).js b/endpoint/templates/name.model(mongooseModels).js similarity index 100% rename from endpoint/templates/name.model(mongoose).js rename to endpoint/templates/name.model(mongooseModels).js diff --git a/endpoint/templates/name.model(sequelize).js b/endpoint/templates/name.model(sequelizeModels).js similarity index 100% rename from endpoint/templates/name.model(sequelize).js rename to endpoint/templates/name.model(sequelizeModels).js From 6dc81306fea1abcf57f293f91776e00e9d50ad26 Mon Sep 17 00:00:00 2001 From: kingcody Date: Tue, 7 Jul 2015 19:36:50 -0400 Subject: [PATCH 082/171] feat(gen): use common endpoint templates for thing route --- app/index.js | 685 +++++++++--------- app/templates/server/api/thing/index.js | 15 - app/templates/server/api/thing/index.spec.js | 97 --- .../api/thing/thing.controller(models).js | 124 ---- .../api/thing/thing.controller(noModels).js | 38 - .../server/api/thing/thing.integration.js | 147 ---- .../api/thing/thing.model(mongooseModels).js | 12 - .../api/thing/thing.model(sequelizeModels).js | 15 - .../api/thing/thing.socket(socketio).js | 33 - 9 files changed, 359 insertions(+), 807 deletions(-) delete mode 100644 app/templates/server/api/thing/index.js delete mode 100644 app/templates/server/api/thing/index.spec.js delete mode 100644 app/templates/server/api/thing/thing.controller(models).js delete mode 100644 app/templates/server/api/thing/thing.controller(noModels).js delete mode 100644 app/templates/server/api/thing/thing.integration.js delete mode 100644 app/templates/server/api/thing/thing.model(mongooseModels).js delete mode 100644 app/templates/server/api/thing/thing.model(sequelizeModels).js delete mode 100644 app/templates/server/api/thing/thing.socket(socketio).js diff --git a/app/index.js b/app/index.js index b987d44ce..5c3ef0673 100644 --- a/app/index.js +++ b/app/index.js @@ -8,379 +8,412 @@ var chalk = require('chalk'); var AngularFullstackGenerator = yeoman.generators.Base.extend({ - init: function () { - this.argument('name', { type: String, required: false }); - this.appname = this.name || path.basename(process.cwd()); - this.appname = this._.camelize(this._.slugify(this._.humanize(this.appname))); - - this.option('app-suffix', { - desc: 'Allow a custom suffix to be added to the module name', - type: String, - required: 'false' - }); - this.scriptAppName = this.appname + genUtils.appName(this); - this.appPath = this.env.options.appPath; - this.pkg = require('../package.json'); - - this.filters = {}; - - // dynamic assertion statement - this.does = this.is = function(foo) { - if (this.filters.should) { - return foo + '.should'; + initializing: { + + init: function () { + this.argument('name', { type: String, required: false }); + this.appname = this.name || path.basename(process.cwd()); + this.appname = this._.camelize(this._.slugify(this._.humanize(this.appname))); + + this.option('app-suffix', { + desc: 'Allow a custom suffix to be added to the module name', + type: String, + required: 'false' + }); + this.scriptAppName = this.appname + genUtils.appName(this); + this.appPath = this.env.options.appPath; + this.pkg = require('../package.json'); + + this.filters = {}; + + // dynamic assertion statement + this.does = this.is = function(foo) { + if (this.filters.should) { + return foo + '.should'; + } else { + return 'expect(' + foo + ').to'; + } + }.bind(this); + }, + + info: function () { + this.log(this.yeoman); + this.log('Out of the box I create an AngularJS app with an Express server.\n'); + }, + + checkForConfig: function() { + var cb = this.async(); + + if(this.config.get('filters')) { + this.prompt([{ + type: 'confirm', + name: 'skipConfig', + message: 'Existing .yo-rc configuration found, would you like to use it?', + default: true, + }], function (answers) { + this.skipConfig = answers.skipConfig; + + this.filters = this._.defaults(this.config.get('filters'), { + bootstrap: true, + uibootstrap: true, + jasmine: true + }); + + // NOTE: temp(?) fix for #403 + if(typeof this.filters.oauth==='undefined') { + var strategies = Object.keys(this.filters).filter(function(key) { + return key.match(/Auth$/) && key; + }); + + if(strategies.length) this.filters.oauth = true; + } + + this.config.forceSave(); + + cb(); + }.bind(this)); } else { - return 'expect(' + foo + ').to'; + cb(); } - }.bind(this); - }, + } - info: function () { - this.log(this.yeoman); - this.log('Out of the box I create an AngularJS app with an Express server.\n'); }, - checkForConfig: function() { - var cb = this.async(); + prompting: { - if(this.config.get('filters')) { - this.prompt([{ - type: 'confirm', - name: 'skipConfig', - message: 'Existing .yo-rc configuration found, would you like to use it?', - default: true, - }], function (answers) { - this.skipConfig = answers.skipConfig; - - this.filters = this._.defaults(this.config.get('filters'), { - bootstrap: true, - uibootstrap: true, - jasmine: true - }); - - // NOTE: temp(?) fix for #403 - if(typeof this.filters.oauth==='undefined') { - var strategies = Object.keys(this.filters).filter(function(key) { - return key.match(/Auth$/) && key; - }); - - if(strategies.length) this.filters.oauth = true; - } + clientPrompts: function() { + if(this.skipConfig) return; + var cb = this.async(); - this.config.forceSave(); + this.log('# Client\n'); - cb(); - }.bind(this)); - } else { - cb(); - } - }, + this.prompt([{ + type: 'list', + name: 'script', + message: 'What would you like to write scripts with?', + choices: [ 'JavaScript', 'CoffeeScript'], + filter: function( val ) { + var filterMap = { + 'JavaScript': 'js', + 'CoffeeScript': 'coffee' + }; + + return filterMap[val]; + } + }, { + type: 'confirm', + name: 'babel', + message: 'Would you like to use Javascript ES6 in your client by preprocessing it with Babel?', + when: function (answers) { + return answers.script === 'js'; + } + }, { + type: 'list', + name: 'markup', + message: 'What would you like to write markup with?', + choices: ['HTML', 'Jade'], + filter: function( val ) { return val.toLowerCase(); } + }, { + type: 'list', + name: 'stylesheet', + default: 1, + message: 'What would you like to write stylesheets with?', + choices: [ 'CSS', 'Sass', 'Stylus', 'Less'], + filter: function( val ) { return val.toLowerCase(); } + }, { + type: 'list', + name: 'router', + default: 1, + message: 'What Angular router would you like to use?', + choices: [ 'ngRoute', 'uiRouter'], + filter: function( val ) { return val.toLowerCase(); } + }, { + type: 'confirm', + name: 'bootstrap', + message: 'Would you like to include Bootstrap?' + }, { + type: 'confirm', + name: 'uibootstrap', + message: 'Would you like to include UI Bootstrap?', + when: function (answers) { + return answers.bootstrap; + } + }], function (answers) { + + this.filters.babel = !!answers.babel; + if(this.filters.babel){ this.filters.js = true; } + this.filters[answers.script] = true; + this.filters[answers.markup] = true; + this.filters[answers.stylesheet] = true; + this.filters[answers.router] = true; + this.filters.bootstrap = !!answers.bootstrap; + this.filters.uibootstrap = !!answers.uibootstrap; + cb(); + }.bind(this)); + }, - clientPrompts: function() { - if(this.skipConfig) return; - var cb = this.async(); + serverPrompts: function() { + if(this.skipConfig) return; + var cb = this.async(); + var self = this; - this.log('# Client\n'); + this.log('\n# Server\n'); - this.prompt([{ + this.prompt([{ + type: 'checkbox', + name: 'odms', + message: 'What would you like to use for data modeling?', + choices: [ + { + value: 'mongoose', + name: 'Mongoose (MongoDB)', + checked: true + }, + { + value: 'sequelize', + name: 'Sequelize (MySQL, SQLite, MariaDB, PostgreSQL)', + checked: false + } + ] + }, { type: 'list', - name: 'script', - message: 'What would you like to write scripts with?', - choices: [ 'JavaScript', 'CoffeeScript'], + name: 'models', + message: 'What would you like to use for the default models?', + choices: [ 'Mongoose', 'Sequelize' ], filter: function( val ) { - var filterMap = { - 'JavaScript': 'js', - 'CoffeeScript': 'coffee' - }; - - return filterMap[val]; + return val.toLowerCase(); + }, + when: function(answers) { + return answers.odms && answers.odms.length > 1; } }, { type: 'confirm', - name: 'babel', - message: 'Would you like to use Javascript ES6 in your client by preprocessing it with Babel?', + name: 'auth', + message: 'Would you scaffold out an authentication boilerplate?', when: function (answers) { - return answers.script === 'js'; + return answers.odms && answers.odms.length !== 0; } }, { - type: 'list', - name: 'markup', - message: 'What would you like to write markup with?', - choices: ['HTML', 'Jade'], - filter: function( val ) { return val.toLowerCase(); } - }, { - type: 'list', - name: 'stylesheet', - default: 1, - message: 'What would you like to write stylesheets with?', - choices: [ 'CSS', 'Sass', 'Stylus', 'Less'], - filter: function( val ) { return val.toLowerCase(); } - }, { - type: 'list', - name: 'router', - default: 1, - message: 'What Angular router would you like to use?', - choices: [ 'ngRoute', 'uiRouter'], - filter: function( val ) { return val.toLowerCase(); } - }, { - type: 'confirm', - name: 'bootstrap', - message: 'Would you like to include Bootstrap?' + type: 'checkbox', + name: 'oauth', + message: 'Would you like to include additional oAuth strategies?', + when: function (answers) { + return answers.auth; + }, + choices: [ + { + value: 'googleAuth', + name: 'Google', + checked: false + }, + { + value: 'facebookAuth', + name: 'Facebook', + checked: false + }, + { + value: 'twitterAuth', + name: 'Twitter', + checked: false + } + ] }, { type: 'confirm', - name: 'uibootstrap', - message: 'Would you like to include UI Bootstrap?', + name: 'socketio', + message: 'Would you like to use socket.io?', + // to-do: should not be dependent on ODMs when: function (answers) { - return answers.bootstrap; - } + return answers.odms && answers.odms.length !== 0; + }, + default: true }], function (answers) { + if(answers.socketio) this.filters.socketio = true; + if(answers.auth) this.filters.auth = true; + if(answers.odms && answers.odms.length > 0) { + var models; + if(!answers.models) { + models = answers.odms[0]; + } else { + models = answers.models; + } + this.filters.models = true; + this.filters[models + 'Models'] = true; + answers.odms.forEach(function(odm) { + this.filters[odm] = true; + }.bind(this)); + } else { + this.filters.noModels = true; + } + if(answers.oauth) { + if(answers.oauth.length) this.filters.oauth = true; + answers.oauth.forEach(function(oauthStrategy) { + this.filters[oauthStrategy] = true; + }.bind(this)); + } - this.filters.babel = !!answers.babel; - if(this.filters.babel){ this.filters.js = true; } - this.filters[answers.script] = true; - this.filters[answers.markup] = true; - this.filters[answers.stylesheet] = true; - this.filters[answers.router] = true; - this.filters.bootstrap = !!answers.bootstrap; - this.filters.uibootstrap = !!answers.uibootstrap; cb(); }.bind(this)); - }, + }, - serverPrompts: function() { - if(this.skipConfig) return; - var cb = this.async(); - var self = this; - - this.log('\n# Server\n'); - - this.prompt([{ - type: 'checkbox', - name: 'odms', - message: 'What would you like to use for data modeling?', - choices: [ - { - value: 'mongoose', - name: 'Mongoose (MongoDB)', - checked: true - }, - { - value: 'sequelize', - name: 'Sequelize (MySQL, SQLite, MariaDB, PostgreSQL)', - checked: false + projectPrompts: function() { + if(this.skipConfig) return; + var cb = this.async(); + var self = this; + + this.log('\n# Project\n'); + + this.prompt([{ + type: 'list', + name: 'testing', + message: 'What would you like to write tests with?', + choices: [ 'Jasmine', 'Mocha + Chai + Sinon'], + filter: function( val ) { + var filterMap = { + 'Jasmine': 'jasmine', + 'Mocha + Chai + Sinon': 'mocha' + }; + + return filterMap[val]; } - ] - }, { - type: 'list', - name: 'models', - message: 'What would you like to use for the default models?', - choices: [ 'Mongoose', 'Sequelize' ], - filter: function( val ) { - return val.toLowerCase(); - }, - when: function(answers) { - return answers.odms && answers.odms.length > 1; - } - }, { - type: 'confirm', - name: 'auth', - message: 'Would you scaffold out an authentication boilerplate?', - when: function (answers) { - return answers.odms && answers.odms.length !== 0; - } - }, { - type: 'checkbox', - name: 'oauth', - message: 'Would you like to include additional oAuth strategies?', - when: function (answers) { - return answers.auth; - }, - choices: [ - { - value: 'googleAuth', - name: 'Google', - checked: false - }, - { - value: 'facebookAuth', - name: 'Facebook', - checked: false + }, { + type: 'list', + name: 'chai', + message: 'What would you like to write Chai assertions with?', + choices: ['Expect', 'Should'], + filter: function( val ) { + return val.toLowerCase(); }, - { - value: 'twitterAuth', - name: 'Twitter', - checked: false + when: function( answers ) { + return answers.testing === 'mocha'; } - ] - }, { - type: 'confirm', - name: 'socketio', - message: 'Would you like to use socket.io?', - // to-do: should not be dependent on ODMs - when: function (answers) { - return answers.odms && answers.odms.length !== 0; - }, - default: true - }], function (answers) { - if(answers.socketio) this.filters.socketio = true; - if(answers.auth) this.filters.auth = true; - if(answers.odms && answers.odms.length > 0) { - var models; - if(!answers.models) { - models = answers.odms[0]; - } else { - models = answers.models; + }], function (answers) { + this.filters[answers.testing] = true; + if (answers.testing === 'mocha') { + this.filters.jasmine = false; + this.filters.should = false; + this.filters.expect = false; + this.filters[answers.chai] = true; + } + if (answers.testing === 'jasmine') { + this.filters.mocha = false; + this.filters.should = false; + this.filters.expect = false; } - this.filters.models = true; - this.filters[models + 'Models'] = true; - answers.odms.forEach(function(odm) { - this.filters[odm] = true; - }.bind(this)); - } else { - this.filters.noModels = true; - } - if(answers.oauth) { - if(answers.oauth.length) this.filters.oauth = true; - answers.oauth.forEach(function(oauthStrategy) { - this.filters[oauthStrategy] = true; - }.bind(this)); - } - cb(); - }.bind(this)); + cb(); + }.bind(this)); + } + }, - projectPrompts: function() { - if(this.skipConfig) return; - var cb = this.async(); - var self = this; - - this.log('\n# Project\n'); - - this.prompt([{ - type: 'list', - name: 'testing', - message: 'What would you like to write tests with?', - choices: [ 'Jasmine', 'Mocha + Chai + Sinon'], - filter: function( val ) { - var filterMap = { - 'Jasmine': 'jasmine', - 'Mocha + Chai + Sinon': 'mocha' - }; - - return filterMap[val]; - } - }, { - type: 'list', - name: 'chai', - message: 'What would you like to write Chai assertions with?', - choices: ['Expect', 'Should'], - filter: function( val ) { - return val.toLowerCase(); - }, - when: function( answers ) { - return answers.testing === 'mocha'; - } - }], function (answers) { - this.filters[answers.testing] = true; - if (answers.testing === 'mocha') { - this.filters.jasmine = false; - this.filters.should = false; - this.filters.expect = false; - this.filters[answers.chai] = true; - } - if (answers.testing === 'jasmine') { - this.filters.mocha = false; - this.filters.should = false; - this.filters.expect = false; - } + configuring: { + + saveSettings: function() { + if(this.skipConfig) return; + this.config.set('insertRoutes', true); + this.config.set('registerRoutesFile', 'server/routes.js'); + this.config.set('routesNeedle', '// Insert routes below'); + + this.config.set('routesBase', '/api/'); + this.config.set('pluralizeRoutes', true); + + this.config.set('insertSockets', true); + this.config.set('registerSocketsFile', 'server/config/socketio.js'); + this.config.set('socketsNeedle', '// Insert sockets below'); + + this.config.set('insertModels', true); + this.config.set('registerModelsFile', 'server/sqldb/index.js'); + this.config.set('modelsNeedle', '// Insert models below'); + + this.config.set('filters', this.filters); + this.config.forceSave(); + }, + + ngComponent: function() { + if(this.skipConfig) return; + var appPath = 'client/app/'; + var extensions = []; + var filters = [ + 'ngroute', + 'uirouter', + 'jasmine', + 'mocha', + 'expect', + 'should' + ].filter(function(v) {return this.filters[v];}, this); + + if(this.filters.ngroute) filters.push('ngroute'); + if(this.filters.uirouter) filters.push('uirouter'); + if(this.filters.babel) extensions.push('babel'); + if(this.filters.coffee) extensions.push('coffee'); + if(this.filters.js) extensions.push('js'); + if(this.filters.html) extensions.push('html'); + if(this.filters.jade) extensions.push('jade'); + if(this.filters.css) extensions.push('css'); + if(this.filters.stylus) extensions.push('styl'); + if(this.filters.sass) extensions.push('scss'); + if(this.filters.less) extensions.push('less'); + + this.composeWith('ng-component', { + options: { + 'routeDirectory': appPath, + 'directiveDirectory': appPath, + 'filterDirectory': appPath, + 'serviceDirectory': appPath, + 'filters': filters, + 'extensions': extensions, + 'basePath': 'client' + } + }, { local: require.resolve('generator-ng-component/app/index.js') }); + }, + + ngModules: function() { + var angModules = [ + "'ngCookies'", + "'ngResource'", + "'ngSanitize'" + ]; + if(this.filters.ngroute) angModules.push("'ngRoute'"); + if(this.filters.socketio) angModules.push("'btford.socket-io'"); + if(this.filters.uirouter) angModules.push("'ui.router'"); + if(this.filters.uibootstrap) angModules.push("'ui.bootstrap'"); + + this.angularModules = '\n ' + angModules.join(',\n ') +'\n'; + } - cb(); - }.bind(this)); }, - saveSettings: function() { - if(this.skipConfig) return; - this.config.set('insertRoutes', true); - this.config.set('registerRoutesFile', 'server/routes.js'); - this.config.set('routesNeedle', '// Insert routes below'); + default: {}, - this.config.set('routesBase', '/api/'); - this.config.set('pluralizeRoutes', true); + writing: { - this.config.set('insertSockets', true); - this.config.set('registerSocketsFile', 'server/config/socketio.js'); - this.config.set('socketsNeedle', '// Insert sockets below'); + generateProject: function() { + this.sourceRoot(path.join(__dirname, './templates')); + genUtils.processDirectory(this, '.', '.'); + }, - this.config.set('insertModels', true); - this.config.set('registerModelsFile', 'server/sqldb/index.js'); - this.config.set('modelsNeedle', '// Insert models below'); + generateEndpoint: function() { + var name = this.name = this.cameledName = 'thing'; + this.classedName = name.charAt(0).toUpperCase() + name.slice(1); + this.route = '/api/' + name + 's'; + this.sourceRoot(path.join(__dirname, '..', 'endpoint', 'templates')); + genUtils.processDirectory(this, '.', 'server/api/' + name); + } - this.config.set('filters', this.filters); - this.config.forceSave(); }, - compose: function() { - if(this.skipConfig) return; - var appPath = 'client/app/'; - var extensions = []; - var filters = [ - 'ngroute', - 'uirouter', - 'jasmine', - 'mocha', - 'expect', - 'should' - ].filter(function(v) {return this.filters[v];}, this); - - if(this.filters.ngroute) filters.push('ngroute'); - if(this.filters.uirouter) filters.push('uirouter'); - if(this.filters.babel) extensions.push('babel'); - if(this.filters.coffee) extensions.push('coffee'); - if(this.filters.js) extensions.push('js'); - if(this.filters.html) extensions.push('html'); - if(this.filters.jade) extensions.push('jade'); - if(this.filters.css) extensions.push('css'); - if(this.filters.stylus) extensions.push('styl'); - if(this.filters.sass) extensions.push('scss'); - if(this.filters.less) extensions.push('less'); - - this.composeWith('ng-component', { - options: { - 'routeDirectory': appPath, - 'directiveDirectory': appPath, - 'filterDirectory': appPath, - 'serviceDirectory': appPath, - 'filters': filters, - 'extensions': extensions, - 'basePath': 'client' - } - }, { local: require.resolve('generator-ng-component/app/index.js') }); - }, + install: { - ngModules: function() { - var angModules = [ - "'ngCookies'", - "'ngResource'", - "'ngSanitize'" - ]; - if(this.filters.ngroute) angModules.push("'ngRoute'"); - if(this.filters.socketio) angModules.push("'btford.socket-io'"); - if(this.filters.uirouter) angModules.push("'ui.router'"); - if(this.filters.uibootstrap) angModules.push("'ui.bootstrap'"); - - this.angularModules = '\n ' + angModules.join(',\n ') +'\n'; - }, + installDeps: function() { + this.installDependencies({ + skipInstall: this.options['skip-install'] + }); + } - generate: function() { - this.sourceRoot(path.join(__dirname, './templates')); - genUtils.processDirectory(this, '.', '.'); }, - end: function() { - this.installDependencies({ - skipInstall: this.options['skip-install'] - }); - } + end: {} + }); module.exports = AngularFullstackGenerator; diff --git a/app/templates/server/api/thing/index.js b/app/templates/server/api/thing/index.js deleted file mode 100644 index fde8f3968..000000000 --- a/app/templates/server/api/thing/index.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -var express = require('express'); -var controller = require('./thing.controller'); - -var router = express.Router(); - -router.get('/', controller.index);<% if (filters.models) { %> -router.get('/:id', controller.show); -router.post('/', controller.create); -router.put('/:id', controller.update); -router.patch('/:id', controller.update); -router.delete('/:id', controller.destroy);<% } %> - -module.exports = router; diff --git a/app/templates/server/api/thing/index.spec.js b/app/templates/server/api/thing/index.spec.js deleted file mode 100644 index 91f02f6df..000000000 --- a/app/templates/server/api/thing/index.spec.js +++ /dev/null @@ -1,97 +0,0 @@ -'use strict'; - -var proxyquire = require('proxyquire').noPreserveCache(); - -var thingCtrlStub = { - index: 'thingCtrl.index'<% if (filters.models) { %>, - show: 'thingCtrl.show', - create: 'thingCtrl.create', - update: 'thingCtrl.update', - destroy: 'thingCtrl.destroy'<% } %> -}; - -var routerStub = { - get: sinon.spy()<% if (filters.models) { %>, - put: sinon.spy(), - patch: sinon.spy(), - post: sinon.spy(), - delete: sinon.spy()<% } %> -}; - -// require the index with our stubbed out modules -var thingIndex = proxyquire('./index.js', { - 'express': { - Router: function() { - return routerStub; - } - }, - './thing.controller': thingCtrlStub -}); - -describe('Thing API Router:', function() { - - it('should return an express router instance', function() { - thingIndex.should.equal(routerStub); - }); - - describe('GET /api/things', function() { - - it('should route to thing.controller.index', function() { - routerStub.get - .withArgs('/', 'thingCtrl.index') - .should.have.been.calledOnce; - }); - - });<% if (filters.models) { %> - - describe('GET /api/things/:id', function() { - - it('should route to thing.controller.show', function() { - routerStub.get - .withArgs('/:id', 'thingCtrl.show') - .should.have.been.calledOnce; - }); - - }); - - describe('POST /api/things', function() { - - it('should route to thing.controller.create', function() { - routerStub.post - .withArgs('/', 'thingCtrl.create') - .should.have.been.calledOnce; - }); - - }); - - describe('PUT /api/things/:id', function() { - - it('should route to thing.controller.update', function() { - routerStub.put - .withArgs('/:id', 'thingCtrl.update') - .should.have.been.calledOnce; - }); - - }); - - describe('PATCH /api/things/:id', function() { - - it('should route to thing.controller.update', function() { - routerStub.patch - .withArgs('/:id', 'thingCtrl.update') - .should.have.been.calledOnce; - }); - - }); - - describe('DELETE /api/things/:id', function() { - - it('should route to thing.controller.destroy', function() { - routerStub.delete - .withArgs('/:id', 'thingCtrl.destroy') - .should.have.been.calledOnce; - }); - - });<% } %> - -}); diff --git a/app/templates/server/api/thing/thing.controller(models).js b/app/templates/server/api/thing/thing.controller(models).js deleted file mode 100644 index c7874b515..000000000 --- a/app/templates/server/api/thing/thing.controller(models).js +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Using Rails-like standard naming convention for endpoints. - * GET /things -> index - * POST /things -> create - * GET /things/:id -> show - * PUT /things/:id -> update - * DELETE /things/:id -> destroy - */ - -'use strict'; - -var _ = require('lodash');<% if (filters.mongooseModels) { %> -var Thing = require('./thing.model');<% } %><% if (filters.sequelizeModels) { %> -var sqldb = require('../../sqldb') -var Thing = sqldb.Thing;<% } %> - -function handleError(res, statusCode) { - statusCode = statusCode || 500; - return function(err) { - res.status(statusCode).send(err); - }; -} - -function responseWithResult(res, statusCode) { - statusCode = statusCode || 200; - return function(entity) { - if (entity) { - return res.status(statusCode).json(entity); - } - }; -} - -function handleEntityNotFound(res) { - return function(entity) { - if (!entity) { - res.status(404).end(); - return null; - } - return entity; - }; -} - -function saveUpdates(updates) { - return function(entity) { - <% if (filters.mongooseModels) { %>var updated = _.merge(entity, updates); - return updated.saveAsync() - .spread(function(updated) {<% } - if (filters.sequelizeModels) { %>return entity.updateAttributes(updates) - .then(function(updated) {<% } %> - return updated; - }); - }; -} - -function removeEntity(res) { - return function(entity) { - if (entity) { - <% if (filters.mongooseModels) { %>return entity.removeAsync()<% } - if (filters.sequelizeModels) { %>return entity.destroy()<% } %> - .then(function() { - return res.status(204).end(); - }); - } - }; -} - -// Get list of things -exports.index = function(req, res) { - <% if (filters.mongooseModels) { %>Thing.findAsync()<% } - if (filters.sequelizeModels) { %>Thing.findAll()<% } %> - .then(responseWithResult(res)) - .catch(handleError(res)); -}; - -// Get a single thing -exports.show = function(req, res) { - <% if (filters.mongooseModels) { %>Thing.findByIdAsync(req.params.id)<% } - if (filters.sequelizeModels) { %>Thing.find({ - where: { - _id: req.params.id - } - })<% } %> - .then(handleEntityNotFound(res)) - .then(responseWithResult(res)) - .catch(handleError(res)); -}; - -// Creates a new thing in the DB. -exports.create = function(req, res) { - <% if (filters.mongooseModels) { %>Thing.createAsync(req.body)<% } - if (filters.sequelizeModels) { %>Thing.create(req.body)<% } %> - .then(responseWithResult(res, 201)) - .catch(handleError(res)); -}; - -// Updates an existing thing in the DB. -exports.update = function(req, res) { - if (req.body._id) { - delete req.body._id; - } - <% if (filters.mongooseModels) { %>Thing.findByIdAsync(req.params.id)<% } - if (filters.sequelizeModels) { %>Thing.find({ - where: { - _id: req.params.id - } - })<% } %> - .then(handleEntityNotFound(res)) - .then(saveUpdates(req.body)) - .then(responseWithResult(res)) - .catch(handleError(res)); -}; - -// Deletes a thing from the DB. -exports.destroy = function(req, res) { - <% if (filters.mongooseModels) { %>Thing.findByIdAsync(req.params.id)<% } - if (filters.sequelizeModels) { %>Thing.find({ - where: { - _id: req.params.id - } - })<% } %> - .then(handleEntityNotFound(res)) - .then(removeEntity(res)) - .catch(handleError(res)); -}; diff --git a/app/templates/server/api/thing/thing.controller(noModels).js b/app/templates/server/api/thing/thing.controller(noModels).js deleted file mode 100644 index a39041258..000000000 --- a/app/templates/server/api/thing/thing.controller(noModels).js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Using Rails-like standard naming convention for endpoints. - * GET /things -> index - * POST /things -> create - * GET /things/:id -> show - * PUT /things/:id -> update - * DELETE /things/:id -> destroy - */ - -'use strict'; - -// Get list of things -exports.index = function(req, res) { - res.json([{ - name: 'Development Tools', - info: 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, ' + - 'Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' - }, { - name: 'Server and Client integration', - info: 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' - }, { - name: 'Smart Build System', - info: 'Build system ignores `spec` files, allowing you to keep tests alongside code. ' + - 'Automatic injection of scripts and styles into your index.html' - }, { - name: 'Modular Structure', - info: 'Best practice client and server structures allow for more code reusability and ' + - 'maximum scalability' - }, { - name: 'Optimized Build', - info: 'Build process packs up your templates as a single JavaScript payload, minifies ' + - 'your scripts/css/images, and rewrites asset names for caching.' - }, { - name: 'Deployment Ready', - info: 'Easily deploy your app to Heroku or Openshift with the heroku and openshift ' + - 'sub-generators' - }]); -}; diff --git a/app/templates/server/api/thing/thing.integration.js b/app/templates/server/api/thing/thing.integration.js deleted file mode 100644 index 3eb5d05d8..000000000 --- a/app/templates/server/api/thing/thing.integration.js +++ /dev/null @@ -1,147 +0,0 @@ -'use strict'; - -var app = require('../../app'); -var request = require('supertest');<% if (filters.models) { %> - -var newThing;<% } %> - -describe('Thing API:', function() { - - describe('GET /api/things', function() { - var things; - - beforeEach(function(done) { - request(app) - .get('/api/things') - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - return done(err); - } - things = res.body; - done(); - }); - }); - - it('should respond with JSON array', function() { - things.should.be.instanceOf(Array); - }); - - });<% if (filters.models) { %> - - describe('POST /api/things', function() { - beforeEach(function(done) { - request(app) - .post('/api/things') - .send({ - name: 'New Thing', - info: 'This is the brand new thing!!!' - }) - .expect(201) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - return done(err); - } - newThing = res.body; - done(); - }); - }); - - it('should respond with the newly created thing', function() { - newThing.name.should.equal('New Thing'); - newThing.info.should.equal('This is the brand new thing!!!'); - }); - - }); - - describe('GET /api/things/:id', function() { - var thing; - - beforeEach(function(done) { - request(app) - .get('/api/things/' + newThing._id) - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - return done(err); - } - thing = res.body; - done(); - }); - }); - - afterEach(function() { - thing = {}; - }); - - it('should respond with the requested thing', function() { - thing.name.should.equal('New Thing'); - thing.info.should.equal('This is the brand new thing!!!'); - }); - - }); - - describe('PUT /api/things/:id', function() { - var updatedThing - - beforeEach(function(done) { - request(app) - .put('/api/things/' + newThing._id) - .send({ - name: 'Updated Thing', - info: 'This is the updated thing!!!' - }) - .expect(200) - .expect('Content-Type', /json/) - .end(function(err, res) { - if (err) { - return done(err); - } - updatedThing = res.body; - done(); - }); - }); - - afterEach(function() { - updatedThing = {}; - }); - - it('should respond with the updated thing', function() { - updatedThing.name.should.equal('Updated Thing'); - updatedThing.info.should.equal('This is the updated thing!!!'); - }); - - }); - - describe('DELETE /api/things/:id', function() { - - it('should respond with 204 on successful removal', function(done) { - request(app) - .delete('/api/things/' + newThing._id) - .expect(204) - .end(function(err, res) { - if (err) { - return done(err); - } - done(); - }); - }); - - it('should respond with 404 when thing does not exist', function(done) { - request(app) - .delete('/api/things/' + newThing._id) - .expect(404) - .end(function(err, res) { - if (err) { - return done(err); - } - done(); - }); - }); - - });<% } %> - -}); diff --git a/app/templates/server/api/thing/thing.model(mongooseModels).js b/app/templates/server/api/thing/thing.model(mongooseModels).js deleted file mode 100644 index d1e6e49ea..000000000 --- a/app/templates/server/api/thing/thing.model(mongooseModels).js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -var mongoose = require('mongoose-bird')(); -var Schema = mongoose.Schema; - -var ThingSchema = new Schema({ - name: String, - info: String, - active: Boolean -}); - -module.exports = mongoose.model('Thing', ThingSchema); diff --git a/app/templates/server/api/thing/thing.model(sequelizeModels).js b/app/templates/server/api/thing/thing.model(sequelizeModels).js deleted file mode 100644 index 8e8072da4..000000000 --- a/app/templates/server/api/thing/thing.model(sequelizeModels).js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -module.exports = function(sequelize, DataTypes) { - return sequelize.define('Thing', { - _id: { - type: DataTypes.INTEGER, - allowNull: false, - primaryKey: true, - autoIncrement: true - }, - name: DataTypes.STRING, - info: DataTypes.STRING, - active: DataTypes.BOOLEAN - }); -}; diff --git a/app/templates/server/api/thing/thing.socket(socketio).js b/app/templates/server/api/thing/thing.socket(socketio).js deleted file mode 100644 index d0d98a6ca..000000000 --- a/app/templates/server/api/thing/thing.socket(socketio).js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Broadcast updates to client when the model changes - */ - -'use strict'; -<% if (filters.mongooseModels) { %> -var thing = require('./thing.model');<% } %><% if (filters.sequelizeModels) { %> -var thing = require('../../sqldb').Thing;<% } %> - -exports.register = function(socket) {<% if (filters.sequelizeModels) { %> - thing.hook('afterCreate', function(doc, fields, fn) { - onSave(socket, doc); - fn(null); - });<% } %> - <% if (filters.mongooseModels) { %>thing.schema.post('save', function(doc) {<% } - if (filters.sequelizeModels) { %>thing.hook('afterUpdate', function(doc, fields, fn) {<% } %> - onSave(socket, doc);<% if (filters.sequelizeModels) { %> - fn(null);<% } %> - }); - <% if (filters.mongooseModels) { %>thing.schema.post('remove', function(doc) {<% } - if (filters.sequelizeModels) { %>thing.hook('afterDestroy', function(doc, fields, fn) {<% } %> - onRemove(socket, doc);<% if (filters.sequelizeModels) { %> - fn(null);<% } %> - }); -}; - -function onSave(socket, doc, cb) { - socket.emit('thing:save', doc); -} - -function onRemove(socket, doc, cb) { - socket.emit('thing:remove', doc); -} From 24171aa4962b460770876bf6232f6b82a27e9925 Mon Sep 17 00:00:00 2001 From: kingcody Date: Tue, 7 Jul 2015 18:30:59 -0400 Subject: [PATCH 083/171] feat(app): implement navbar as directive --- .../client/app/account(auth)/login/login(html).html | 2 +- .../client/app/account(auth)/login/login(jade).jade | 2 +- .../app/account(auth)/settings/settings(html).html | 2 +- .../app/account(auth)/settings/settings(jade).jade | 2 +- .../client/app/account(auth)/signup/signup(html).html | 2 +- .../client/app/account(auth)/signup/signup(jade).jade | 2 +- app/templates/client/app/admin(auth)/admin(html).html | 2 +- app/templates/client/app/admin(auth)/admin(jade).jade | 4 ++-- app/templates/client/app/main/main(html).html | 2 +- app/templates/client/app/main/main(jade).jade | 4 ++-- .../components/navbar/navbar.directive(coffee).coffee | 7 +++++++ .../client/components/navbar/navbar.directive(js).js | 10 ++++++++++ test/test-file-creation.js | 1 + 13 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 app/templates/client/components/navbar/navbar.directive(coffee).coffee create mode 100644 app/templates/client/components/navbar/navbar.directive(js).js diff --git a/app/templates/client/app/account(auth)/login/login(html).html b/app/templates/client/app/account(auth)/login/login(html).html index f02de2616..667ecdd0e 100644 --- a/app/templates/client/app/account(auth)/login/login(html).html +++ b/app/templates/client/app/account(auth)/login/login(html).html @@ -1,4 +1,4 @@ -
+
diff --git a/app/templates/client/app/account(auth)/login/login(jade).jade b/app/templates/client/app/account(auth)/login/login(jade).jade index fd95e6ff1..e7ce91916 100644 --- a/app/templates/client/app/account(auth)/login/login(jade).jade +++ b/app/templates/client/app/account(auth)/login/login(jade).jade @@ -1,4 +1,4 @@ -div(ng-include='"components/navbar/navbar.html"') +navbar .container .row .col-sm-12 diff --git a/app/templates/client/app/account(auth)/settings/settings(html).html b/app/templates/client/app/account(auth)/settings/settings(html).html index bb5d8ded0..fe5c7f0f7 100644 --- a/app/templates/client/app/account(auth)/settings/settings(html).html +++ b/app/templates/client/app/account(auth)/settings/settings(html).html @@ -1,4 +1,4 @@ -
+
diff --git a/app/templates/client/app/account(auth)/settings/settings(jade).jade b/app/templates/client/app/account(auth)/settings/settings(jade).jade index 2dc55d402..96340b522 100644 --- a/app/templates/client/app/account(auth)/settings/settings(jade).jade +++ b/app/templates/client/app/account(auth)/settings/settings(jade).jade @@ -1,4 +1,4 @@ -div(ng-include='"components/navbar/navbar.html"') +navbar .container .row .col-sm-12 diff --git a/app/templates/client/app/account(auth)/signup/signup(html).html b/app/templates/client/app/account(auth)/signup/signup(html).html index fc55d3790..5052a02ee 100644 --- a/app/templates/client/app/account(auth)/signup/signup(html).html +++ b/app/templates/client/app/account(auth)/signup/signup(html).html @@ -1,4 +1,4 @@ -
+
diff --git a/app/templates/client/app/account(auth)/signup/signup(jade).jade b/app/templates/client/app/account(auth)/signup/signup(jade).jade index 081657a6d..cca29a28e 100644 --- a/app/templates/client/app/account(auth)/signup/signup(jade).jade +++ b/app/templates/client/app/account(auth)/signup/signup(jade).jade @@ -1,4 +1,4 @@ -div(ng-include='"components/navbar/navbar.html"') +navbar .container .row .col-sm-12 diff --git a/app/templates/client/app/admin(auth)/admin(html).html b/app/templates/client/app/admin(auth)/admin(html).html index 5c27c7af2..e20129214 100644 --- a/app/templates/client/app/admin(auth)/admin(html).html +++ b/app/templates/client/app/admin(auth)/admin(html).html @@ -1,4 +1,4 @@ -
+

The delete user and user index api routes are restricted to users with the 'admin' role.

diff --git a/app/templates/client/app/admin(auth)/admin(jade).jade b/app/templates/client/app/admin(auth)/admin(jade).jade index fd80a0bb6..bcef64773 100644 --- a/app/templates/client/app/admin(auth)/admin(jade).jade +++ b/app/templates/client/app/admin(auth)/admin(jade).jade @@ -1,4 +1,4 @@ -div(ng-include='"components/navbar/navbar.html"') +navbar .container p | The delete user and user index api routes are restricted to users with the 'admin' role. @@ -8,4 +8,4 @@ div(ng-include='"components/navbar/navbar.html"') br span.text-muted {{user.email}} a.trash(ng-click='delete(user)') - span.glyphicon.glyphicon-trash.pull-right \ No newline at end of file + span.glyphicon.glyphicon-trash.pull-right diff --git a/app/templates/client/app/main/main(html).html b/app/templates/client/app/main/main(html).html index 7d0d5c44b..9416ef0db 100644 --- a/app/templates/client/app/main/main(html).html +++ b/app/templates/client/app/main/main(html).html @@ -1,4 +1,4 @@ -
+
-
\ No newline at end of file +
diff --git a/app/templates/client/app/admin(auth)/admin(coffee).coffee b/app/templates/client/app/admin(auth)/admin(coffee).coffee index 18bb9d4ad..99b49177f 100644 --- a/app/templates/client/app/admin(auth)/admin(coffee).coffee +++ b/app/templates/client/app/admin(auth)/admin(coffee).coffee @@ -12,4 +12,4 @@ angular.module '<%= scriptAppName %>' url: '/admin' templateUrl: 'app/admin/admin.html' controller: 'AdminCtrl' -<% } %> \ No newline at end of file +<% } %> diff --git a/app/templates/client/app/admin(auth)/admin(html).html b/app/templates/client/app/admin(auth)/admin(html).html index e20129214..7688c9b47 100644 --- a/app/templates/client/app/admin(auth)/admin(html).html +++ b/app/templates/client/app/admin(auth)/admin(html).html @@ -9,4 +9,4 @@ -
\ No newline at end of file +
diff --git a/app/templates/client/app/admin(auth)/admin(less).less b/app/templates/client/app/admin(auth)/admin(less).less index ad8202750..a6f536dc5 100644 --- a/app/templates/client/app/admin(auth)/admin(less).less +++ b/app/templates/client/app/admin(auth)/admin(less).less @@ -1 +1 @@ -.trash { color:rgb(209, 91, 71); } \ No newline at end of file +.trash { color:rgb(209, 91, 71); } diff --git a/app/templates/client/app/admin(auth)/admin(stylus).styl b/app/templates/client/app/admin(auth)/admin(stylus).styl index d57e50db5..d7d50a172 100644 --- a/app/templates/client/app/admin(auth)/admin(stylus).styl +++ b/app/templates/client/app/admin(auth)/admin(stylus).styl @@ -1,2 +1,2 @@ .trash - color rgb(209, 91, 71) \ No newline at end of file + color rgb(209, 91, 71) diff --git a/app/templates/client/app/main/main(coffee).coffee b/app/templates/client/app/main/main(coffee).coffee index 5d28335d1..04cd367bb 100644 --- a/app/templates/client/app/main/main(coffee).coffee +++ b/app/templates/client/app/main/main(coffee).coffee @@ -12,4 +12,4 @@ angular.module '<%= scriptAppName %>' url: '/' templateUrl: 'app/main/main.html' controller: 'MainCtrl' -<% } %> \ No newline at end of file +<% } %> diff --git a/app/templates/client/components/modal(uibootstrap)/modal(css).css b/app/templates/client/components/modal(uibootstrap)/modal(css).css index f5cc0d9e7..ae0406856 100644 --- a/app/templates/client/components/modal(uibootstrap)/modal(css).css +++ b/app/templates/client/components/modal(uibootstrap)/modal(css).css @@ -20,4 +20,4 @@ } .modal-danger .modal-header { background: #d9534f; -} \ No newline at end of file +} diff --git a/app/templates/client/components/modal(uibootstrap)/modal(html).html b/app/templates/client/components/modal(uibootstrap)/modal(html).html index 4580254ff..f04d0db03 100644 --- a/app/templates/client/components/modal(uibootstrap)/modal(html).html +++ b/app/templates/client/components/modal(uibootstrap)/modal(html).html @@ -8,4 +8,4 @@
\ No newline at end of file + diff --git a/app/templates/client/components/mongoose-error(auth)/mongoose-error.directive(coffee).coffee b/app/templates/client/components/mongoose-error(auth)/mongoose-error.directive(coffee).coffee index d255f614d..cf0e1ccf0 100644 --- a/app/templates/client/components/mongoose-error(auth)/mongoose-error.directive(coffee).coffee +++ b/app/templates/client/components/mongoose-error(auth)/mongoose-error.directive(coffee).coffee @@ -9,4 +9,4 @@ angular.module '<%= scriptAppName %>' require: 'ngModel' link: (scope, element, attrs, ngModel) -> element.on 'keydown', -> - ngModel.$setValidity 'mongoose', true \ No newline at end of file + ngModel.$setValidity 'mongoose', true diff --git a/app/templates/client/components/mongoose-error(auth)/mongoose-error.directive(js).js b/app/templates/client/components/mongoose-error(auth)/mongoose-error.directive(js).js index 8a331009b..a71cb03cf 100644 --- a/app/templates/client/components/mongoose-error(auth)/mongoose-error.directive(js).js +++ b/app/templates/client/components/mongoose-error(auth)/mongoose-error.directive(js).js @@ -14,4 +14,4 @@ angular.module('<%= scriptAppName %>') }); } }; - }); \ No newline at end of file + }); diff --git a/app/templates/client/components/navbar/navbar(jade).jade b/app/templates/client/components/navbar/navbar(jade).jade index 5d7cb2795..e20a8fffa 100644 --- a/app/templates/client/components/navbar/navbar(jade).jade +++ b/app/templates/client/components/navbar/navbar(jade).jade @@ -31,4 +31,4 @@ div.navbar.navbar-default.navbar-static-top(ng-controller='NavbarCtrl') span.glyphicon.glyphicon-cog li(ng-show='isLoggedIn()') - a(<% if (filters.uirouter) { %>ui-sref='logout'<% } else { %>href='/logout'<% } %>) Logout<% } %> \ No newline at end of file + a(<% if (filters.uirouter) { %>ui-sref='logout'<% } else { %>href='/logout'<% } %>) Logout<% } %> diff --git a/app/templates/client/components/navbar/navbar.controller(coffee).coffee b/app/templates/client/components/navbar/navbar.controller(coffee).coffee index 9dda1ae4b..98eaf2213 100644 --- a/app/templates/client/components/navbar/navbar.controller(coffee).coffee +++ b/app/templates/client/components/navbar/navbar.controller(coffee).coffee @@ -12,4 +12,4 @@ angular.module '<%= scriptAppName %>' $scope.getCurrentUser = Auth.getCurrentUser<% } %><% if(!filters.uirouter) { %> $scope.isActive = (route) -> - route is $location.path()<% } %> \ No newline at end of file + route is $location.path()<% } %> diff --git a/app/templates/client/components/socket(socketio)/socket.mock(js).js b/app/templates/client/components/socket(socketio)/socket.mock(js).js index 84a2e0c36..ba09c1d35 100644 --- a/app/templates/client/components/socket(socketio)/socket.mock(js).js +++ b/app/templates/client/components/socket(socketio)/socket.mock(js).js @@ -13,4 +13,4 @@ angular.module('socketMock', []) syncUpdates: function() {}, unsyncUpdates: function() {} }; - }); \ No newline at end of file + }); diff --git a/controller/index.js b/controller/index.js index 29f65325b..6d8897d61 100644 --- a/controller/index.js +++ b/controller/index.js @@ -7,4 +7,4 @@ var Generator = yeoman.generators.Base.extend({ } }); -module.exports = Generator; \ No newline at end of file +module.exports = Generator; diff --git a/decorator/index.js b/decorator/index.js index b28be5c88..ae8193eb7 100644 --- a/decorator/index.js +++ b/decorator/index.js @@ -7,4 +7,4 @@ var Generator = yeoman.generators.Base.extend({ } }); -module.exports = Generator; \ No newline at end of file +module.exports = Generator; diff --git a/directive/index.js b/directive/index.js index 298f4240e..257a4b19a 100644 --- a/directive/index.js +++ b/directive/index.js @@ -7,4 +7,4 @@ var Generator = yeoman.generators.Base.extend({ } }); -module.exports = Generator; \ No newline at end of file +module.exports = Generator; diff --git a/factory/index.js b/factory/index.js index 584079bad..c303eb9b8 100644 --- a/factory/index.js +++ b/factory/index.js @@ -7,4 +7,4 @@ var Generator = yeoman.generators.Base.extend({ } }); -module.exports = Generator; \ No newline at end of file +module.exports = Generator; diff --git a/filter/index.js b/filter/index.js index 8aafad6f7..d1119b27d 100644 --- a/filter/index.js +++ b/filter/index.js @@ -7,4 +7,4 @@ var Generator = yeoman.generators.Base.extend({ } }); -module.exports = Generator; \ No newline at end of file +module.exports = Generator; diff --git a/generators/constant/index.js b/generators/constant/index.js index fd7b5c574..4524a8eed 100644 --- a/generators/constant/index.js +++ b/generators/constant/index.js @@ -11,4 +11,4 @@ util.inherits(Generator, yeoman.generators.Base); Generator.prototype.deprecated = function deprecated() { this.log(chalk.yellow('This sub-generator is deprecated. \n')); -}; \ No newline at end of file +}; diff --git a/generators/deploy/index.js b/generators/deploy/index.js index 6a3d5ec9c..7fb3452fa 100644 --- a/generators/deploy/index.js +++ b/generators/deploy/index.js @@ -12,4 +12,4 @@ util.inherits(Generator, yeoman.generators.NamedBase); Generator.prototype.deprecated = function deprecated() { this.log(chalk.yellow(chalk.bold('yo angular-fullstack:deploy') + ' is deprecated, instead use: \n') + chalk.green('yo angular-fullstack:heroku') + ' or ' + chalk.green('yo angular-fullstack:openshift')); -}; \ No newline at end of file +}; diff --git a/generators/readme.md b/generators/readme.md index 670a62a57..d56c72138 100644 --- a/generators/readme.md +++ b/generators/readme.md @@ -1 +1 @@ -This folder is for deprecated generators only. \ No newline at end of file +This folder is for deprecated generators only. diff --git a/generators/value/index.js b/generators/value/index.js index fd7b5c574..4524a8eed 100644 --- a/generators/value/index.js +++ b/generators/value/index.js @@ -11,4 +11,4 @@ util.inherits(Generator, yeoman.generators.Base); Generator.prototype.deprecated = function deprecated() { this.log(chalk.yellow('This sub-generator is deprecated. \n')); -}; \ No newline at end of file +}; diff --git a/generators/view/index.js b/generators/view/index.js index fd7b5c574..4524a8eed 100644 --- a/generators/view/index.js +++ b/generators/view/index.js @@ -11,4 +11,4 @@ util.inherits(Generator, yeoman.generators.Base); Generator.prototype.deprecated = function deprecated() { this.log(chalk.yellow('This sub-generator is deprecated. \n')); -}; \ No newline at end of file +}; diff --git a/openshift/USAGE b/openshift/USAGE index b3dd18759..a57763b36 100644 --- a/openshift/USAGE +++ b/openshift/USAGE @@ -5,4 +5,4 @@ Example: yo angular-fullstack:openshift This will create: - a dist folder and initialize an openshift app \ No newline at end of file + a dist folder and initialize an openshift app diff --git a/provider/index.js b/provider/index.js index ed40ef29d..5e3ac882e 100644 --- a/provider/index.js +++ b/provider/index.js @@ -7,4 +7,4 @@ var Generator = yeoman.generators.Base.extend({ } }); -module.exports = Generator; \ No newline at end of file +module.exports = Generator; diff --git a/route/index.js b/route/index.js index 2a69476c5..cc8569854 100644 --- a/route/index.js +++ b/route/index.js @@ -7,4 +7,4 @@ var Generator = yeoman.generators.Base.extend({ } }); -module.exports = Generator; \ No newline at end of file +module.exports = Generator; diff --git a/scripts/sauce_connect_setup.sh b/scripts/sauce_connect_setup.sh index 06b6e2092..4348e3662 100755 --- a/scripts/sauce_connect_setup.sh +++ b/scripts/sauce_connect_setup.sh @@ -57,4 +57,4 @@ done unset SAUCE_CONNECT_PLATFORM SAUCE_TMP_DIR SC_DIR SC_DISTRIBUTION SC_READYFILE SC_LOGFILE SC_TUNNEL_ID -popd \ No newline at end of file +popd diff --git a/service/index.js b/service/index.js index d133abdbc..9aa5f48b0 100644 --- a/service/index.js +++ b/service/index.js @@ -7,4 +7,4 @@ var Generator = yeoman.generators.Base.extend({ } }); -module.exports = Generator; \ No newline at end of file +module.exports = Generator; diff --git a/test/fixtures/.yo-rc.json b/test/fixtures/.yo-rc.json index 9ab655e54..a26821115 100644 --- a/test/fixtures/.yo-rc.json +++ b/test/fixtures/.yo-rc.json @@ -17,4 +17,4 @@ "googleAuth": true } } -} \ No newline at end of file +} diff --git a/util.js b/util.js index 7544f8c8e..4bf2170c4 100644 --- a/util.js +++ b/util.js @@ -137,4 +137,4 @@ function processDirectory (self, source, destination) { } } }); -} \ No newline at end of file +} From e7a1a462cd825d03c3b9564c1114182fef416466 Mon Sep 17 00:00:00 2001 From: Tyson Quek Date: Wed, 22 Jul 2015 10:32:28 +0800 Subject: [PATCH 117/171] refactor(client:auth): use $cookies instead $cookieStore is deprecated. See https://docs.angularjs.org/api/ngCookies/service/$cookieStore --- app/templates/client/app/app(coffee).coffee | 6 +++--- app/templates/client/app/app(js).js | 8 ++++---- .../auth(auth)/auth.service(coffee).coffee | 12 ++++++------ .../client/components/auth(auth)/auth.service(js).js | 12 ++++++------ 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/templates/client/app/app(coffee).coffee b/app/templates/client/app/app(coffee).coffee index 25b572a75..b070abd8b 100644 --- a/app/templates/client/app/app(coffee).coffee +++ b/app/templates/client/app/app(coffee).coffee @@ -15,12 +15,12 @@ angular.module '<%= scriptAppName %>', [<%= angularModules %>] $locationProvider.html5Mode true<% if (filters.auth) { %> $httpProvider.interceptors.push 'authInterceptor'<% } %> <% } %><% if (filters.auth) { %> -.factory 'authInterceptor', ($rootScope, $q, $cookieStore<% if (filters.ngroute) { %>, $location<% } if (filters.uirouter) { %>, $injector<% } %>) -> +.factory 'authInterceptor', ($rootScope, $q, $cookies<% if (filters.ngroute) { %>, $location<% } if (filters.uirouter) { %>, $injector<% } %>) -> <% if (filters.uirouter) { %>state = null <% } %># Add authorization token to headers request: (config) -> config.headers = config.headers or {} - config.headers.Authorization = 'Bearer ' + $cookieStore.get 'token' if $cookieStore.get 'token' + config.headers.Authorization = 'Bearer ' + $cookies.get 'token' if $cookies.get 'token' config # Intercept 401s and redirect you to login @@ -28,7 +28,7 @@ angular.module '<%= scriptAppName %>', [<%= angularModules %>] if response.status is 401 <% if (filters.ngroute) { %>$location.path '/login'<% } if (filters.uirouter) { %>(state || state = $injector.get '$state').go 'login'<% } %> # remove any stale tokens - $cookieStore.remove 'token' + $cookies.remove 'token' $q.reject response diff --git a/app/templates/client/app/app(js).js b/app/templates/client/app/app(js).js index 2ee87aafe..0b636714d 100644 --- a/app/templates/client/app/app(js).js +++ b/app/templates/client/app/app(js).js @@ -17,14 +17,14 @@ angular.module('<%= scriptAppName %>', [<%= angularModules %>]) $httpProvider.interceptors.push('authInterceptor');<% } %> })<% } if (filters.auth) { %> - .factory('authInterceptor', function($rootScope, $q, $cookieStore<% if (filters.ngroute) { %>, $location<% } if (filters.uirouter) { %>, $injector<% } %>) { + .factory('authInterceptor', function($rootScope, $q, $cookies<% if (filters.ngroute) { %>, $location<% } if (filters.uirouter) { %>, $injector<% } %>) { <% if (filters.uirouter) { %>var state; <% } %>return { // Add authorization token to headers request: function(config) { config.headers = config.headers || {}; - if ($cookieStore.get('token')) { - config.headers.Authorization = 'Bearer ' + $cookieStore.get('token'); + if ($cookies.get('token')) { + config.headers.Authorization = 'Bearer ' + $cookies.get('token'); } return config; }, @@ -34,7 +34,7 @@ angular.module('<%= scriptAppName %>', [<%= angularModules %>]) if (response.status === 401) { <% if (filters.ngroute) { %>$location.path('/login');<% } if (filters.uirouter) { %>(state || (state = $injector.get('$state'))).go('login');<% } %> // remove any stale tokens - $cookieStore.remove('token'); + $cookies.remove('token'); return $q.reject(response); } else { diff --git a/app/templates/client/components/auth(auth)/auth.service(coffee).coffee b/app/templates/client/components/auth(auth)/auth.service(coffee).coffee index 1d4b29544..778c4e547 100644 --- a/app/templates/client/components/auth(auth)/auth.service(coffee).coffee +++ b/app/templates/client/components/auth(auth)/auth.service(coffee).coffee @@ -1,8 +1,8 @@ 'use strict' angular.module '<%= scriptAppName %>' -.factory 'Auth', ($http, User, $cookieStore, $q) -> - currentUser = if $cookieStore.get 'token' then User.get() else {} +.factory 'Auth', ($http, User, $cookies, $q) -> + currentUser = if $cookies.get 'token' then User.get() else {} ### Authenticate user and save token @@ -17,7 +17,7 @@ angular.module '<%= scriptAppName %>' password: user.password .then (res) -> - $cookieStore.put 'token', res.data.token + $cookies.put 'token', res.data.token currentUser = User.get() callback?() res.data @@ -32,7 +32,7 @@ angular.module '<%= scriptAppName %>' Delete access token and user info ### logout: -> - $cookieStore.remove 'token' + $cookies.remove 'token' currentUser = {} return @@ -47,7 +47,7 @@ angular.module '<%= scriptAppName %>' createUser: (user, callback) -> User.save user, (data) -> - $cookieStore.put 'token', data.token + $cookies.put 'token', data.token currentUser = User.get() callback? null, user @@ -144,4 +144,4 @@ angular.module '<%= scriptAppName %>' Get auth token ### getToken: -> - $cookieStore.get 'token' + $cookies.get 'token' diff --git a/app/templates/client/components/auth(auth)/auth.service(js).js b/app/templates/client/components/auth(auth)/auth.service(js).js index 5baf0e0b4..6b0c80e08 100644 --- a/app/templates/client/components/auth(auth)/auth.service(js).js +++ b/app/templates/client/components/auth(auth)/auth.service(js).js @@ -1,7 +1,7 @@ 'use strict'; angular.module('<%= scriptAppName %>') - .factory('Auth', function Auth($http, User, $cookieStore, $q) { + .factory('Auth', function Auth($http, User, $cookies, $q) { /** * Return a callback or noop function * @@ -14,7 +14,7 @@ angular.module('<%= scriptAppName %>') currentUser = {}; - if ($cookieStore.get('token')) { + if ($cookies.get('token')) { currentUser = User.get(); } @@ -33,7 +33,7 @@ angular.module('<%= scriptAppName %>') password: user.password }) .then(function(res) { - $cookieStore.put('token', res.data.token); + $cookies.put('token', res.data.token); currentUser = User.get(); safeCb(callback)(); return res.data; @@ -48,7 +48,7 @@ angular.module('<%= scriptAppName %>') * Delete access token and user info */ logout: function() { - $cookieStore.remove('token'); + $cookies.remove('token'); currentUser = {}; }, @@ -62,7 +62,7 @@ angular.module('<%= scriptAppName %>') createUser: function(user, callback) { return User.save(user, function(data) { - $cookieStore.put('token', data.token); + $cookies.put('token', data.token); currentUser = User.get(); return safeCb(callback)(null, user); }, @@ -160,7 +160,7 @@ angular.module('<%= scriptAppName %>') * @return {String} - a token string used for authenticating */ getToken: function() { - return $cookieStore.get('token'); + return $cookies.get('token'); } }; }); From 90ff36e8d5eebd53b2a615c4803c412812a11673 Mon Sep 17 00:00:00 2001 From: kingcody Date: Fri, 31 Jul 2015 05:10:28 -0400 Subject: [PATCH 118/171] fix(gen): default grunt filter This takes care of the README.md template as well as allowing other template updates related to the grunt/gulp implementation --- app/index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/index.js b/app/index.js index 5c3ef0673..6c0a7fe3f 100644 --- a/app/index.js +++ b/app/index.js @@ -287,6 +287,11 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ return answers.testing === 'mocha'; } }], function (answers) { + /** + * Default to grunt until gulp support is implemented + */ + this.filters.grunt = true; + this.filters[answers.testing] = true; if (answers.testing === 'mocha') { this.filters.jasmine = false; From 1bfcc1fc05202b2abd05915c52ec5010ad960b5e Mon Sep 17 00:00:00 2001 From: Denis Ciccale Date: Sun, 2 Aug 2015 16:28:22 +0200 Subject: [PATCH 119/171] fix(app.coffee): missing preventDefault() --- app/templates/client/app/app(coffee).coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/templates/client/app/app(coffee).coffee b/app/templates/client/app/app(coffee).coffee index b070abd8b..bf97363b1 100644 --- a/app/templates/client/app/app(coffee).coffee +++ b/app/templates/client/app/app(coffee).coffee @@ -36,5 +36,6 @@ angular.module '<%= scriptAppName %>', [<%= angularModules %>] # Redirect to login if route requires auth and you're not logged in $rootScope.$on <% if (filters.ngroute) { %>'$routeChangeStart'<% } %><% if (filters.uirouter) { %>'$stateChangeStart'<% } %>, (event, next) -> Auth.isLoggedIn (loggedIn) -> + event.preventDefault() <% if (filters.ngroute) { %>$location.path '/login'<% } %><% if (filters.uirouter) { %>$state.go 'login'<% } %> if next.authenticate and not loggedIn <% } %> From 6bfc1da235d96a1d569c9ac7cba6d765a2a51c4b Mon Sep 17 00:00:00 2001 From: Denis Ciccale Date: Sun, 2 Aug 2015 16:29:30 +0200 Subject: [PATCH 120/171] chore(auth.service): changePassword does not receive success data --- .../client/components/auth(auth)/auth.service(coffee).coffee | 4 ++-- .../client/components/auth(auth)/auth.service(js).js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/templates/client/components/auth(auth)/auth.service(coffee).coffee b/app/templates/client/components/auth(auth)/auth.service(coffee).coffee index 778c4e547..d59b1040d 100644 --- a/app/templates/client/components/auth(auth)/auth.service(coffee).coffee +++ b/app/templates/client/components/auth(auth)/auth.service(coffee).coffee @@ -73,8 +73,8 @@ angular.module '<%= scriptAppName %>' oldPassword: oldPassword newPassword: newPassword - , (user) -> - callback? null, user + , () -> + callback? null , (err) -> callback? err diff --git a/app/templates/client/components/auth(auth)/auth.service(js).js b/app/templates/client/components/auth(auth)/auth.service(js).js index 6b0c80e08..d7f787a94 100644 --- a/app/templates/client/components/auth(auth)/auth.service(js).js +++ b/app/templates/client/components/auth(auth)/auth.service(js).js @@ -84,8 +84,8 @@ angular.module('<%= scriptAppName %>') return User.changePassword({ id: currentUser._id }, { oldPassword: oldPassword, newPassword: newPassword - }, function(user) { - return safeCb(callback)(null, user); + }, function() { + return safeCb(callback)(null); }, function(err) { return safeCb(callback)(err); }).$promise; From 7ecf75180fc89de2ad441adf651dcfcd95ad85ae Mon Sep 17 00:00:00 2001 From: kingcody Date: Tue, 4 Aug 2015 00:05:49 -0400 Subject: [PATCH 121/171] chore(dependencies): update grunt-wiredep to `^2.0.0` --- app/templates/_package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/_package.json b/app/templates/_package.json index 194af868e..31b5aa5d5 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -36,7 +36,7 @@ "devDependencies": { "autoprefixer-core": "^5.2.1", "grunt": "~0.4.4", - "grunt-wiredep": "~1.8.0", + "grunt-wiredep": "^2.0.0", "grunt-concurrent": "~0.5.0", "grunt-contrib-clean": "~0.5.0", "grunt-contrib-concat": "~0.4.0", From f31e9968dc1aca0b0254d411b19fdc5bd19a6b7f Mon Sep 17 00:00:00 2001 From: kingcody Date: Tue, 4 Aug 2015 00:07:14 -0400 Subject: [PATCH 122/171] feat(app): use wiredep to inject bower deps into karma.conf.js --- app/templates/Gruntfile.js | 31 +++++++++++++++++++++++-------- app/templates/karma.conf.js | 14 +++----------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index ce5275629..e635727f9 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -72,7 +72,7 @@ module.exports = function (grunt) { }, jsTest: { files: ['<%%= yeoman.client %>/{app,components}/**/*.{spec,mock}.js'], - tasks: ['newer:jshint:all', 'karma'] + tasks: ['newer:jshint:all', 'wiredep:test', 'karma'] },<% if (filters.stylus) { %> injectStylus: { files: ['<%%= yeoman.client %>/{app,components}/**/*.styl'], @@ -240,12 +240,26 @@ module.exports = function (grunt) { } }, - // Automatically inject Bower components into the app + // Automatically inject Bower components into the app and karma.conf.js wiredep: { - target: { + options: { + exclude: [ + /angular-scenario/, + /bootstrap-sass-official/, + /bootstrap.js/, + '/json3/', + '/es5-shim/'<% if(!filters.css) { %>, + /bootstrap.css/, + /font-awesome.css/<% } %> + ] + }, + client: { src: '<%%= yeoman.client %>/index.html', ignorePath: '<%%= yeoman.client %>/', - exclude: [/bootstrap-sass-official/, /bootstrap.js/, '/json3/', '/es5-shim/'<% if(!filters.css) { %>, /bootstrap.css/, /font-awesome.css/ <% } %>] + }, + test: { + src: './karma.conf.js', + devDependencies: true } }, @@ -773,7 +787,7 @@ module.exports = function (grunt) { 'injector:sass',<% } %> 'concurrent:server', 'injector', - 'wiredep', + 'wiredep:client', 'postcss', 'concurrent:debug' ]); @@ -787,7 +801,7 @@ module.exports = function (grunt) { 'injector:sass',<% } %> 'concurrent:server', 'injector', - 'wiredep', + 'wiredep:client', 'postcss', 'express:dev', 'wait', @@ -821,6 +835,7 @@ module.exports = function (grunt) { 'concurrent:test', 'injector', 'postcss', + 'wiredep:test', 'karma' ]); } @@ -847,7 +862,7 @@ module.exports = function (grunt) { 'injector:sass',<% } %> 'concurrent:test', 'injector', - 'wiredep', + 'wiredep:client', 'postcss', 'express:dev', 'protractor' @@ -903,7 +918,7 @@ module.exports = function (grunt) { 'injector:sass',<% } %> 'concurrent:dist', 'injector', - 'wiredep', + 'wiredep:client', 'useminPrepare', 'postcss', 'ngtemplates', diff --git a/app/templates/karma.conf.js b/app/templates/karma.conf.js index b18bc8da3..5b45e4f86 100644 --- a/app/templates/karma.conf.js +++ b/app/templates/karma.conf.js @@ -12,17 +12,9 @@ module.exports = function(config) { // list of files / patterns to load in the browser files: [ - 'client/bower_components/jquery/dist/jquery.js', - 'client/bower_components/angular/angular.js', - 'client/bower_components/angular-mocks/angular-mocks.js', - 'client/bower_components/angular-resource/angular-resource.js', - 'client/bower_components/angular-cookies/angular-cookies.js', - 'client/bower_components/angular-sanitize/angular-sanitize.js', - 'client/bower_components/angular-route/angular-route.js',<% if (filters.uibootstrap) { %> - 'client/bower_components/angular-bootstrap/ui-bootstrap-tpls.js',<% } %> - 'client/bower_components/lodash/dist/lodash.compat.js',<% if (filters.socketio) { %> - 'client/bower_components/angular-socket-io/socket.js',<% } %><% if (filters.uirouter) { %> - 'client/bower_components/angular-ui-router/release/angular-ui-router.js',<% } %> + // bower:js + // endbower<% if (filters.socketio) { %> + 'node_modules/socket.io-client/socket.io.js',<% } %> 'client/app/app.js', 'client/app/app.coffee', 'client/app/**/*.js', From e04185a7227ad018f620aa1e32d813b555d1dc76 Mon Sep 17 00:00:00 2001 From: kingcody Date: Tue, 4 Aug 2015 03:45:09 -0400 Subject: [PATCH 123/171] fix(app): auth.login waits for user resource to resolve --- .../auth(auth)/auth.service(coffee).coffee | 11 +++++++---- .../client/components/auth(auth)/auth.service(js).js | 12 ++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/templates/client/components/auth(auth)/auth.service(coffee).coffee b/app/templates/client/components/auth(auth)/auth.service(coffee).coffee index 778c4e547..3b1ab4d0c 100644 --- a/app/templates/client/components/auth(auth)/auth.service(coffee).coffee +++ b/app/templates/client/components/auth(auth)/auth.service(coffee).coffee @@ -8,7 +8,7 @@ angular.module '<%= scriptAppName %>' Authenticate user and save token @param {Object} user - login info - @param {Function} callback - optional, function(error) + @param {Function} callback - optional, function(error, user) @return {Promise} ### login: (user, callback) -> @@ -19,10 +19,13 @@ angular.module '<%= scriptAppName %>' .then (res) -> $cookies.put 'token', res.data.token currentUser = User.get() - callback?() - res.data + currentUser.$promise + + .then (user) -> + callback? null, user + user - , (err) => + .catch (err) => @logout() callback? err.data $q.reject err.data diff --git a/app/templates/client/components/auth(auth)/auth.service(js).js b/app/templates/client/components/auth(auth)/auth.service(js).js index 6b0c80e08..32a4eabf3 100644 --- a/app/templates/client/components/auth(auth)/auth.service(js).js +++ b/app/templates/client/components/auth(auth)/auth.service(js).js @@ -24,7 +24,7 @@ angular.module('<%= scriptAppName %>') * Authenticate user and save token * * @param {Object} user - login info - * @param {Function} callback - optional, function(error) + * @param {Function} callback - optional, function(error, user) * @return {Promise} */ login: function(user, callback) { @@ -35,9 +35,13 @@ angular.module('<%= scriptAppName %>') .then(function(res) { $cookies.put('token', res.data.token); currentUser = User.get(); - safeCb(callback)(); - return res.data; - }, function(err) { + return currentUser.$promise; + }) + .then(function(user) { + safeCb(callback)(null, user); + return user; + }) + .catch(function(err) { this.logout(); safeCb(callback)(err.data); return $q.reject(err.data); From 8247085844f3b73feb7db3cc785a4bc48b3b2e6c Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Wed, 5 Aug 2015 00:57:24 -0400 Subject: [PATCH 124/171] feat(grunt): add watch to bower.json --- app/templates/Gruntfile.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index e635727f9..361c49f1a 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -133,7 +133,11 @@ module.exports = function (grunt) { livereload: true, nospawn: true //Without this option specified express won't be reloaded } - } + }, + bower: { + files: ['bower.json'], + tasks: ['wiredep'] + }, }, // Make sure code styles are up to par and there are no obvious mistakes From 2526ca5c90edced22eac4980bef5b9b6a5f1b156 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Wed, 5 Aug 2015 13:28:26 -0400 Subject: [PATCH 125/171] feat(server:auth): add role to signToken add role as second parameter, and attach to jwt closes #382 --- app/templates/server/auth(auth)/auth.service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/templates/server/auth(auth)/auth.service.js b/app/templates/server/auth(auth)/auth.service.js index 86f781f84..faa2684cf 100644 --- a/app/templates/server/auth(auth)/auth.service.js +++ b/app/templates/server/auth(auth)/auth.service.js @@ -71,8 +71,8 @@ function hasRole(roleRequired) { /** * Returns a jwt token signed by the app secret */ -function signToken(id) { - return jwt.sign({ _id: id }, config.secrets.session, { +function signToken(id, role) { + return jwt.sign({ _id: id, role: role }, config.secrets.session, { expiresInMinutes: 60 * 5 }); } From 76325bdc24b6c0a613981a4f95830341c09bf490 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Fri, 17 Jul 2015 18:51:33 -0400 Subject: [PATCH 126/171] fix(gen:css): ensure scss/less/stylus files have proper relative paths --- app/templates/Gruntfile.js | 6 +++--- app/templates/client/app/app(less).less | 8 ++++---- app/templates/client/app/app(sass).scss | 8 ++++---- app/templates/client/app/app(stylus).styl | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index 361c49f1a..fde0acb0a 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -689,7 +689,7 @@ module.exports = function (grunt) { options: { transform: function(filePath) { filePath = filePath.replace('/client/app/', ''); - filePath = filePath.replace('/client/components/', ''); + filePath = filePath.replace('/client/components/', '../components/'); return '@import \'' + filePath + '\';'; }, starttag: '// injector', @@ -708,7 +708,7 @@ module.exports = function (grunt) { options: { transform: function(filePath) { filePath = filePath.replace('/client/app/', ''); - filePath = filePath.replace('/client/components/', ''); + filePath = filePath.replace('/client/components/', '../components/'); return '@import \'' + filePath + '\';'; }, starttag: '// injector', @@ -727,7 +727,7 @@ module.exports = function (grunt) { options: { transform: function(filePath) { filePath = filePath.replace('/client/app/', ''); - filePath = filePath.replace('/client/components/', ''); + filePath = filePath.replace('/client/components/', '../components/'); return '@import \'' + filePath + '\';'; }, starttag: '// injector', diff --git a/app/templates/client/app/app(less).less b/app/templates/client/app/app(less).less index 460b56bdb..cbfffbe88 100644 --- a/app/templates/client/app/app(less).less +++ b/app/templates/client/app/app(less).less @@ -1,8 +1,8 @@ -<% if (filters.bootstrap) { %>@import 'bootstrap/less/bootstrap.less';<% } %> -@import 'font-awesome/less/font-awesome.less'; +<% if (filters.bootstrap) { %>@import '../bower_components/bootstrap/less/bootstrap.less';<% } %> +@import '../bower_components/font-awesome/less/font-awesome.less'; -<% if (filters.bootstrap) { %>@icon-font-path: '/bower_components/bootstrap/fonts/';<% } %> -@fa-font-path: '/bower_components/font-awesome/fonts'; +<% if (filters.bootstrap) { %>@icon-font-path: '../bower_components/bootstrap/fonts/';<% } %> +@fa-font-path: '../bower_components/font-awesome/fonts'; /** * App-wide Styles diff --git a/app/templates/client/app/app(sass).scss b/app/templates/client/app/app(sass).scss index 30befa636..889878aee 100644 --- a/app/templates/client/app/app(sass).scss +++ b/app/templates/client/app/app(sass).scss @@ -1,8 +1,8 @@ -<% if (filters.bootstrap) { %>$icon-font-path: "/bower_components/bootstrap-sass-official/vendor/assets/fonts/bootstrap/";<% } %> -$fa-font-path: "/bower_components/font-awesome/fonts"; +<% if (filters.bootstrap) { %>$icon-font-path: "../bower_components/bootstrap-sass-official/vendor/assets/fonts/bootstrap/";<% } %> +$fa-font-path: "../bower_components/font-awesome/fonts"; <% if (filters.bootstrap) { %> -@import 'bootstrap-sass-official/vendor/assets/stylesheets/bootstrap';<% } %> -@import 'font-awesome/scss/font-awesome'; +@import '../bower_components/bootstrap-sass-official/vendor/assets/stylesheets/bootstrap';<% } %> +@import '../bower_components/font-awesome/scss/font-awesome'; /** * App-wide Styles diff --git a/app/templates/client/app/app(stylus).styl b/app/templates/client/app/app(stylus).styl index 9eab53889..d25cdfc59 100644 --- a/app/templates/client/app/app(stylus).styl +++ b/app/templates/client/app/app(stylus).styl @@ -1,5 +1,5 @@ -@import "font-awesome/css/font-awesome.css" -<% if (filters.bootstrap) { %>@import "bootstrap/dist/css/bootstrap.css" +@import "../bower_components/font-awesome/css/font-awesome.css" +<% if (filters.bootstrap) { %>@import "../bower_components/bootstrap/dist/css/bootstrap.css" // // Bootstrap Fonts From 799e94b9a12e119cc29bc68a864aab833f4e28ba Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Wed, 5 Aug 2015 12:59:29 -0400 Subject: [PATCH 127/171] refactor(grunt): remove no longer needed paths remove Sass/Stylus/Less include paths --- app/templates/Gruntfile.js | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index fde0acb0a..d562f6b3b 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -612,11 +612,6 @@ module.exports = function (grunt) { stylus: { server: { options: { - paths: [ - '<%%= yeoman.client %>/bower_components', - '<%%= yeoman.client %>/app', - '<%%= yeoman.client %>/components' - ], "include css": true }, files: { @@ -629,11 +624,6 @@ module.exports = function (grunt) { sass: { server: { options: { - loadPath: [ - '<%%= yeoman.client %>/bower_components', - '<%%= yeoman.client %>/app', - '<%%= yeoman.client %>/components' - ], compass: false }, files: { @@ -644,18 +634,11 @@ module.exports = function (grunt) { // Compiles Less to CSS less: { - options: { - paths: [ - '<%%= yeoman.client %>/bower_components', - '<%%= yeoman.client %>/app', - '<%%= yeoman.client %>/components' - ] - }, server: { files: { '.tmp/app/app.css' : '<%%= yeoman.client %>/app/app.less' } - }, + } },<% } %> injector: { From 9c3e7eed5d66d5d3a136d7d72dd7f0f8a32b65d6 Mon Sep 17 00:00:00 2001 From: kingcody Date: Thu, 6 Aug 2015 02:42:18 -0400 Subject: [PATCH 128/171] fix(grunt): `watch.jade` only watches jade files --- app/templates/Gruntfile.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index 361c49f1a..420f6dd21 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -99,10 +99,7 @@ module.exports = function (grunt) { tasks: ['less', 'postcss'] },<% } if (filters.jade) { %> jade: { - files: [ - '<%%= yeoman.client %>/{app,components}/*', - '<%%= yeoman.client %>/{app,components}/**/*.jade' - ], + files: ['<%%= yeoman.client %>/{app,components}/**/*.jade'], tasks: ['jade'] },<% } if (filters.coffee) { %> coffee: { From 1dfc856d21f94808954c0dd5cae97741a216e62f Mon Sep 17 00:00:00 2001 From: kingcody Date: Fri, 24 Jul 2015 03:17:00 -0400 Subject: [PATCH 129/171] chore(dependencies): update yeoman-generator to ~0.8.10 --- app/index.js | 9 +++++---- endpoint/index.js | 16 ++++++++-------- package.json | 4 ++-- test/test-file-creation.js | 30 +++++++++++++++--------------- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/app/index.js b/app/index.js index 6c0a7fe3f..4ae1ad871 100644 --- a/app/index.js +++ b/app/index.js @@ -37,7 +37,7 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ }, info: function () { - this.log(this.yeoman); + this.log(this.welcome); this.log('Out of the box I create an AngularJS app with an Express server.\n'); }, @@ -60,14 +60,15 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ }); // NOTE: temp(?) fix for #403 - if(typeof this.filters.oauth==='undefined') { + if(typeof this.filters.oauth === 'undefined') { var strategies = Object.keys(this.filters).filter(function(key) { - return key.match(/Auth$/) && key; - }); + return key.match(/Auth$/) && this.filters[key]; + }.bind(this)); if(strategies.length) this.filters.oauth = true; } + this.config.set('filters', this.filters); this.config.forceSave(); cb(); diff --git a/endpoint/index.js b/endpoint/index.js index 8fa1f498d..22e8e8950 100644 --- a/endpoint/index.js +++ b/endpoint/index.js @@ -11,7 +11,7 @@ var Generator = module.exports = function Generator() { util.inherits(Generator, ScriptBase); -Generator.prototype.askFor = function askFor() { +Generator.prototype.prompting = function askFor() { var done = this.async(); var name = this.name; @@ -66,7 +66,13 @@ Generator.prototype.askFor = function askFor() { }.bind(this)); }; -Generator.prototype.registerEndpoint = function registerEndpoint() { +Generator.prototype.writing = function createFiles() { + var dest = this.config.get('endpointDirectory') || 'server/api/' + this.name; + this.sourceRoot(path.join(__dirname, './templates')); + ngUtil.processDirectory(this, '.', dest); +}; + +Generator.prototype.end = function registerEndpoint() { if(this.config.get('insertRoutes')) { var routeConfig = { file: this.config.get('registerRoutesFile'), @@ -110,9 +116,3 @@ Generator.prototype.registerEndpoint = function registerEndpoint() { } } }; - -Generator.prototype.createFiles = function createFiles() { - var dest = this.config.get('endpointDirectory') || 'server/api/' + this.name; - this.sourceRoot(path.join(__dirname, './templates')); - ngUtil.processDirectory(this, '.', dest); -}; diff --git a/package.json b/package.json index f66b72cb0..c1a50c792 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,9 @@ "test": "grunt test" }, "dependencies": { - "yeoman-generator": "~0.17.0", "chalk": "~0.4.0", - "generator-ng-component": "~0.0.4" + "generator-ng-component": "~0.0.4", + "yeoman-generator": "~0.18.10" }, "devDependencies": { "chai": "^1.9.1", diff --git a/test/test-file-creation.js b/test/test-file-creation.js index bec4ab17d..ce8635c0b 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -26,13 +26,13 @@ describe('angular-fullstack generator', function () { }, dependenciesInstalled = false; function generatorTest(generatorType, name, mockPrompt, callback) { - gen.run({}, function () { + gen.run(function () { var afGenerator; var deps = [path.join('../..', generatorType)]; afGenerator = helpers.createGenerator('angular-fullstack:' + generatorType, deps, [name]); helpers.mockPrompt(afGenerator, mockPrompt); - afGenerator.run([], function () { + afGenerator.run(function () { callback(); }); }); @@ -107,7 +107,7 @@ describe('angular-fullstack generator', function () { if (endpoint) { generatorTest('endpoint', endpoint, {}, execFn); } else { - gen.run({}, execFn); + gen.run(execFn); } } @@ -430,7 +430,7 @@ describe('angular-fullstack generator', function () { helpers.mockPrompt(gen, { skipConfig: true }); - gen.run({}, function () { + gen.run(function () { helpers.assertFile([ 'client/app/main/main.less', 'client/app/main/main.coffee', @@ -454,7 +454,7 @@ describe('angular-fullstack generator', function () { helpers.mockPrompt(gen, { skipConfig: true }); - gen.run({}, function () { + gen.run(function () { var yoConfig = require(__dirname + '/temp/.yo-rc.json'); expect(yoConfig['generator-angular-fullstack'].filters.oauth).to.be.true; done(); @@ -462,14 +462,14 @@ describe('angular-fullstack generator', function () { }); it('should generate expected files', function (done) { - gen.run({}, function () { + gen.run(function () { helpers.assertFile(genFiles(defaultOptions)); done(); }); }); it('should not generate unexpected files', function (done) { - gen.run({}, function () { + gen.run(function () { assertOnlyFiles(genFiles(defaultOptions), done); }); }); @@ -533,14 +533,14 @@ describe('angular-fullstack generator', function () { }); it('should generate expected files', function (done) { - gen.run({}, function () { + gen.run(function () { helpers.assertFile(genFiles(testOptions)); done(); }); }); it('should not generate unexpected files', function (done) { - gen.run({}, function () { + gen.run(function () { assertOnlyFiles(genFiles(testOptions), done); }); }); @@ -605,14 +605,14 @@ describe('angular-fullstack generator', function () { }); it('should generate expected files', function (done) { - gen.run({}, function () { + gen.run(function () { helpers.assertFile(genFiles(testOptions)); done(); }); }); it('should not generate unexpected files', function (done) { - gen.run({}, function () { + gen.run(function () { assertOnlyFiles(genFiles(testOptions), done); }); }); @@ -679,14 +679,14 @@ describe('angular-fullstack generator', function () { }); it('should generate expected files', function (done) { - gen.run({}, function () { + gen.run(function () { helpers.assertFile(genFiles(testOptions)); done(); }); }); it('should not generate unexpected files', function (done) { - gen.run({}, function () { + gen.run(function () { assertOnlyFiles(genFiles(testOptions), done); }); }); @@ -740,14 +740,14 @@ describe('angular-fullstack generator', function () { }); it('should generate expected files', function (done) { - gen.run({}, function () { + gen.run(function () { helpers.assertFile(genFiles(testOptions)); done(); }); }); it('should not generate unexpected files', function (done) { - gen.run({}, function () { + gen.run(function () { assertOnlyFiles(genFiles(testOptions), done); }); }); From 533bbd7991e4066e724f7c4807020eeef2739384 Mon Sep 17 00:00:00 2001 From: kingcody Date: Thu, 6 Aug 2015 02:58:35 -0400 Subject: [PATCH 130/171] chore(dependencies): update generator-ng-component to ~0.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c1a50c792..a062a68a9 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ }, "dependencies": { "chalk": "~0.4.0", - "generator-ng-component": "~0.0.4", + "generator-ng-component": "~0.1.0", "yeoman-generator": "~0.18.10" }, "devDependencies": { From d7fc0a80abda86c0cdca0de9e7748bb81e1b92ff Mon Sep 17 00:00:00 2001 From: Denis Ciccale Date: Fri, 7 Aug 2015 10:16:42 +0200 Subject: [PATCH 131/171] chore(user): change status code 204 for empty response body --- app/templates/server/api/user(auth)/user.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/server/api/user(auth)/user.controller.js b/app/templates/server/api/user(auth)/user.controller.js index d074c666b..f1c2498fb 100644 --- a/app/templates/server/api/user(auth)/user.controller.js +++ b/app/templates/server/api/user(auth)/user.controller.js @@ -128,7 +128,7 @@ exports.changePassword = function(req, res, next) { <% if (filters.mongooseModels) { %>return user.saveAsync()<% } if (filters.sequelizeModels) { %>return user.save()<% } %> .then(function() { - res.status(200).end(); + res.status(204).end(); }) .catch(validationError(res)); } else { From 63782b360e3c7f12bce073de0a4bfe8817e03dbb Mon Sep 17 00:00:00 2001 From: Dan Rocha Date: Sat, 8 Aug 2015 00:52:31 -0500 Subject: [PATCH 132/171] feat(package): upgrade jshint packages --- app/templates/_package.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/templates/_package.json b/app/templates/_package.json index 31b5aa5d5..c9b1533be 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -44,7 +44,7 @@ "grunt-contrib-cssmin": "~0.9.0", "grunt-contrib-htmlmin": "~0.2.0", "grunt-contrib-imagemin": "~0.7.1", - "grunt-contrib-jshint": "~0.10.0", + "grunt-contrib-jshint": "~0.11.2", "grunt-contrib-uglify": "~0.4.0", "grunt-contrib-watch": "~0.6.1",<% if (filters.coffee) { %> "grunt-contrib-coffee": "^0.10.1",<% } %><% if (filters.jade) { %> @@ -76,7 +76,7 @@ "grunt-postcss": "^0.5.5", "grunt-open": "~0.2.3", "open": "~0.0.4", - "jshint-stylish": "~0.1.5", + "jshint-stylish": "~2.0.1", "connect-livereload": "^0.5.3", "mocha": "^2.2.5", "grunt-mocha-test": "~0.12.7", diff --git a/package.json b/package.json index f66b72cb0..e576c2ca9 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "grunt": "~0.4.1", "grunt-build-control": "DaftMonk/grunt-build-control", "grunt-contrib-clean": "^0.6.0", - "grunt-contrib-jshint": "^0.10.0", + "grunt-contrib-jshint": "^0.11.2", "grunt-conventional-changelog": "~1.0.0", "grunt-env": "^0.4.1", "grunt-mocha-test": "^0.11.0", From ea2a7331cfdb3fd7d0c0ee0f8f6a7ba021dd7647 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Tue, 11 Aug 2015 10:56:00 -0400 Subject: [PATCH 133/171] fix(server:errors): fix res.render syntax --- app/templates/server/components/errors/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/templates/server/components/errors/index.js b/app/templates/server/components/errors/index.js index 52dba3749..ba71c73ba 100644 --- a/app/templates/server/components/errors/index.js +++ b/app/templates/server/components/errors/index.js @@ -12,11 +12,11 @@ module.exports[404] = function pageNotFound(req, res) { }; res.status(result.status); - res.render(viewFilePath, function(err) { + res.render(viewFilePath, {}, function(err, html) { if (err) { return res.json(result, result.status); } - res.render(viewFilePath); + res.send(html); }); }; From 2617ab28a772af21cbb21597cb36bb47a7ea6a00 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Tue, 11 Aug 2015 11:08:32 -0400 Subject: [PATCH 134/171] feat(client:main): use times symbol instead of 'x' in jade template --- app/templates/client/app/main/main(jade).jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/client/app/main/main(jade).jade b/app/templates/client/app/main/main(jade).jade index e791e96ab..3277e7b05 100644 --- a/app/templates/client/app/main/main(jade).jade +++ b/app/templates/client/app/main/main(jade).jade @@ -14,7 +14,7 @@ header#banner.hero-unit li a(href='#', tooltip='{{thing.info}}') | {{thing.name}}<% if (filters.socketio) { %> - button.close(type='button', ng-click='deleteThing(thing)') ×<% } %><% if (filters.socketio) { %> + button.close(type='button', ng-click='deleteThing(thing)') ×<% } %><% if (filters.socketio) { %> form.thing-form label Syncs in realtime across clients From bb394eeff9deb119b40b1c75cabd4c15d8931506 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Tue, 11 Aug 2015 16:12:29 -0400 Subject: [PATCH 135/171] feat(gen): move babel choice to script list --- app/index.js | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/app/index.js b/app/index.js index 4ae1ad871..e25388d7b 100644 --- a/app/index.js +++ b/app/index.js @@ -92,21 +92,13 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ type: 'list', name: 'script', message: 'What would you like to write scripts with?', - choices: [ 'JavaScript', 'CoffeeScript'], + choices: [ 'JavaScript', 'JavaScript + Babel', 'CoffeeScript'], filter: function( val ) { - var filterMap = { + return { 'JavaScript': 'js', + 'JavaScript + Babel': 'babel', 'CoffeeScript': 'coffee' - }; - - return filterMap[val]; - } - }, { - type: 'confirm', - name: 'babel', - message: 'Would you like to use Javascript ES6 in your client by preprocessing it with Babel?', - when: function (answers) { - return answers.script === 'js'; + }[val]; } }, { type: 'list', @@ -141,8 +133,8 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ } }], function (answers) { - this.filters.babel = !!answers.babel; - if(this.filters.babel){ this.filters.js = true; } + // also set 'js' to true if using babel + if(answers.script === 'babel') { this.filters.js = true; } this.filters[answers.script] = true; this.filters[answers.markup] = true; this.filters[answers.stylesheet] = true; From fb915b29d07044b09f5014b317b2303b94f47252 Mon Sep 17 00:00:00 2001 From: kingcody Date: Tue, 28 Jul 2015 17:59:38 -0400 Subject: [PATCH 136/171] fix(gen): `endpointDirectory` is now a full option/config --- app/index.js | 1 + endpoint/index.js | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/index.js b/app/index.js index 4ae1ad871..f31a71fa7 100644 --- a/app/index.js +++ b/app/index.js @@ -316,6 +316,7 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ saveSettings: function() { if(this.skipConfig) return; + this.config.set('endpointDirectory', 'server/api/'); this.config.set('insertRoutes', true); this.config.set('registerRoutesFile', 'server/routes.js'); this.config.set('routesNeedle', '// Insert routes below'); diff --git a/endpoint/index.js b/endpoint/index.js index 22e8e8950..01d4486df 100644 --- a/endpoint/index.js +++ b/endpoint/index.js @@ -7,6 +7,11 @@ var ScriptBase = require('../script-base.js'); var Generator = module.exports = function Generator() { ScriptBase.apply(this, arguments); + + this.option('endpointDirectory', { + desc: 'Parent directory for enpoints', + type: String + }); }; util.inherits(Generator, ScriptBase); @@ -66,10 +71,14 @@ Generator.prototype.prompting = function askFor() { }.bind(this)); }; +Generator.prototype.configuring = function config() { + this.routeDest = path.join(this.options.endpointDirectory || + this.config.get('endpointDirectory') || 'server/api/', this.name); +}; + Generator.prototype.writing = function createFiles() { - var dest = this.config.get('endpointDirectory') || 'server/api/' + this.name; this.sourceRoot(path.join(__dirname, './templates')); - ngUtil.processDirectory(this, '.', dest); + ngUtil.processDirectory(this, '.', this.routeDest); }; Generator.prototype.end = function registerEndpoint() { From 3fcd8e909c29ee4903a083ef6bce83b9fa1b93dc Mon Sep 17 00:00:00 2001 From: kingcody Date: Tue, 28 Jul 2015 18:03:53 -0400 Subject: [PATCH 137/171] feat(gen): add `relativeRequire` and other template helpers --- script-base.js | 15 +++++++++++++-- util.js | 25 ++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/script-base.js b/script-base.js index 5d6c7dd2e..d88cd988c 100644 --- a/script-base.js +++ b/script-base.js @@ -15,8 +15,13 @@ var Generator = module.exports = function Generator() { this.appname = this._.slugify(this._.humanize(this.appname)); this.scriptAppName = this._.camelize(this.appname) + angularUtils.appName(this); - this.cameledName = this._.camelize(this.name); - this.classedName = this._.classify(this.name); + var name = this.name.replace(/\//g, '-'); + + this.cameledName = this._.camelize(name); + this.classedName = this._.classify(name); + + this.basename = path.basename(this.name); + this.dirname = (this.name.indexOf('/') >= 0) ? path.dirname(this.name) : this.name; // dynamic assertion statement this.does = this.is = function(foo) { @@ -27,6 +32,12 @@ var Generator = module.exports = function Generator() { } }.bind(this); + // dynamic relative require path + this.relativeRequire = function(to, fr) { + fr = fr || this.filePath; + return angularUtils.relativeRequire(this, to, fr); + }.bind(this); + this.filters = this.config.get('filters'); this.sourceRoot(path.join(__dirname, '/templates')); }; diff --git a/util.js b/util.js index 4bf2170c4..6657cfc9f 100644 --- a/util.js +++ b/util.js @@ -6,7 +6,8 @@ module.exports = { rewrite: rewrite, rewriteFile: rewriteFile, appName: appName, - processDirectory: processDirectory + processDirectory: processDirectory, + relativeRequire: relativeRequire }; function rewriteFile (args) { @@ -74,6 +75,23 @@ function appName (self) { return suffix ? self._.classify(suffix) : ''; } +function destinationPath (self, filepath) { + filepath = path.normalize(filepath); + if (!path.isAbsolute(filepath)) { + filepath = path.join(self.destinationRoot(), filepath); + } + + return filepath; +} + +function relativeRequire (self, to, fr) { + fr = destinationPath(self, fr); + to = destinationPath(self, to); + return path.relative(path.dirname(fr), to) + .replace(/^(?!\.\.)(.*)/, './$1') + .replace(/[\/\\]index\.js$/, ''); +} + function filterFile (template) { // Find matches for parans var filterMatches = template.match(/\(([^)]+)\)/g); @@ -109,6 +127,9 @@ function processDirectory (self, source, destination) { files.forEach(function(f) { var filteredFile = filterFile(f); + if(self.basename) { + filteredFile.name = filteredFile.name.replace('basename', self.basename); + } if(self.name) { filteredFile.name = filteredFile.name.replace('name', self.name); } @@ -133,7 +154,9 @@ function processDirectory (self, source, destination) { if(copy) { self.copy(src, dest); } else { + self.filePath = dest; self.template(src, dest); + delete self.filePath; } } }); From 5227ef033fa80f51c7a764c99d99780d4bad958a Mon Sep 17 00:00:00 2001 From: kingcody Date: Tue, 28 Jul 2015 18:16:15 -0400 Subject: [PATCH 138/171] fix(gen): endpoint accepts path as name --- app/index.js | 18 ++- endpoint/index.js | 119 +++++++++++------- ...e.controller.js => basename.controller.js} | 14 +-- ...(models).js => basename.events(models).js} | 4 +- ...integration.js => basename.integration.js} | 20 +-- ...).js => basename.model(mongooseModels).js} | 0 ....js => basename.model(sequelizeModels).js} | 0 ...ketio).js => basename.socket(socketio).js} | 4 +- endpoint/templates/index.js | 2 +- endpoint/templates/index.spec.js | 36 +++--- 10 files changed, 124 insertions(+), 93 deletions(-) rename endpoint/templates/{name.controller.js => basename.controller.js} (89%) rename endpoint/templates/{name.events(models).js => basename.events(models).js} (84%) rename endpoint/templates/{name.integration.js => basename.integration.js} (84%) rename endpoint/templates/{name.model(mongooseModels).js => basename.model(mongooseModels).js} (100%) rename endpoint/templates/{name.model(sequelizeModels).js => basename.model(sequelizeModels).js} (100%) rename endpoint/templates/{name.socket(socketio).js => basename.socket(socketio).js} (82%) diff --git a/app/index.js b/app/index.js index f31a71fa7..99f1e3995 100644 --- a/app/index.js +++ b/app/index.js @@ -400,11 +400,19 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ }, generateEndpoint: function() { - var name = this.name = this.cameledName = 'thing'; - this.classedName = name.charAt(0).toUpperCase() + name.slice(1); - this.route = '/api/' + name + 's'; - this.sourceRoot(path.join(__dirname, '..', 'endpoint', 'templates')); - genUtils.processDirectory(this, '.', 'server/api/' + name); + var models; + if (this.filters.mongooseModels) { + models = 'mongoose'; + } else if (this.filters.sequelizeModels) { + models = 'sequelize'; + } + this.composeWith('angular-fullstack:endpoint', { + options: { + route: '/api/things', + models: models + }, + args: ['thing'] + }); } }, diff --git a/endpoint/index.js b/endpoint/index.js index 01d4486df..cbb8d5aeb 100644 --- a/endpoint/index.js +++ b/endpoint/index.js @@ -8,6 +8,16 @@ var ScriptBase = require('../script-base.js'); var Generator = module.exports = function Generator() { ScriptBase.apply(this, arguments); + this.option('route', { + desc: 'URL for the endpoint', + type: String + }); + + this.option('models', { + desc: 'Specify which model(s) to use', + type: String + }); + this.option('endpointDirectory', { desc: 'Parent directory for enpoints', type: String @@ -18,6 +28,38 @@ util.inherits(Generator, ScriptBase); Generator.prototype.prompting = function askFor() { var done = this.async(); + var promptCb = function (props) { + if(props.route.charAt(0) !== '/') { + props.route = '/' + props.route; + } + + this.route = props.route; + + if (props.models) { + delete this.filters.mongoose; + delete this.filters.mongooseModels; + delete this.filters.sequelize; + delete this.filters.sequelizeModels; + + this.filters[props.models] = true; + this.filters[props.models + 'Models'] = true; + } + done(); + }.bind(this); + + if (this.options.route) { + if (this.filters.mongoose && this.filters.sequelize) { + if (this.options.models) { + return promptCb(this.options); + } + } else { + if (this.filters.mongooseModels) { this.options.models = 'mongoose'; } + else if (this.filters.sequelizeModels) { this.options.models = 'sequelize'; } + else { delete this.options.models; } + return promptCb(this.options); + } + } + var name = this.name; var base = this.config.get('routesBase') || '/api/'; @@ -51,24 +93,7 @@ Generator.prototype.prompting = function askFor() { } ]; - this.prompt(prompts, function (props) { - if(props.route.charAt(0) !== '/') { - props.route = '/' + props.route; - } - - this.route = props.route; - - if (props.models) { - delete this.filters.mongoose; - delete this.filters.mongooseModels; - delete this.filters.sequelize; - delete this.filters.sequelizeModels; - - this.filters[props.models] = true; - this.filters[props.models + 'Models'] = true; - } - done(); - }.bind(this)); + this.prompt(prompts, promptCb); }; Generator.prototype.configuring = function config() { @@ -83,45 +108,43 @@ Generator.prototype.writing = function createFiles() { Generator.prototype.end = function registerEndpoint() { if(this.config.get('insertRoutes')) { + var routesFile = this.config.get('registerRoutesFile'); + var reqPath = this.relativeRequire(this.routeDest, routesFile); var routeConfig = { - file: this.config.get('registerRoutesFile'), + file: routesFile, needle: this.config.get('routesNeedle'), splicable: [ - "app.use(\'" + this.route +"\', require(\'./api/" + this.name + "\'));" + "app.use(\'" + this.route +"\', require(\'" + reqPath + "\'));" ] }; ngUtil.rewriteFile(routeConfig); } - if (this.filters.socketio) { - if(this.config.get('insertSockets')) { - var socketConfig = { - file: this.config.get('registerSocketsFile'), - needle: this.config.get('socketsNeedle'), - splicable: [ - "require(\'../api/" + this.name + '/' + this.name + ".socket\').register(socket);" - ] - }; - ngUtil.rewriteFile(socketConfig); - } + if (this.filters.socketio && this.config.get('insertSockets')) { + var socketsFile = this.config.get('registerSocketsFile'); + var reqPath = this.relativeRequire(this.routeDest + '/' + this.basename + + '.socket', socketsFile); + var socketConfig = { + file: socketsFile, + needle: this.config.get('socketsNeedle'), + splicable: [ + "require(\'" + reqPath + "\').register(socket);" + ] + }; + ngUtil.rewriteFile(socketConfig); } - if (this.filters.sequelize) { - if (this.config.get('insertModels')) { - var modelConfig = { - file: this.config.get('registerModelsFile'), - needle: this.config.get('modelsNeedle'), - splicable: [ - "db." + this.classedName + " = db.sequelize.import(path.join(\n" + - " config.root,\n" + - " 'server',\n" + - " 'api',\n" + - " '" + this.name + "',\n" + - " '" + this.name + ".model'\n" + - "));" - ] - }; - ngUtil.rewriteFile(modelConfig); - } + if (this.filters.sequelize && this.config.get('insertModels')) { + var modelsFile = this.config.get('registerModelsFile'); + var reqPath = this.relativeRequire(this.routeDest + '/' + this.basename + + '.model', modelsFile); + var modelConfig = { + file: modelsFile, + needle: this.config.get('modelsNeedle'), + splicable: [ + "db." + this.classedName + " = db.sequelize.import(\'" + reqPath +"\');" + ] + }; + ngUtil.rewriteFile(modelConfig); } }; diff --git a/endpoint/templates/name.controller.js b/endpoint/templates/basename.controller.js similarity index 89% rename from endpoint/templates/name.controller.js rename to endpoint/templates/basename.controller.js index abcc5f34b..cd151b13a 100644 --- a/endpoint/templates/name.controller.js +++ b/endpoint/templates/basename.controller.js @@ -10,8 +10,8 @@ 'use strict';<% if (filters.models) { %> var _ = require('lodash');<% if (filters.mongooseModels) { %> -var <%= classedName %> = require('./<%= name %>.model');<% } if (filters.sequelizeModels) { %> -var sqldb = require('../../sqldb'); +var <%= classedName %> = require('./<%= basename %>.model');<% } if (filters.sequelizeModels) { %> +var sqldb = require('<%= relativeRequire(config.get('registerModelsFile')) %>'); var <%= classedName %> = sqldb.<%= classedName %>;<% } %> function handleError(res, statusCode) { @@ -64,7 +64,7 @@ function removeEntity(res) { }; }<% } %> -// Gets a list of <%= name %>s +// Gets a list of <%= classedName %>s exports.index = function(req, res) {<% if (!filters.models) { %> res.json([]);<% } else { %> <% if (filters.mongooseModels) { %><%= classedName %>.findAsync()<% } @@ -73,7 +73,7 @@ exports.index = function(req, res) {<% if (!filters.models) { %> .catch(handleError(res));<% } %> };<% if (filters.models) { %> -// Gets a single <%= name %> from the DB +// Gets a single <%= classedName %> from the DB exports.show = function(req, res) { <% if (filters.mongooseModels) { %><%= classedName %>.findByIdAsync(req.params.id)<% } if (filters.sequelizeModels) { %><%= classedName %>.find({ @@ -86,7 +86,7 @@ exports.show = function(req, res) { .catch(handleError(res)); }; -// Creates a new <%= name %> in the DB +// Creates a new <%= classedName %> in the DB exports.create = function(req, res) { <% if (filters.mongooseModels) { %><%= classedName %>.createAsync(req.body)<% } if (filters.sequelizeModels) { %><%= classedName %>.create(req.body)<% } %> @@ -94,7 +94,7 @@ exports.create = function(req, res) { .catch(handleError(res)); }; -// Updates an existing <%= name %> in the DB +// Updates an existing <%= classedName %> in the DB exports.update = function(req, res) { if (req.body._id) { delete req.body._id; @@ -111,7 +111,7 @@ exports.update = function(req, res) { .catch(handleError(res)); }; -// Deletes a <%= name %> from the DB +// Deletes a <%= classedName %> from the DB exports.destroy = function(req, res) { <% if (filters.mongooseModels) { %><%= classedName %>.findByIdAsync(req.params.id)<% } if (filters.sequelizeModels) { %><%= classedName %>.find({ diff --git a/endpoint/templates/name.events(models).js b/endpoint/templates/basename.events(models).js similarity index 84% rename from endpoint/templates/name.events(models).js rename to endpoint/templates/basename.events(models).js index 8bda5cf9a..f39b5b0be 100644 --- a/endpoint/templates/name.events(models).js +++ b/endpoint/templates/basename.events(models).js @@ -5,8 +5,8 @@ 'use strict'; var EventEmitter = require('events').EventEmitter;<% if (filters.mongooseModels) { %> -var <%= classedName %> = require('./<%= name %>.model');<% } if (filters.sequelizeModels) { %> -var <%= classedName %> = require('../../sqldb').<%= classedName %>;<% } %> +var <%= classedName %> = require('./<%= basename %>.model');<% } if (filters.sequelizeModels) { %> +var <%= classedName %> = require('<%= relativeRequire(config.get('registerModelsFile')) %>').<%= classedName %>;<% } %> var <%= classedName %>Events = new EventEmitter(); // Set max event listeners (0 == unlimited) diff --git a/endpoint/templates/name.integration.js b/endpoint/templates/basename.integration.js similarity index 84% rename from endpoint/templates/name.integration.js rename to endpoint/templates/basename.integration.js index 160cdedfa..bcd3fd407 100644 --- a/endpoint/templates/name.integration.js +++ b/endpoint/templates/basename.integration.js @@ -1,6 +1,6 @@ 'use strict'; -var app = require('../../app'); +var app = require('<%= relativeRequire('server/app') %>'); var request = require('supertest');<% if(filters.models) { %> var new<%= classedName %>;<% } %> @@ -36,7 +36,7 @@ describe('<%= classedName %> API:', function() { .post('<%= route %>') .send({ name: 'New <%= classedName %>', - info: 'This is the brand new <%= name %>!!!' + info: 'This is the brand new <%= cameledName %>!!!' }) .expect(201) .expect('Content-Type', /json/) @@ -49,9 +49,9 @@ describe('<%= classedName %> API:', function() { }); }); - it('should respond with the newly created <%= name %>', function() { + it('should respond with the newly created <%= cameledName %>', function() { new<%= classedName %>.name.should.equal('New <%= classedName %>'); - new<%= classedName %>.info.should.equal('This is the brand new <%= name %>!!!'); + new<%= classedName %>.info.should.equal('This is the brand new <%= cameledName %>!!!'); }); }); @@ -77,9 +77,9 @@ describe('<%= classedName %> API:', function() { <%= cameledName %> = {}; }); - it('should respond with the requested <%= name %>', function() { + it('should respond with the requested <%= cameledName %>', function() { <%= cameledName %>.name.should.equal('New <%= classedName %>'); - <%= cameledName %>.info.should.equal('This is the brand new <%= name %>!!!'); + <%= cameledName %>.info.should.equal('This is the brand new <%= cameledName %>!!!'); }); }); @@ -92,7 +92,7 @@ describe('<%= classedName %> API:', function() { .put('<%= route %>/' + new<%= classedName %>._id) .send({ name: 'Updated <%= classedName %>', - info: 'This is the updated <%= name %>!!!' + info: 'This is the updated <%= cameledName %>!!!' }) .expect(200) .expect('Content-Type', /json/) @@ -109,9 +109,9 @@ describe('<%= classedName %> API:', function() { updated<%= classedName %> = {}; }); - it('should respond with the updated <%= name %>', function() { + it('should respond with the updated <%= cameledName %>', function() { updated<%= classedName %>.name.should.equal('Updated <%= classedName %>'); - updated<%= classedName %>.info.should.equal('This is the updated <%= name %>!!!'); + updated<%= classedName %>.info.should.equal('This is the updated <%= cameledName %>!!!'); }); }); @@ -130,7 +130,7 @@ describe('<%= classedName %> API:', function() { }); }); - it('should respond with 404 when <%= name %> does not exist', function(done) { + it('should respond with 404 when <%= cameledName %> does not exist', function(done) { request(app) .delete('<%= route %>/' + new<%= classedName %>._id) .expect(404) diff --git a/endpoint/templates/name.model(mongooseModels).js b/endpoint/templates/basename.model(mongooseModels).js similarity index 100% rename from endpoint/templates/name.model(mongooseModels).js rename to endpoint/templates/basename.model(mongooseModels).js diff --git a/endpoint/templates/name.model(sequelizeModels).js b/endpoint/templates/basename.model(sequelizeModels).js similarity index 100% rename from endpoint/templates/name.model(sequelizeModels).js rename to endpoint/templates/basename.model(sequelizeModels).js diff --git a/endpoint/templates/name.socket(socketio).js b/endpoint/templates/basename.socket(socketio).js similarity index 82% rename from endpoint/templates/name.socket(socketio).js rename to endpoint/templates/basename.socket(socketio).js index 24d744d13..037f6113a 100644 --- a/endpoint/templates/name.socket(socketio).js +++ b/endpoint/templates/basename.socket(socketio).js @@ -4,7 +4,7 @@ 'use strict'; -var <%= classedName %>Events = require('./<%= name %>.events'); +var <%= classedName %>Events = require('./<%= basename %>.events'); // Model events to emit var events = ['save', 'remove']; @@ -13,7 +13,7 @@ exports.register = function(socket) { // Bind model events to socket events for (var i = 0, eventsLength = events.length; i < eventsLength; i++) { var event = events[i]; - var listener = createListener('<%= name %>:' + event, socket); + var listener = createListener('<%= cameledName %>:' + event, socket); <%= classedName %>Events.on(event, listener); socket.on('disconnect', removeListener(event, listener)); diff --git a/endpoint/templates/index.js b/endpoint/templates/index.js index 60198d142..26dc430dd 100644 --- a/endpoint/templates/index.js +++ b/endpoint/templates/index.js @@ -1,7 +1,7 @@ 'use strict'; var express = require('express'); -var controller = require('./<%= name %>.controller'); +var controller = require('./<%= basename %>.controller'); var router = express.Router(); diff --git a/endpoint/templates/index.spec.js b/endpoint/templates/index.spec.js index 291a04662..4bd178948 100644 --- a/endpoint/templates/index.spec.js +++ b/endpoint/templates/index.spec.js @@ -3,11 +3,11 @@ var proxyquire = require('proxyquire').noPreserveCache(); var <%= cameledName %>CtrlStub = { - index: '<%= name %>Ctrl.index'<% if(filters.models) { %>, - show: '<%= name %>Ctrl.show', - create: '<%= name %>Ctrl.create', - update: '<%= name %>Ctrl.update', - destroy: '<%= name %>Ctrl.destroy'<% } %> + index: '<%= cameledName %>Ctrl.index'<% if(filters.models) { %>, + show: '<%= cameledName %>Ctrl.show', + create: '<%= cameledName %>Ctrl.create', + update: '<%= cameledName %>Ctrl.update', + destroy: '<%= cameledName %>Ctrl.destroy'<% } %> }; var routerStub = { @@ -25,7 +25,7 @@ var <%= cameledName %>Index = proxyquire('./index.js', { return routerStub; } }, - './<%= name %>.controller': <%= cameledName %>CtrlStub + './<%= basename %>.controller': <%= cameledName %>CtrlStub }); describe('<%= classedName %> API Router:', function() { @@ -36,9 +36,9 @@ describe('<%= classedName %> API Router:', function() { describe('GET <%= route %>', function() { - it('should route to <%= name %>.controller.index', function() { + it('should route to <%= cameledName %>.controller.index', function() { routerStub.get - .withArgs('/', '<%= name %>Ctrl.index') + .withArgs('/', '<%= cameledName %>Ctrl.index') .should.have.been.calledOnce; }); @@ -46,9 +46,9 @@ describe('<%= classedName %> API Router:', function() { describe('GET <%= route %>/:id', function() { - it('should route to <%= name %>.controller.show', function() { + it('should route to <%= cameledName %>.controller.show', function() { routerStub.get - .withArgs('/:id', '<%= name %>Ctrl.show') + .withArgs('/:id', '<%= cameledName %>Ctrl.show') .should.have.been.calledOnce; }); @@ -56,9 +56,9 @@ describe('<%= classedName %> API Router:', function() { describe('POST <%= route %>', function() { - it('should route to <%= name %>.controller.create', function() { + it('should route to <%= cameledName %>.controller.create', function() { routerStub.post - .withArgs('/', '<%= name %>Ctrl.create') + .withArgs('/', '<%= cameledName %>Ctrl.create') .should.have.been.calledOnce; }); @@ -66,9 +66,9 @@ describe('<%= classedName %> API Router:', function() { describe('PUT <%= route %>/:id', function() { - it('should route to <%= name %>.controller.update', function() { + it('should route to <%= cameledName %>.controller.update', function() { routerStub.put - .withArgs('/:id', '<%= name %>Ctrl.update') + .withArgs('/:id', '<%= cameledName %>Ctrl.update') .should.have.been.calledOnce; }); @@ -76,9 +76,9 @@ describe('<%= classedName %> API Router:', function() { describe('PATCH <%= route %>/:id', function() { - it('should route to <%= name %>.controller.update', function() { + it('should route to <%= cameledName %>.controller.update', function() { routerStub.patch - .withArgs('/:id', '<%= name %>Ctrl.update') + .withArgs('/:id', '<%= cameledName %>Ctrl.update') .should.have.been.calledOnce; }); @@ -86,9 +86,9 @@ describe('<%= classedName %> API Router:', function() { describe('DELETE <%= route %>/:id', function() { - it('should route to <%= name %>.controller.destroy', function() { + it('should route to <%= cameledName %>.controller.destroy', function() { routerStub.delete - .withArgs('/:id', '<%= name %>Ctrl.destroy') + .withArgs('/:id', '<%= cameledName %>Ctrl.destroy') .should.have.been.calledOnce; }); From 40277fdb35478df8c4860ed2bed2997bb2b4edb4 Mon Sep 17 00:00:00 2001 From: kingcody Date: Wed, 29 Jul 2015 07:08:59 -0400 Subject: [PATCH 139/171] chore(dependencies): update sequelize --- app/templates/_package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/_package.json b/app/templates/_package.json index c9b1533be..f72c18ca5 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -19,7 +19,7 @@ "mongoose": "~4.0.3", "mongoose-bird": "~0.0.1", "connect-mongo": "^0.8.1",<% } %><% if (filters.sequelize) { %> - "sequelize": "^2.0.0-rc2", + "sequelize": "^3.5.1", "sqlite3": "~3.0.2",<% } %><% if (filters.auth) { %> "jsonwebtoken": "^5.0.0", "express-jwt": "^3.0.0", From 63fae5cb2c8e0b21e5140ebd522a8b8d768c075b Mon Sep 17 00:00:00 2001 From: kingcody Date: Wed, 29 Jul 2015 07:09:40 -0400 Subject: [PATCH 140/171] fix(gen): remove thing import from sequelize template --- .../server/sqldb(sequelize)/index.js | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/app/templates/server/sqldb(sequelize)/index.js b/app/templates/server/sqldb(sequelize)/index.js index 231db9823..2500a2213 100644 --- a/app/templates/server/sqldb(sequelize)/index.js +++ b/app/templates/server/sqldb(sequelize)/index.js @@ -13,23 +13,8 @@ var db = { Sequelize: Sequelize, sequelize: new Sequelize(config.sequelize.uri, config.sequelize.options) }; -<% if (filters.sequelizeModels) { %> -db.Thing = db.sequelize.import(path.join( - config.root, - 'server', - 'api', - 'thing', - 'thing.model' -)); -<% if (filters.auth) { %> -db.User = db.sequelize.import(path.join( - config.root, - 'server', - 'api', - 'user', - 'user.model' -)); -<% } %><% } %> -// Insert models below + +// Insert models below<% if (filters.sequelizeModels && filters.auth) { %> +db.User = db.sequelize.import('../api/user/user.model');<% } %> module.exports = db; From 06eeab4312bde750819366b40ab062fd5c8b7465 Mon Sep 17 00:00:00 2001 From: kingcody Date: Sun, 2 Aug 2015 05:23:26 -0400 Subject: [PATCH 141/171] fix(test): add endpoint sub-gen as test dependency --- test/test-file-creation.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test-file-creation.js b/test/test-file-creation.js index ce8635c0b..21b4c69fa 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -314,6 +314,7 @@ describe('angular-fullstack generator', function () { this.timeout(10000); var deps = [ '../../app', + '../../endpoint', [ helpers.createDummyGenerator(), 'ng-component:app' @@ -421,6 +422,7 @@ describe('angular-fullstack generator', function () { fs.copySync(__dirname + '/fixtures/.yo-rc.json', __dirname + '/temp/.yo-rc.json'); var gen = helpers.createGenerator('angular-fullstack:app', [ '../../app', + '../../endpoint', [ helpers.createDummyGenerator(), 'ng-component:app' @@ -445,6 +447,7 @@ describe('angular-fullstack generator', function () { fs.copySync(__dirname + '/fixtures/.yo-rc.json', __dirname + '/temp/.yo-rc.json'); var gen = helpers.createGenerator('angular-fullstack:app', [ '../../app', + '../../endpoint', [ helpers.createDummyGenerator(), 'ng-component:app' From c18824a8bbdeedf84807fc04d08eaa3ff7246772 Mon Sep 17 00:00:00 2001 From: kingcody Date: Sun, 2 Aug 2015 06:36:29 -0400 Subject: [PATCH 142/171] test(endpoint): test against a path name endpoint --- test/test-file-creation.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/test-file-creation.js b/test/test-file-creation.js index 21b4c69fa..fb0fb2b91 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -417,6 +417,29 @@ describe('angular-fullstack generator', function () { 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() { + helpers.assertFile([ + '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); fs.copySync(__dirname + '/fixtures/.yo-rc.json', __dirname + '/temp/.yo-rc.json'); From 3b6650710ff7b212e4fc9e74b98c7bac719c397f Mon Sep 17 00:00:00 2001 From: kingcody Date: Mon, 10 Aug 2015 00:25:45 -0400 Subject: [PATCH 143/171] feat(gen): add david grunt task --- Gruntfile.js | 15 +++++++++++++++ package.json | 1 + 2 files changed, 16 insertions(+) diff --git a/Gruntfile.js b/Gruntfile.js index c81bffa5c..befea75e5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -87,6 +87,16 @@ module.exports = function (grunt) { ] }] } + }, + david: { + gen: { + options: {} + }, + app: { + options: { + package: 'test/fixtures/package.json' + } + } } }); @@ -271,6 +281,11 @@ module.exports = function (grunt) { ]) }); + grunt.registerTask('deps', function(target) { + if (!target || target === 'app') grunt.task.run(['updateFixtures']); + grunt.task.run(['david:' + (target || '')]); + }); + grunt.registerTask('demo', [ 'clean:demo', 'generateDemo' diff --git a/package.json b/package.json index f20b54725..7dfa26f05 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "grunt-contrib-clean": "^0.6.0", "grunt-contrib-jshint": "^0.11.2", "grunt-conventional-changelog": "~1.0.0", + "grunt-david": "~0.5.0", "grunt-env": "^0.4.1", "grunt-mocha-test": "^0.11.0", "grunt-release": "~0.6.0", From 1d5ab7db3b71110b30aa9fc0a3a3601c47e6e72f Mon Sep 17 00:00:00 2001 From: kingcody Date: Wed, 12 Aug 2015 06:01:22 -0400 Subject: [PATCH 144/171] feat(app): server ships with babel --- app/templates/README.md | 3 +-- app/templates/_package.json | 4 ++-- app/templates/server/index.js | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/templates/README.md b/app/templates/README.md index bb4def0ce..e49273a90 100644 --- a/app/templates/README.md +++ b/app/templates/README.md @@ -11,8 +11,7 @@ This project was generated with the [Angular Full-Stack Generator](https://githu - [Bower](bower.io) (`npm install --global bower`)<% if(filters.sass) { %> - [Ruby](https://www.ruby-lang.org) and then `gem install sass`<% } if(filters.grunt) { %> - [Grunt](http://gruntjs.com/) (`npm install --global grunt-cli`)<% } if(filters.gulp) { %> -- [Gulp](http://gulpjs.com/) (`npm install --global gulp`)<% } if(filters.babel) { %> -- [Babel](https://babeljs.io) (`npm install --global babel`)<% } if(filters.mongoose) { %> +- [Gulp](http://gulpjs.com/) (`npm install --global gulp`)<% } if(filters.mongoose) { %> - [MongoDB](https://www.mongodb.org/) - Keep a running daemon with `mongod`<% } if(filters.sequelize) { %> - [SQLite](https://www.sqlite.org/quickstart.html)<% } %> diff --git a/app/templates/_package.json b/app/templates/_package.json index f72c18ca5..7ea2eda4b 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -12,8 +12,8 @@ "errorhandler": "~1.0.0", "compression": "~1.0.1", "composable-middleware": "^0.3.0", - "lodash": "~2.4.1",<% if(filters.babel) { %> - "babel-core": "^5.6.4",<% } %><% if (filters.jade) { %> + "lodash": "~2.4.1", + "babel-core": "^5.6.4",<% if (filters.jade) { %> "jade": "~1.2.0",<% } %><% if (filters.html) { %> "ejs": "~0.8.4",<% } %><% if (filters.mongoose) { %> "mongoose": "~4.0.3", diff --git a/app/templates/server/index.js b/app/templates/server/index.js index 7722a0e6c..fc65cd5f4 100644 --- a/app/templates/server/index.js +++ b/app/templates/server/index.js @@ -1,7 +1,7 @@ -'use strict';<% if (filters.babel) { %> +'use strict'; // Register the Babel require hook -require('babel-core/register');<% } %> +require('babel-core/register'); // Export the application exports = module.exports = require('./app'); From 733843e68348a96a5444fd37640d1726dc517701 Mon Sep 17 00:00:00 2001 From: Jinyoung Kim Date: Wed, 12 Aug 2015 22:32:37 +0900 Subject: [PATCH 145/171] chore(dependencies): remove angular-scenario --- app/templates/Gruntfile.js | 1 - app/templates/_bower.json | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index bd4559db6..8788038b1 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -245,7 +245,6 @@ module.exports = function (grunt) { wiredep: { options: { exclude: [ - /angular-scenario/, /bootstrap-sass-official/, /bootstrap.js/, '/json3/', diff --git a/app/templates/_bower.json b/app/templates/_bower.json index 5798b8a73..1d41b39d1 100644 --- a/app/templates/_bower.json +++ b/app/templates/_bower.json @@ -18,7 +18,6 @@ "angular-ui-router": "~0.2.15"<% } %> }, "devDependencies": { - "angular-mocks": "~1.4.0", - "angular-scenario": "~1.4.0" + "angular-mocks": "~1.4.0" } } From a849395d67290dd68721eb4f9871db2f1d3c6b66 Mon Sep 17 00:00:00 2001 From: Jinyoung Kim Date: Wed, 12 Aug 2015 22:35:49 +0900 Subject: [PATCH 146/171] docs(readme): remove angular-senario in bower list --- readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/readme.md b/readme.md index af727cdb8..f5f37e9e1 100644 --- a/readme.md +++ b/readme.md @@ -302,7 +302,6 @@ The following packages are always installed by the [app](#app) generator: * angular-mocks * angular-resource * angular-sanitize -* angular-scenario * es5-shim * font-awesome * json3 From c8e2f0f36e373bb22596c36fcb74cc3695f02e9a Mon Sep 17 00:00:00 2001 From: kingcody Date: Wed, 12 Aug 2015 23:05:29 -0400 Subject: [PATCH 147/171] feat(gen): test helper `does` accepts escaped template strings --- app/index.js | 2 ++ script-base.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/index.js b/app/index.js index c161fd381..50ec1deed 100644 --- a/app/index.js +++ b/app/index.js @@ -28,6 +28,8 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ // dynamic assertion statement this.does = this.is = function(foo) { + foo = this.engine(foo.replace(/\(;>%%<;\)/g, '<%') + .replace(/\(;>%<;\)/g, '%>'), this); if (this.filters.should) { return foo + '.should'; } else { diff --git a/script-base.js b/script-base.js index d88cd988c..f24b61f85 100644 --- a/script-base.js +++ b/script-base.js @@ -25,6 +25,8 @@ var Generator = module.exports = function Generator() { // dynamic assertion statement this.does = this.is = function(foo) { + foo = this.engine(foo.replace(/\(;>%%<;\)/g, '<%') + .replace(/\(;>%<;\)/g, '%>'), this); if (this.filters.should) { return foo + '.should'; } else { From 6fa4b4e56c6f6e56d103fba8dab7ba75cabbe142 Mon Sep 17 00:00:00 2001 From: kingcody Date: Wed, 12 Aug 2015 23:28:56 -0400 Subject: [PATCH 148/171] fix(app): only generate excludes for included deps --- app/templates/Gruntfile.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index 8788038b1..c9d5568aa 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -245,12 +245,12 @@ module.exports = function (grunt) { wiredep: { options: { exclude: [ - /bootstrap-sass-official/, /bootstrap.js/, '/json3/', '/es5-shim/'<% if(!filters.css) { %>, - /bootstrap.css/, - /font-awesome.css/<% } %> + /font-awesome\.css/<% if(filters.bootstrap) { %>, + /bootstrap\.css/<% if(filters.sass) { %>, + /bootstrap-sass-official/<% }}} %> ] }, client: { From 385ca937ab67478ee83835e4e656ddbbc840d986 Mon Sep 17 00:00:00 2001 From: kingcody Date: Thu, 13 Aug 2015 08:00:51 -0400 Subject: [PATCH 149/171] chore(dependencies): update mongoose to `^4.1.2` --- app/templates/_package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/templates/_package.json b/app/templates/_package.json index 7ea2eda4b..ad59553ed 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -16,7 +16,7 @@ "babel-core": "^5.6.4",<% if (filters.jade) { %> "jade": "~1.2.0",<% } %><% if (filters.html) { %> "ejs": "~0.8.4",<% } %><% if (filters.mongoose) { %> - "mongoose": "~4.0.3", + "mongoose": "^4.1.2", "mongoose-bird": "~0.0.1", "connect-mongo": "^0.8.1",<% } %><% if (filters.sequelize) { %> "sequelize": "^3.5.1", From ee9f4f08a4d6557ff9be446a1622de3e01082b8e Mon Sep 17 00:00:00 2001 From: kingcody Date: Thu, 13 Aug 2015 08:15:20 -0400 Subject: [PATCH 150/171] fix(app): karma requires `babel` always --- app/templates/mocha.conf.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/templates/mocha.conf.js b/app/templates/mocha.conf.js index 6b1504e9c..76f56625e 100644 --- a/app/templates/mocha.conf.js +++ b/app/templates/mocha.conf.js @@ -1,7 +1,7 @@ -'use strict';<% if(filters.babel) { %> +'use strict'; // Register the Babel require hook -require('babel-core/register');<% } %> +require('babel-core/register'); var chai = require('chai'); From 4b3b53d48acabb1d71cd677957d4ab9956a375ad Mon Sep 17 00:00:00 2001 From: kingcody Date: Thu, 13 Aug 2015 23:01:55 -0400 Subject: [PATCH 151/171] fix(app): increase mocha default timeout to 5000 This matches the default jasmine spec timeout --- app/templates/Gruntfile.js | 3 ++- app/templates/karma.conf.js | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index c9d5568aa..a287531d3 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -484,7 +484,8 @@ module.exports = function (grunt) { mochaTest: { options: { reporter: 'spec', - require: 'mocha.conf.js' + require: 'mocha.conf.js', + timeout: 5000 // set default mocha spec timeout }, unit: { src: ['server/**/*.spec.js'] diff --git a/app/templates/karma.conf.js b/app/templates/karma.conf.js index 5b45e4f86..9b46a3a22 100644 --- a/app/templates/karma.conf.js +++ b/app/templates/karma.conf.js @@ -8,7 +8,13 @@ module.exports = function(config) { // testing framework to use (jasmine/mocha/qunit/...)<% if (filters.jasmine) { %> frameworks: ['jasmine'],<% } if (filters.mocha) { %> - frameworks: ['mocha', 'chai', 'sinon-chai', 'chai-as-promised', 'chai-things'],<% } %> + frameworks: ['mocha', 'chai', 'sinon-chai', 'chai-as-promised', 'chai-things'], + + client: { + mocha: { + timeout: 5000 // set default mocha spec timeout + } + },<% } %> // list of files / patterns to load in the browser files: [ From 6b49ba37bd08ae8774068779e7c96b1f4e5ce5ac Mon Sep 17 00:00:00 2001 From: kingcody Date: Thu, 13 Aug 2015 08:06:58 -0400 Subject: [PATCH 152/171] feat(app): directly promisify mongoose Changes: * Remove `mongoose-bird` dependency * Add `bluebird` dependency * Use `bluebird.promisifyAll` when requiring `mongoose` for models see https://github.com/petkaantonov/bluebird/blob/master/API.md#promisification --- app/templates/_package.json | 2 +- app/templates/protractor.conf.js | 2 +- .../server/api/user(auth)/user.model(mongooseModels).js | 2 +- app/templates/server/app.js | 2 +- app/templates/server/auth(auth)/auth.service.js | 3 +-- app/templates/server/config/express.js | 2 +- endpoint/templates/basename.model(mongooseModels).js | 2 +- 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/templates/_package.json b/app/templates/_package.json index ad59553ed..7d061fe3b 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -17,7 +17,7 @@ "jade": "~1.2.0",<% } %><% if (filters.html) { %> "ejs": "~0.8.4",<% } %><% if (filters.mongoose) { %> "mongoose": "^4.1.2", - "mongoose-bird": "~0.0.1", + "bluebird": "^2.9.34", "connect-mongo": "^0.8.1",<% } %><% if (filters.sequelize) { %> "sequelize": "^3.5.1", "sqlite3": "~3.0.2",<% } %><% if (filters.auth) { %> diff --git a/app/templates/protractor.conf.js b/app/templates/protractor.conf.js index 7142f6f54..6178c1a76 100644 --- a/app/templates/protractor.conf.js +++ b/app/templates/protractor.conf.js @@ -82,7 +82,7 @@ var config = { var serverConfig = config.params.serverConfig;<% if (filters.mongoose) { %> // Setup mongo for tests - var mongoose = require('mongoose-bird')(); + var mongoose = require('mongoose'); mongoose.connect(serverConfig.mongo.uri, serverConfig.mongo.options); // Connect to database<% } %> } }; diff --git a/app/templates/server/api/user(auth)/user.model(mongooseModels).js b/app/templates/server/api/user(auth)/user.model(mongooseModels).js index 008412eaf..f8fa923cf 100644 --- a/app/templates/server/api/user(auth)/user.model(mongooseModels).js +++ b/app/templates/server/api/user(auth)/user.model(mongooseModels).js @@ -1,6 +1,6 @@ 'use strict'; -var mongoose = require('mongoose-bird')(); +var mongoose = require('bluebird').promisifyAll(require('mongoose')); var Schema = mongoose.Schema; var crypto = require('crypto');<% if (filters.oauth) { %> var authTypes = ['github', 'twitter', 'facebook', 'google'];<% } %> diff --git a/app/templates/server/app.js b/app/templates/server/app.js index 5c98d1612..ca1e4ca78 100644 --- a/app/templates/server/app.js +++ b/app/templates/server/app.js @@ -8,7 +8,7 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development'; var express = require('express');<% if (filters.mongoose) { %> -var mongoose = require('mongoose-bird')();<% } %><% if (filters.sequelize) { %> +var mongoose = require('mongoose');<% } %><% if (filters.sequelize) { %> var sqldb = require('./sqldb');<% } %> var config = require('./config/environment'); <% if (filters.mongoose) { %> diff --git a/app/templates/server/auth(auth)/auth.service.js b/app/templates/server/auth(auth)/auth.service.js index faa2684cf..8c100e84b 100644 --- a/app/templates/server/auth(auth)/auth.service.js +++ b/app/templates/server/auth(auth)/auth.service.js @@ -1,6 +1,5 @@ 'use strict'; -<% if (filters.mongooseModels) { %> -var mongoose = require('mongoose-bird')();<% } %> + var passport = require('passport'); var config = require('../config/environment'); var jwt = require('jsonwebtoken'); diff --git a/app/templates/server/config/express.js b/app/templates/server/config/express.js index b29739e01..3cd2f2739 100644 --- a/app/templates/server/config/express.js +++ b/app/templates/server/config/express.js @@ -17,7 +17,7 @@ var config = require('./environment');<% if (filters.auth) { %> var passport = require('passport');<% } %><% if (filters.twitterAuth) { %> var session = require('express-session');<% if (filters.mongoose) { %> var mongoStore = require('connect-mongo')(session); -var mongoose = require('mongoose-bird')();<% } %><% } %> +var mongoose = require('mongoose');<% } %><% } %> module.exports = function(app) { var env = app.get('env'); diff --git a/endpoint/templates/basename.model(mongooseModels).js b/endpoint/templates/basename.model(mongooseModels).js index ac7f7f72a..09787cdf3 100644 --- a/endpoint/templates/basename.model(mongooseModels).js +++ b/endpoint/templates/basename.model(mongooseModels).js @@ -1,6 +1,6 @@ 'use strict'; -var mongoose = require('mongoose-bird')(); +var mongoose = require('bluebird').promisifyAll(require('mongoose')); var Schema = mongoose.Schema; var <%= classedName %>Schema = new Schema({ From 5927906013ae68e74e1579b2f0523ec08defe402 Mon Sep 17 00:00:00 2001 From: kingcody Date: Fri, 14 Aug 2015 03:03:32 -0400 Subject: [PATCH 153/171] chore(dependencies): update socket.io related dependencies --- app/templates/_package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/templates/_package.json b/app/templates/_package.json index 7ea2eda4b..467e2a8ba 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -28,9 +28,9 @@ "passport-facebook": "latest",<% } %><% if (filters.twitterAuth) { %> "passport-twitter": "latest",<% } %><% if (filters.googleAuth) { %> "passport-google-oauth": "latest",<% } %><% if (filters.socketio) { %> - "socket.io": "^1.0.6", - "socket.io-client": "^1.0.6", - "socketio-jwt": "^3.0.0",<% } %> + "socket.io": "^1.3.5", + "socket.io-client": "^1.3.5", + "socketio-jwt": "^4.2.0",<% } %> "serve-favicon": "~2.0.1" }, "devDependencies": { From f4bf6851551742aa525df17d971694aad5549db6 Mon Sep 17 00:00:00 2001 From: kingcody Date: Fri, 14 Aug 2015 03:07:31 -0400 Subject: [PATCH 154/171] fix(socket.io): update docs and socket.addres ref --- app/templates/server/config/socketio(socketio).js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/templates/server/config/socketio(socketio).js b/app/templates/server/config/socketio(socketio).js index 446b49dff..92f629729 100644 --- a/app/templates/server/config/socketio(socketio).js +++ b/app/templates/server/config/socketio(socketio).js @@ -27,7 +27,7 @@ module.exports = function(socketio) { // // ex: DEBUG: "http*,socket.io:socket" - // We can authenticate socket.io users and access their token through socket.handshake.decoded_token + // We can authenticate socket.io users and access their token through socket.decoded_token // // 1. You will need to send the token in `client/components/socket/socket.service.js` // @@ -38,10 +38,8 @@ module.exports = function(socketio) { // })); socketio.on('connection', function(socket) { - socket.address = - socket.handshake.address !== null ? - socket.handshake.address.address + ':' + socket.handshake.address.port : - process.env.DOMAIN; + socket.address = socket.request.connection.remoteAddress + + ':' + socket.request.connection.remotePort; socket.connectedAt = new Date(); From 6a07a81443c56995558d95b7e7f10013e9253ab9 Mon Sep 17 00:00:00 2001 From: kingcody Date: Fri, 14 Aug 2015 08:55:30 -0400 Subject: [PATCH 155/171] fix(app): only check loggedIn when required --- app/templates/client/app/app(coffee).coffee | 11 ++++++----- app/templates/client/app/app(js).js | 16 +++++++++------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/templates/client/app/app(coffee).coffee b/app/templates/client/app/app(coffee).coffee index bf97363b1..f0c1bd129 100644 --- a/app/templates/client/app/app(coffee).coffee +++ b/app/templates/client/app/app(coffee).coffee @@ -33,9 +33,10 @@ angular.module '<%= scriptAppName %>', [<%= angularModules %>] $q.reject response .run ($rootScope<% if (filters.ngroute) { %>, $location<% } %><% if (filters.uirouter) { %>, $state<% } %>, Auth) -> - # Redirect to login if route requires auth and you're not logged in + # Redirect to login if route requires auth and the user is not logged in $rootScope.$on <% if (filters.ngroute) { %>'$routeChangeStart'<% } %><% if (filters.uirouter) { %>'$stateChangeStart'<% } %>, (event, next) -> - Auth.isLoggedIn (loggedIn) -> - event.preventDefault() - <% if (filters.ngroute) { %>$location.path '/login'<% } %><% if (filters.uirouter) { %>$state.go 'login'<% } %> if next.authenticate and not loggedIn -<% } %> + if next.authenticate + Auth.isLoggedIn (loggedIn) -> + if !loggedIn + event.preventDefault() + <% if (filters.ngroute) { %>$location.path '/login'<% } %><% if (filters.uirouter) { %>$state.go 'login'<% }} %> diff --git a/app/templates/client/app/app(js).js b/app/templates/client/app/app(js).js index 0b636714d..27410af8a 100644 --- a/app/templates/client/app/app(js).js +++ b/app/templates/client/app/app(js).js @@ -45,13 +45,15 @@ angular.module('<%= scriptAppName %>', [<%= angularModules %>]) }) .run(function($rootScope<% if (filters.ngroute) { %>, $location<% } if (filters.uirouter) { %>, $state<% } %>, Auth) { - // Redirect to login if route requires auth and you're not logged in + // Redirect to login if route requires auth and the user is not logged in $rootScope.$on(<% if (filters.ngroute) { %>'$routeChangeStart'<% } %><% if (filters.uirouter) { %>'$stateChangeStart'<% } %>, function(event, next) { - Auth.isLoggedIn(function(loggedIn) { - if (next.authenticate && !loggedIn) { - event.preventDefault(); - <% if (filters.ngroute) { %>$location.path('/login');<% } if (filters.uirouter) { %>$state.go('login');<% } %> - } - }); + if (next.authenticate) { + Auth.isLoggedIn(function(loggedIn) { + if (!loggedIn) { + event.preventDefault(); + <% if (filters.ngroute) { %>$location.path('/login');<% } if (filters.uirouter) { %>$state.go('login');<% } %> + } + }); + } }); })<% } %>; From 49b2535f65080b458cd5d90b57c54114da3bd418 Mon Sep 17 00:00:00 2001 From: kingcody Date: Fri, 14 Aug 2015 09:02:12 -0400 Subject: [PATCH 156/171] refactor(gen): switch fs-extra to node fs --- Gruntfile.js | 10 +++------- package.json | 1 - test/test-file-creation.js | 8 +++++--- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index befea75e5..e5c4e1768 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -6,7 +6,7 @@ var shell = require('shelljs'); var child_process = require('child_process'); var Q = require('q'); var helpers = require('yeoman-generator').test; -var fs = require('fs-extra'); +var fs = require('fs'); var path = require('path'); module.exports = function (grunt) { @@ -218,7 +218,6 @@ module.exports = function (grunt) { }); grunt.registerTask('updateFixtures', 'updates package and bower fixtures', function() { - var done = this.async(); var packageJson = fs.readFileSync(path.resolve('app/templates/_package.json'), 'utf8'); var bowerJson = fs.readFileSync(path.resolve('app/templates/_bower.json'), 'utf8'); @@ -231,11 +230,8 @@ module.exports = function (grunt) { bowerJson = bowerJson.replace(/<%(.*)%>/g, ''); // save files - fs.writeFile(path.resolve(__dirname + '/test/fixtures/package.json'), packageJson, function() { - fs.writeFile(path.resolve(__dirname + '/test/fixtures/bower.json'), bowerJson, function() { - done(); - }); - }); + fs.writeFileSync(path.resolve(__dirname + '/test/fixtures/package.json'), packageJson); + fs.writeFileSync(path.resolve(__dirname + '/test/fixtures/bower.json'), bowerJson); }); grunt.registerTask('installFixtures', 'install package and bower fixtures', function() { diff --git a/package.json b/package.json index 7dfa26f05..94377188d 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ }, "devDependencies": { "chai": "^1.9.1", - "fs-extra": "^0.9.1", "grunt": "~0.4.1", "grunt-build-control": "DaftMonk/grunt-build-control", "grunt-contrib-clean": "^0.6.0", diff --git a/test/test-file-creation.js b/test/test-file-creation.js index fb0fb2b91..db5750900 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -1,7 +1,7 @@ /*global describe, beforeEach, it */ 'use strict'; var path = require('path'); -var fs = require('fs-extra'); +var fs = require('fs'); var exec = require('child_process').exec; var helpers = require('yeoman-generator').test; var chai = require('chai'); @@ -25,6 +25,8 @@ describe('angular-fullstack generator', function () { 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; @@ -442,7 +444,7 @@ describe('angular-fullstack generator', function () { it('should use existing config if available', function(done) { this.timeout(60000); - fs.copySync(__dirname + '/fixtures/.yo-rc.json', __dirname + '/temp/.yo-rc.json'); + copySync(__dirname + '/fixtures/.yo-rc.json', __dirname + '/temp/.yo-rc.json'); var gen = helpers.createGenerator('angular-fullstack:app', [ '../../app', '../../endpoint', @@ -467,7 +469,7 @@ describe('angular-fullstack generator', function () { it('should add oauth option if existing config had oauth strategy selected', function(done) { this.timeout(60000); - fs.copySync(__dirname + '/fixtures/.yo-rc.json', __dirname + '/temp/.yo-rc.json'); + copySync(__dirname + '/fixtures/.yo-rc.json', __dirname + '/temp/.yo-rc.json'); var gen = helpers.createGenerator('angular-fullstack:app', [ '../../app', '../../endpoint', From 973d38c0947e236fc715a01bef90820aac254a13 Mon Sep 17 00:00:00 2001 From: kingcody Date: Fri, 14 Aug 2015 09:19:48 -0400 Subject: [PATCH 157/171] feat(gen): implement jit-grunt --- Gruntfile.js | 6 +++++- package.json | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index e5c4e1768..f67a88c57 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -10,7 +10,11 @@ var fs = require('fs'); var path = require('path'); module.exports = function (grunt) { - require('load-grunt-tasks')(grunt); + // Load grunt tasks automatically, when needed + require('jit-grunt')(grunt, { + buildcontrol: 'grunt-build-control', + changelog: 'grunt-conventional-changelog' + }); grunt.initConfig({ config: { diff --git a/package.json b/package.json index 94377188d..f95dbef65 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "grunt-env": "^0.4.1", "grunt-mocha-test": "^0.11.0", "grunt-release": "~0.6.0", - "load-grunt-tasks": "~0.2.0", + "jit-grunt": "^0.9.1", "marked": "~0.2.8", "mocha": "~1.21.0", "q": "^1.0.1", From 2916a1dfe54d0a44e1d730ca780d6a38c0b12e67 Mon Sep 17 00:00:00 2001 From: kingcody Date: Fri, 14 Aug 2015 10:37:51 -0400 Subject: [PATCH 158/171] chore(gen): update generator task dependencies --- Gruntfile.js | 3 +-- package.json | 19 +++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index f67a88c57..38fc08efc 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,7 +1,6 @@ 'use strict'; -var markdown = require('marked'); + var semver = require('semver'); -var _s = require('underscore.string'); var shell = require('shelljs'); var child_process = require('child_process'); var Q = require('q'); diff --git a/package.json b/package.json index f95dbef65..219b9349b 100644 --- a/package.json +++ b/package.json @@ -26,29 +26,28 @@ "test": "grunt test" }, "dependencies": { - "chalk": "~0.4.0", + "chalk": "^1.1.0", "generator-ng-component": "~0.1.0", "yeoman-generator": "~0.18.10" }, "devDependencies": { - "chai": "^1.9.1", + "chai": "^3.2.0", "grunt": "~0.4.1", - "grunt-build-control": "DaftMonk/grunt-build-control", + "grunt-build-control": "^0.5.0", "grunt-contrib-clean": "^0.6.0", "grunt-contrib-jshint": "^0.11.2", "grunt-conventional-changelog": "~1.0.0", "grunt-david": "~0.5.0", "grunt-env": "^0.4.1", - "grunt-mocha-test": "^0.11.0", - "grunt-release": "~0.6.0", + "grunt-mocha-test": "^0.12.7", + "grunt-release": "^0.13.0", "jit-grunt": "^0.9.1", - "marked": "~0.2.8", - "mocha": "~1.21.0", + "mocha": "^2.2.5", "q": "^1.0.1", "recursive-readdir": "^1.2.0", - "semver": "~2.2.1", - "shelljs": "^0.3.0", - "underscore.string": "^2.3.3" + "semver": "^5.0.1", + "shelljs": "^0.5.3", + "underscore.string": "^3.1.1" }, "engines": { "node": ">=0.10.0", From c708e0c178dce7c0ca07e43fda825d46d25f8b03 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Fri, 14 Aug 2015 16:53:54 -0400 Subject: [PATCH 159/171] 3.0.0-rc1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 219b9349b..ff7c96f5c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "generator-angular-fullstack", - "version": "2.1.1", + "version": "3.0.0-rc1", "description": "Yeoman generator for creating MEAN stack applications, using MongoDB, Express, AngularJS, and Node", "keywords": [ "yeoman-generator", From e1c9ca3e581b8527583e8e9ad255a4c064d3dad9 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Fri, 14 Aug 2015 17:03:49 -0400 Subject: [PATCH 160/171] 3.0.0-rc2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ff7c96f5c..63d5b7d85 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "generator-angular-fullstack", - "version": "3.0.0-rc1", + "version": "3.0.0-rc2", "description": "Yeoman generator for creating MEAN stack applications, using MongoDB, Express, AngularJS, and Node", "keywords": [ "yeoman-generator", From aa6047e23f9252d5e69cf69be99aa505dd027ca6 Mon Sep 17 00:00:00 2001 From: kingcody Date: Thu, 13 Aug 2015 08:26:53 -0400 Subject: [PATCH 161/171] chore(dependencies): update grunt dependencies Changes: * `grunt-contrib-watch` now uses `spawn: false` instead of `nospawn: true` @see https://github.com/gruntjs/grunt-contrib-watch#optionsspawn * `grunt-contrib-imagemin` now minifies svg files, remove `grunt-svgmin` @see https://github.com/gruntjs/grunt-contrib-imagemin#imagemin-task --- app/templates/Gruntfile.js | 18 +++---------- app/templates/_package.json | 51 ++++++++++++++++++------------------- 2 files changed, 28 insertions(+), 41 deletions(-) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index c9d5568aa..070aff36d 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -128,7 +128,7 @@ module.exports = function (grunt) { tasks: ['express:dev', 'wait'], options: { livereload: true, - nospawn: true //Without this option specified express won't be reloaded + spawn: false //Without this option specified express won't be reloaded } }, bower: { @@ -309,18 +309,7 @@ module.exports = function (grunt) { files: [{ expand: true, cwd: '<%%= yeoman.client %>/assets/images', - src: '{,*/}*.{png,jpg,jpeg,gif}', - dest: '<%%= yeoman.dist %>/client/assets/images' - }] - } - }, - - svgmin: { - dist: { - files: [{ - expand: true, - cwd: '<%%= yeoman.client %>/assets/images', - src: '{,*/}*.svg', + src: '{,*/}*.{png,jpg,jpeg,gif,svg}', dest: '<%%= yeoman.dist %>/client/assets/images' }] } @@ -468,8 +457,7 @@ module.exports = function (grunt) { 'stylus',<% } if(filters.sass) { %> 'sass',<% } if(filters.less) { %> 'less',<% } %> - 'imagemin', - 'svgmin' + 'imagemin' ] }, diff --git a/app/templates/_package.json b/app/templates/_package.json index 7d061fe3b..f37d674b1 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -35,44 +35,42 @@ }, "devDependencies": { "autoprefixer-core": "^5.2.1", - "grunt": "~0.4.4", + "grunt": "~0.4.5", "grunt-wiredep": "^2.0.0", - "grunt-concurrent": "~0.5.0", - "grunt-contrib-clean": "~0.5.0", - "grunt-contrib-concat": "~0.4.0", - "grunt-contrib-copy": "~0.5.0", - "grunt-contrib-cssmin": "~0.9.0", - "grunt-contrib-htmlmin": "~0.2.0", - "grunt-contrib-imagemin": "~0.7.1", + "grunt-concurrent": "^2.0.1", + "grunt-contrib-clean": "^0.6.0", + "grunt-contrib-concat": "^0.5.1", + "grunt-contrib-copy": "^0.8.0", + "grunt-contrib-cssmin": "^0.13.0", + "grunt-contrib-imagemin": "^0.9.4", "grunt-contrib-jshint": "~0.11.2", - "grunt-contrib-uglify": "~0.4.0", + "grunt-contrib-uglify": "^0.9.1", "grunt-contrib-watch": "~0.6.1",<% if (filters.coffee) { %> - "grunt-contrib-coffee": "^0.10.1",<% } %><% if (filters.jade) { %> - "grunt-contrib-jade": "^0.11.0",<% } %><% if (filters.less) { %> - "grunt-contrib-less": "^0.11.4",<% } %><% if(filters.babel) { %> + "grunt-contrib-coffee": "^0.13.0",<% } %><% if (filters.jade) { %> + "grunt-contrib-jade": "^0.15.0",<% } %><% if (filters.less) { %> + "grunt-contrib-less": "^1.0.0",<% } %><% if(filters.babel) { %> "karma-babel-preprocessor": "^5.2.1", "grunt-babel": "~5.0.0",<% } %> "grunt-google-cdn": "~0.4.0", - "grunt-jscs": "~0.7.1", - "grunt-newer": "~0.7.0", - "grunt-ng-annotate": "^0.2.3", + "grunt-jscs": "^2.0.0", + "grunt-newer": "^1.1.1", + "grunt-ng-annotate": "^1.0.1", "grunt-filerev": "^2.3.1", - "grunt-svgmin": "~0.4.0", - "grunt-usemin": "~2.1.1", + "grunt-usemin": "^3.0.0", "grunt-env": "~0.4.1", - "grunt-node-inspector": "~0.1.5", - "grunt-nodemon": "~0.2.0", + "grunt-node-inspector": "^0.2.0", + "grunt-nodemon": "^0.4.0", "grunt-angular-templates": "^0.5.4", "grunt-dom-munger": "^3.4.0", "grunt-protractor-runner": "^2.0.0", - "grunt-injector": "~0.5.4", + "grunt-injector": "^0.6.0", "grunt-karma": "~0.12.0", - "grunt-build-control": "~0.4.0",<% if(filters.sass) { %> - "grunt-contrib-sass": "^0.7.3",<% } %><% if(filters.stylus) { %> + "grunt-build-control": "^0.5.0",<% if(filters.sass) { %> + "grunt-contrib-sass": "^0.9.0",<% } %><% if(filters.stylus) { %> "grunt-contrib-stylus": "latest",<% } %> - "jit-grunt": "^0.5.0", - "time-grunt": "~0.3.1", - "grunt-express-server": "~0.4.17", + "jit-grunt": "^0.9.1", + "time-grunt": "^1.2.1", + "grunt-express-server": "^0.5.1", "grunt-postcss": "^0.5.5", "grunt-open": "~0.2.3", "open": "~0.0.4", @@ -80,7 +78,8 @@ "connect-livereload": "^0.5.3", "mocha": "^2.2.5", "grunt-mocha-test": "~0.12.7", - "grunt-mocha-istanbul": "^2.4.0", + "grunt-mocha-istanbul": "^3.0.1", + "istanbul": "^0.3.17", "chai-as-promised": "^5.1.0", "chai-things": "^0.2.0", "sinon-chai": "^2.8.0",<% if (filters.mocha) { %> From 0017b6fee26d21d29fd5f082bea8418f336a5643 Mon Sep 17 00:00:00 2001 From: kingcody Date: Fri, 14 Aug 2015 21:49:09 -0400 Subject: [PATCH 162/171] fix(app): babel watches all client (non-spec) js for changes --- app/templates/Gruntfile.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/templates/Gruntfile.js b/app/templates/Gruntfile.js index 070aff36d..b67e1825a 100644 --- a/app/templates/Gruntfile.js +++ b/app/templates/Gruntfile.js @@ -54,13 +54,17 @@ module.exports = function (grunt) { url: 'http://localhost:<%%= express.options.port %>' } }, - watch: { + watch: {<% if(filters.babel) { %> + babel: { + files: ['<%%= yeoman.client %>/{app,components}/**/!(*.spec|*.mock).js'], + tasks: ['newer:babel:client'] + },<% } %> injectJS: { files: [ '<%%= yeoman.client %>/{app,components}/**/!(*.spec|*.mock).js', '!<%%= yeoman.client %>/app/app.js' ], - tasks: [<% if(filters.babel) { %>'newer:babel:client', <% } %>'injector:scripts'] + tasks: ['injector:scripts'] }, injectCss: { files: ['<%%= yeoman.client %>/{app,components}/**/*.css'], From a22847de6a48e8c29628cd304726de375dfe1e60 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Sat, 15 Aug 2015 15:39:17 -0400 Subject: [PATCH 163/171] 3.0.0-rc3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 63d5b7d85..54ae8127f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "generator-angular-fullstack", - "version": "3.0.0-rc2", + "version": "3.0.0-rc3", "description": "Yeoman generator for creating MEAN stack applications, using MongoDB, Express, AngularJS, and Node", "keywords": [ "yeoman-generator", From a7cdf29b4f87b7a009a9bbc5a738ed604ff5c4be Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Fri, 14 Aug 2015 15:57:16 -0400 Subject: [PATCH 164/171] feat(express): add express-sequelize-session --- app/templates/_package.json | 3 ++- app/templates/server/config/express.js | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/templates/_package.json b/app/templates/_package.json index 35c2c6c74..725b466ba 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -20,7 +20,8 @@ "bluebird": "^2.9.34", "connect-mongo": "^0.8.1",<% } %><% if (filters.sequelize) { %> "sequelize": "^3.5.1", - "sqlite3": "~3.0.2",<% } %><% if (filters.auth) { %> + "sqlite3": "~3.0.2", + "express-sequelize-session": "0.4.0",<% } %><% if (filters.auth) { %> "jsonwebtoken": "^5.0.0", "express-jwt": "^3.0.0", "passport": "~0.2.0", diff --git a/app/templates/server/config/express.js b/app/templates/server/config/express.js index 3cd2f2739..39a62312c 100644 --- a/app/templates/server/config/express.js +++ b/app/templates/server/config/express.js @@ -17,7 +17,9 @@ var config = require('./environment');<% if (filters.auth) { %> var passport = require('passport');<% } %><% if (filters.twitterAuth) { %> var session = require('express-session');<% if (filters.mongoose) { %> var mongoStore = require('connect-mongo')(session); -var mongoose = require('mongoose');<% } %><% } %> +var mongoose = require('mongoose');<% } else if(filters.sequelize) { %> +var sqldb = require('../sqldb'); +var Store = require('express-sequelize-session')(session.Store);<% } %><% } %> module.exports = function(app) { var env = app.get('env'); @@ -42,7 +44,8 @@ module.exports = function(app) { store: new mongoStore({ mongooseConnection: mongoose.connection, db: '<%= _.slugify(_.humanize(appname)) %>' - })<% } %> + })<% } else if(filters.sequelize) { %>, + store: new Store(sqldb.sequelize)<% } %> })); <% } %> app.set('appPath', path.join(config.root, 'client')); From cc9cbcc4b7df758c9e8e98851a3e7006f3a5faa6 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Sun, 16 Aug 2015 02:28:22 -0400 Subject: [PATCH 165/171] chore(gen): update generated .travis.yml and package.json node target to 0.12 --- app/templates/.travis.yml | 3 +-- app/templates/_package.json | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/templates/.travis.yml b/app/templates/.travis.yml index 5e8066dc8..c12f57edb 100644 --- a/app/templates/.travis.yml +++ b/app/templates/.travis.yml @@ -1,7 +1,6 @@ language: node_js node_js: - - '0.10' - - '0.11' + - '0.12' before_script: - npm install -g bower grunt-cli<% if (filters.sass) { %> - gem install sass<% } %> diff --git a/app/templates/_package.json b/app/templates/_package.json index 725b466ba..53b4e9824 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -107,7 +107,7 @@ "supertest": "~0.11.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.12.0" }, "scripts": { "start": "node server", From ca7fb10829e2f56ae3d43fb155645f1c33c8a969 Mon Sep 17 00:00:00 2001 From: kingcody Date: Sun, 16 Aug 2015 02:32:21 -0400 Subject: [PATCH 166/171] chore(gen): bump node requirements to `>=0.12.0` --- app/templates/README.md | 2 +- app/templates/_package.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/templates/README.md b/app/templates/README.md index e49273a90..958834120 100644 --- a/app/templates/README.md +++ b/app/templates/README.md @@ -7,7 +7,7 @@ This project was generated with the [Angular Full-Stack Generator](https://githu ### Prerequisites - [Git](https://git-scm.com/) -- [Node.js and NPM](nodejs.org) >= v0.10.0 +- [Node.js and NPM](nodejs.org) >= v0.12.0 - [Bower](bower.io) (`npm install --global bower`)<% if(filters.sass) { %> - [Ruby](https://www.ruby-lang.org) and then `gem install sass`<% } if(filters.grunt) { %> - [Grunt](http://gruntjs.com/) (`npm install --global grunt-cli`)<% } if(filters.gulp) { %> diff --git a/app/templates/_package.json b/app/templates/_package.json index 725b466ba..53b4e9824 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -107,7 +107,7 @@ "supertest": "~0.11.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.12.0" }, "scripts": { "start": "node server", diff --git a/package.json b/package.json index 54ae8127f..353fcbfdd 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "underscore.string": "^3.1.1" }, "engines": { - "node": ">=0.10.0", + "node": ">=0.12.0", "npm": ">=1.2.10" }, "license": "BSD-2-Clause" From b7c91acebf48684d33235f5e3f83963b442ed236 Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Mon, 17 Aug 2015 10:38:48 -0400 Subject: [PATCH 167/171] 3.0.0-rc4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 353fcbfdd..ccfcd3ff6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "generator-angular-fullstack", - "version": "3.0.0-rc3", + "version": "3.0.0-rc4", "description": "Yeoman generator for creating MEAN stack applications, using MongoDB, Express, AngularJS, and Node", "keywords": [ "yeoman-generator", From ad267383cfe3653846bc9fa8dcb5f7fe318c839b Mon Sep 17 00:00:00 2001 From: kingcody Date: Wed, 19 Aug 2015 08:14:31 -0400 Subject: [PATCH 168/171] chore(gen): update fixtures config --- test/fixtures/.yo-rc.json | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/test/fixtures/.yo-rc.json b/test/fixtures/.yo-rc.json index a26821115..01d568984 100644 --- a/test/fixtures/.yo-rc.json +++ b/test/fixtures/.yo-rc.json @@ -1,20 +1,36 @@ { "generator-angular-fullstack": { - "insertRoutes": "true", + "endpointDirectory": "server/api/", + "insertRoutes": true, "registerRoutesFile": "server/routes.js", "routesNeedle": "// Insert routes below", - "insertSockets": "true", + "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": { "coffee": true, "html": true, "less": true, "uirouter": true, + "bootstrap": false, + "uibootstrap": false, "socketio": true, - "mongoose": true, "auth": true, - "googleAuth": true + "models": true, + "mongooseModels": true, + "mongoose": true, + "oauth": true, + "googleAuth": true, + "grunt": true, + "mocha": true, + "jasmine": false, + "should": true, + "expect": false } } } From baeff6ca497dbbcaea61ad289fa08e99b3c48900 Mon Sep 17 00:00:00 2001 From: kingcody Date: Wed, 19 Aug 2015 08:20:52 -0400 Subject: [PATCH 169/171] chore(gen): remove migration code and test --- app/index.js | 9 --------- test/test-file-creation.js | 22 ---------------------- 2 files changed, 31 deletions(-) diff --git a/app/index.js b/app/index.js index 50ec1deed..ec954aa46 100644 --- a/app/index.js +++ b/app/index.js @@ -61,15 +61,6 @@ var AngularFullstackGenerator = yeoman.generators.Base.extend({ jasmine: true }); - // NOTE: temp(?) fix for #403 - if(typeof this.filters.oauth === 'undefined') { - var strategies = Object.keys(this.filters).filter(function(key) { - return key.match(/Auth$/) && this.filters[key]; - }.bind(this)); - - if(strategies.length) this.filters.oauth = true; - } - this.config.set('filters', this.filters); this.config.forceSave(); diff --git a/test/test-file-creation.js b/test/test-file-creation.js index db5750900..7850fc9e4 100644 --- a/test/test-file-creation.js +++ b/test/test-file-creation.js @@ -467,28 +467,6 @@ describe('angular-fullstack generator', function () { }); }); - it('should add oauth option if existing config had oauth strategy selected', function(done) { - this.timeout(60000); - copySync(__dirname + '/fixtures/.yo-rc.json', __dirname + '/temp/.yo-rc.json'); - var gen = helpers.createGenerator('angular-fullstack:app', [ - '../../app', - '../../endpoint', - [ - helpers.createDummyGenerator(), - 'ng-component:app' - ] - ]); - gen.options['skip-install'] = true; - helpers.mockPrompt(gen, { - skipConfig: true - }); - gen.run(function () { - var yoConfig = require(__dirname + '/temp/.yo-rc.json'); - expect(yoConfig['generator-angular-fullstack'].filters.oauth).to.be.true; - done(); - }); - }); - it('should generate expected files', function (done) { gen.run(function () { helpers.assertFile(genFiles(defaultOptions)); From dbc43e9bf1357650f965c90d181ac8470b90dc2f Mon Sep 17 00:00:00 2001 From: kingcody Date: Wed, 19 Aug 2015 08:25:54 -0400 Subject: [PATCH 170/171] fix(test): require `server` instead of `server/app.js` --- app/templates/server/api/user(auth)/user.integration.js | 2 +- .../server/api/user(auth)/user.model.spec(mongooseModels).js | 2 +- .../server/api/user(auth)/user.model.spec(sequelizeModels).js | 2 +- endpoint/templates/basename.integration.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/templates/server/api/user(auth)/user.integration.js b/app/templates/server/api/user(auth)/user.integration.js index 5273be72a..19978ce48 100644 --- a/app/templates/server/api/user(auth)/user.integration.js +++ b/app/templates/server/api/user(auth)/user.integration.js @@ -1,6 +1,6 @@ 'use strict'; -var app = require('../../app');<% if (filters.mongooseModels) { %> +var app = require('../..');<% if (filters.mongooseModels) { %> var User = require('./user.model');<% } %><% if (filters.sequelizeModels) { %> var User = require('../../sqldb').User;<% } %> var request = require('supertest'); diff --git a/app/templates/server/api/user(auth)/user.model.spec(mongooseModels).js b/app/templates/server/api/user(auth)/user.model.spec(mongooseModels).js index 1aad3b25e..099e4d5c6 100644 --- a/app/templates/server/api/user(auth)/user.model.spec(mongooseModels).js +++ b/app/templates/server/api/user(auth)/user.model.spec(mongooseModels).js @@ -1,6 +1,6 @@ 'use strict'; -var app = require('../../app'); +var app = require('../..'); var User = require('./user.model'); var user; var genUser = function() { diff --git a/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js b/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js index a7af1bd38..7e0ca0cc4 100644 --- a/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js +++ b/app/templates/server/api/user(auth)/user.model.spec(sequelizeModels).js @@ -1,6 +1,6 @@ 'use strict'; -var app = require('../../app'); +var app = require('../..'); var User = require('../../sqldb').User; var user; var genUser = function() { diff --git a/endpoint/templates/basename.integration.js b/endpoint/templates/basename.integration.js index bcd3fd407..067898d72 100644 --- a/endpoint/templates/basename.integration.js +++ b/endpoint/templates/basename.integration.js @@ -1,6 +1,6 @@ 'use strict'; -var app = require('<%= relativeRequire('server/app') %>'); +var app = require('<%= relativeRequire('server') %>'); var request = require('supertest');<% if(filters.models) { %> var new<%= classedName %>;<% } %> From 1855265dc3d5b4956b7069d49b2b5e8c1a61e42f Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Wed, 19 Aug 2015 16:48:37 -0400 Subject: [PATCH 171/171] fix(tests): only use livereload for development --- app/templates/server/config/express.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/templates/server/config/express.js b/app/templates/server/config/express.js index 39a62312c..aa32be65a 100644 --- a/app/templates/server/config/express.js +++ b/app/templates/server/config/express.js @@ -56,8 +56,11 @@ module.exports = function(app) { app.use(morgan('dev')); } - if ('development' === env || 'test' === env) { + if ('development' === env) { app.use(require('connect-livereload')()); + } + + if ('development' === env || 'test' === env) { app.use(express.static(path.join(config.root, '.tmp'))); app.use(express.static(app.get('appPath'))); app.use(morgan('dev'));