From dc880eb5a77168099b214f1733ed0d6d23ff7093 Mon Sep 17 00:00:00 2001 From: Michael Leanos Date: Thu, 27 Jul 2017 13:19:22 -0700 Subject: [PATCH 01/14] feat(config): Mongoose 4.11 upgrade (#1818) Upgrades Mongoose to 4.11.1 Updates Mongoose connection implementation to accommodate deprecated features & connection options. Also, updates the Gulp Mocha tasks to reflect changes to the Mongoose implementation. Fixes tests to get the database from the existing Mongoose singleton. Derives from changes in https://github.com/meanjs/mean/pull/1816 Closes https://github.com/meanjs/mean/issues/1814 --- config/env/development.js | 5 +-- config/env/local.example.js | 5 +-- config/env/production.js | 2 -- config/env/test.js | 5 +-- config/lib/app.js | 10 +++--- config/lib/express.js | 6 ++-- config/lib/mongoose.js | 36 ++++++++++--------- config/lib/socket.io.js | 2 +- gulpfile.js | 31 +++++++++------- .../server/config/articles.server.config.js | 2 +- .../admin.article.server.routes.tests.js | 2 +- .../server/article.server.routes.tests.js | 14 ++++---- .../tests/server/core.server.config.tests.js | 2 +- .../server/config/users.server.config.js | 2 +- .../tests/server/user.server.routes.tests.js | 2 +- package.json | 2 +- 16 files changed, 62 insertions(+), 66 deletions(-) diff --git a/config/env/development.js b/config/env/development.js index da024eb8..42cbecf3 100644 --- a/config/env/development.js +++ b/config/env/development.js @@ -5,10 +5,7 @@ var defaultEnvConfig = require('./default'); module.exports = { db: { uri: process.env.MONGOHQ_URL || process.env.MONGODB_URI || 'mongodb://' + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/mean-dev', - options: { - user: '', - pass: '' - }, + options: {}, // Enable mongoose debug mode debug: process.env.MONGODB_DEBUG || false }, diff --git a/config/env/local.example.js b/config/env/local.example.js index 7a8641ff..2f4aeabf 100644 --- a/config/env/local.example.js +++ b/config/env/local.example.js @@ -20,10 +20,7 @@ module.exports = { db: { uri: 'mongodb://localhost/local-dev', - options: { - user: '', - pass: '' - } + options: {} }, sessionSecret: process.env.SESSION_SECRET || 'youshouldchangethistosomethingsecret', facebook: { diff --git a/config/env/production.js b/config/env/production.js index c1124b08..c43281fa 100644 --- a/config/env/production.js +++ b/config/env/production.js @@ -15,8 +15,6 @@ module.exports = { db: { uri: process.env.MONGOHQ_URL || process.env.MONGODB_URI || 'mongodb://' + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/mean', options: { - user: '', - pass: '' /** * Uncomment to enable ssl certificate based authentication to mongodb * servers. Adjust the settings below for your specific certificate diff --git a/config/env/test.js b/config/env/test.js index 845be38d..0cbfb6bc 100644 --- a/config/env/test.js +++ b/config/env/test.js @@ -5,10 +5,7 @@ var defaultEnvConfig = require('./default'); module.exports = { db: { uri: process.env.MONGOHQ_URL || process.env.MONGODB_URI || 'mongodb://' + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/mean-test', - options: { - user: '', - pass: '' - }, + options: {}, // Enable mongoose debug mode debug: process.env.MONGODB_DEBUG || false }, diff --git a/config/lib/app.js b/config/lib/app.js index 5c4d1309..3321e78e 100644 --- a/config/lib/app.js +++ b/config/lib/app.js @@ -4,7 +4,7 @@ * Module dependencies. */ var config = require('../config'), - mongoose = require('./mongoose'), + mongooseService = require('./mongoose'), express = require('./express'), chalk = require('chalk'), seed = require('./seed'); @@ -16,11 +16,11 @@ function seedDB() { } } -// Initialize Models -mongoose.loadModels(seedDB); - module.exports.init = function init(callback) { - mongoose.connect(function (db) { + mongooseService.connect(function (db) { + // Initialize Models + mongooseService.loadModels(seedDB); + // Initialize express var app = express.init(db); if (callback) callback(app, db, config); diff --git a/config/lib/express.js b/config/lib/express.js index 9a7d9996..3df5eaf0 100644 --- a/config/lib/express.js +++ b/config/lib/express.js @@ -118,7 +118,7 @@ module.exports.initSession = function (app, db) { }, name: config.sessionKey, store: new MongoStore({ - mongooseConnection: db.connection, + db: db, collection: config.sessionCollection }) })); @@ -130,9 +130,9 @@ module.exports.initSession = function (app, db) { /** * Invoke modules server configuration */ -module.exports.initModulesConfiguration = function (app, db) { +module.exports.initModulesConfiguration = function (app) { config.files.server.configs.forEach(function (configPath) { - require(path.resolve(configPath))(app, db); + require(path.resolve(configPath))(app); }); }; diff --git a/config/lib/mongoose.js b/config/lib/mongoose.js index 17fae2c2..86554bda 100644 --- a/config/lib/mongoose.js +++ b/config/lib/mongoose.js @@ -3,7 +3,8 @@ /** * Module dependencies. */ -var config = require('../config'), +var _ = require('lodash'), + config = require('../config'), chalk = require('chalk'), path = require('path'), mongoose = require('mongoose'); @@ -19,30 +20,31 @@ module.exports.loadModels = function (callback) { }; // Initialize Mongoose -module.exports.connect = function (cb) { - var _this = this; - +module.exports.connect = function (callback) { mongoose.Promise = config.db.promise; - var db = mongoose.connect(config.db.uri, config.db.options, function (err) { - // Log Error - if (err) { - console.error(chalk.red('Could not connect to MongoDB!')); - console.log(err); - } else { + var options = _.merge(config.db.options || {}, { useMongoClient: true }); + mongoose + .connect(config.db.uri, options) + .then(function (connection) { // Enabling mongoose debug mode if required mongoose.set('debug', config.db.debug); // Call callback FN - if (cb) cb(db); - } - }); + if (callback) callback(connection.db); + }) + .catch(function (err) { + console.error(chalk.red('Could not connect to MongoDB!')); + console.log(err); + }); + }; module.exports.disconnect = function (cb) { - mongoose.disconnect(function (err) { - console.info(chalk.yellow('Disconnected from MongoDB.')); - cb(err); - }); + mongoose.connection.db + .close(function (err) { + console.info(chalk.yellow('Disconnected from MongoDB.')); + return cb(err); + }); }; diff --git a/config/lib/socket.io.js b/config/lib/socket.io.js index 0050f4fb..03da0dd8 100644 --- a/config/lib/socket.io.js +++ b/config/lib/socket.io.js @@ -71,7 +71,7 @@ module.exports = function (app, db) { // Create a MongoDB storage object var mongoStore = new MongoStore({ - mongooseConnection: db.connection, + db: db, collection: config.sessionCollection }); diff --git a/gulpfile.js b/gulpfile.js index a578b682..870d5015 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -277,15 +277,15 @@ gulp.task('templatecache', function () { // Mocha tests task gulp.task('mocha', function (done) { - // Open mongoose connections - var mongoose = require('./config/lib/mongoose.js'); + var mongooseService = require('./config/lib/mongoose'); var testSuites = changedTestFiles.length ? changedTestFiles : testAssets.tests.server; var error; // Connect mongoose - mongoose.connect(function () { - mongoose.loadModels(); - // Run the tests + mongooseService.connect(function (db) { + // Load mongoose models + mongooseService.loadModels(); + gulp.src(testSuites) .pipe(plugins.mocha({ reporter: 'spec', @@ -296,9 +296,13 @@ gulp.task('mocha', function (done) { error = err; }) .on('end', function () { - // When the tests are done, disconnect mongoose and pass the error state back to gulp - mongoose.disconnect(function () { - done(error); + mongooseService.disconnect(function (err) { + if (err) { + console.log('Error disconnecting from database'); + console.log(err); + } + + return done(error); }); }); }); @@ -362,16 +366,17 @@ gulp.task('karma:coverage', function(done) { // Drops the MongoDB database, used in e2e testing gulp.task('dropdb', function (done) { // Use mongoose configuration - var mongoose = require('./config/lib/mongoose.js'); + var mongooseService = require('./config/lib/mongoose'); - mongoose.connect(function (db) { - db.connection.db.dropDatabase(function (err) { + mongooseService.connect(function (db) { + db.dropDatabase(function (err) { if (err) { console.error(err); } else { - console.log('Successfully dropped db: ', db.connection.db.databaseName); + console.log('Successfully dropped db: ', db.databaseName); } - db.connection.db.close(done); + + mongooseService.disconnect(done); }); }); }); diff --git a/modules/articles/server/config/articles.server.config.js b/modules/articles/server/config/articles.server.config.js index 629c3606..1e5355ca 100644 --- a/modules/articles/server/config/articles.server.config.js +++ b/modules/articles/server/config/articles.server.config.js @@ -9,6 +9,6 @@ var path = require('path'), /** * Module init function. */ -module.exports = function (app, db) { +module.exports = function (app) { }; diff --git a/modules/articles/tests/server/admin.article.server.routes.tests.js b/modules/articles/tests/server/admin.article.server.routes.tests.js index ea9b9e14..ba080eb2 100644 --- a/modules/articles/tests/server/admin.article.server.routes.tests.js +++ b/modules/articles/tests/server/admin.article.server.routes.tests.js @@ -23,7 +23,7 @@ var app, describe('Article Admin CRUD tests', function () { before(function (done) { // Get application - app = express.init(mongoose); + app = express.init(mongoose.connection.db); agent = request.agent(app); done(); diff --git a/modules/articles/tests/server/article.server.routes.tests.js b/modules/articles/tests/server/article.server.routes.tests.js index 0c23a521..6bc05235 100644 --- a/modules/articles/tests/server/article.server.routes.tests.js +++ b/modules/articles/tests/server/article.server.routes.tests.js @@ -24,7 +24,7 @@ describe('Article CRUD tests', function () { before(function (done) { // Get application - app = express.init(mongoose); + app = express.init(mongoose.connection.db); agent = request.agent(app); done(); @@ -119,7 +119,7 @@ describe('Article CRUD tests', function () { // Save the article articleObj.save(function () { // Request articles - request(app).get('/api/articles') + agent.get('/api/articles') .end(function (req, res) { // Set assertion res.body.should.be.instanceof(Array).and.have.lengthOf(1); @@ -137,7 +137,7 @@ describe('Article CRUD tests', function () { // Save the article articleObj.save(function () { - request(app).get('/api/articles/' + articleObj._id) + agent.get('/api/articles/' + articleObj._id) .end(function (req, res) { // Set assertion res.body.should.be.instanceof(Object).and.have.property('title', article.title); @@ -150,7 +150,7 @@ describe('Article CRUD tests', function () { it('should return proper error for single article with an invalid Id, if not signed in', function (done) { // test is not a valid mongoose Id - request(app).get('/api/articles/test') + agent.get('/api/articles/test') .end(function (req, res) { // Set assertion res.body.should.be.instanceof(Object).and.have.property('message', 'Article is invalid'); @@ -162,7 +162,7 @@ describe('Article CRUD tests', function () { it('should return proper error for single article which doesnt exist, if not signed in', function (done) { // This is a valid mongoose Id but a non-existent article - request(app).get('/api/articles/559e9cd815f80b4c256a8f41') + agent.get('/api/articles/559e9cd815f80b4c256a8f41') .end(function (req, res) { // Set assertion res.body.should.be.instanceof(Object).and.have.property('message', 'No article with that identifier has been found'); @@ -202,7 +202,7 @@ describe('Article CRUD tests', function () { // Save the article articleObj.save(function () { // Try deleting article - request(app).delete('/api/articles/' + articleObj._id) + agent.delete('/api/articles/' + articleObj._id) .expect(403) .end(function (articleDeleteErr, articleDeleteRes) { // Set message assertion @@ -312,7 +312,7 @@ describe('Article CRUD tests', function () { if (err) { return done(err); } - request(app).get('/api/articles/' + articleObj._id) + agent.get('/api/articles/' + articleObj._id) .end(function (req, res) { // Set assertion res.body.should.be.instanceof(Object).and.have.property('title', article.title); diff --git a/modules/core/tests/server/core.server.config.tests.js b/modules/core/tests/server/core.server.config.tests.js index ab8e2368..c4f3159e 100644 --- a/modules/core/tests/server/core.server.config.tests.js +++ b/modules/core/tests/server/core.server.config.tests.js @@ -517,7 +517,7 @@ describe('Configuration Tests:', function () { process.env.NODE_ENV = env; // Gget application - app = express.init(mongoose); + app = express.init(mongoose.connection.db); agent = request.agent(app); // Get rendered layout diff --git a/modules/users/server/config/users.server.config.js b/modules/users/server/config/users.server.config.js index 2bfedceb..f67857b3 100644 --- a/modules/users/server/config/users.server.config.js +++ b/modules/users/server/config/users.server.config.js @@ -11,7 +11,7 @@ var passport = require('passport'), /** * Module init function */ -module.exports = function (app, db) { +module.exports = function (app) { // Serialize sessions passport.serializeUser(function (user, done) { done(null, user.id); diff --git a/modules/users/tests/server/user.server.routes.tests.js b/modules/users/tests/server/user.server.routes.tests.js index 6a51d633..2dd8441d 100644 --- a/modules/users/tests/server/user.server.routes.tests.js +++ b/modules/users/tests/server/user.server.routes.tests.js @@ -26,7 +26,7 @@ describe('User CRUD tests', function () { before(function (done) { // Get application - app = express.init(mongoose); + app = express.init(mongoose.connection.db); agent = request.agent(app); done(); diff --git a/package.json b/package.json index e4ec91b9..c2d14cf5 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "lusca": "~1.4.1", "method-override": "~2.3.8", "mocha": "~3.4.2", - "mongoose": "~4.10.2", + "mongoose": "~4.11.3", "morgan": "~1.8.1", "multer": "~1.3.0", "nodemailer": "~4.0.1", From 6021c145f930a3001fe7f874f0e66c242573fc96 Mon Sep 17 00:00:00 2001 From: Dale Lotts Date: Sat, 29 Jul 2017 17:35:43 -0500 Subject: [PATCH 02/14] refactor(menus): Refactor to the Menus client service to use functional loops/filters (#1575) The for loops, expecially the nested for loops, over array elements with variable names contianing 'index' made the code incomprehensible. --- .../client/services/menu.client.service.js | 121 ++++++++---------- 1 file changed, 52 insertions(+), 69 deletions(-) diff --git a/modules/core/client/services/menu.client.service.js b/modules/core/client/services/menu.client.service.js index 86c4b46c..773180e0 100644 --- a/modules/core/client/services/menu.client.service.js +++ b/modules/core/client/services/menu.client.service.js @@ -5,7 +5,7 @@ .module('core') .factory('menuService', menuService); - function menuService() { + function menuService () { var shouldRender; var service = { addMenu: addMenu, @@ -25,7 +25,7 @@ return service; // Add new menu object by menu id - function addMenu(menuId, options) { + function addMenu (menuId, options) { options = options || {}; // Create the new menu @@ -40,12 +40,12 @@ } // Add menu item object - function addMenuItem(menuId, options) { - options = options || {}; - + function addMenuItem (menuId, options) { // Validate that the menu exists service.validateMenuExistence(menuId); + options = options || {}; + // Push new menu item service.menus[menuId].items.push({ title: options.title || '', @@ -60,11 +60,9 @@ // Add submenu items if (options.items) { - for (var i in options.items) { - if (options.items.hasOwnProperty(i)) { - service.addSubMenuItem(menuId, options.state, options.items[i]); - } - } + options.items.forEach(function (subMenuItem) { + service.addSubMenuItem(menuId, options.state, subMenuItem); + }); } // Return the menu object @@ -72,33 +70,32 @@ } // Add submenu item object - function addSubMenuItem(menuId, parentItemState, options) { + function addSubMenuItem (menuId, parentItemState, options) { options = options || {}; // Validate that the menu exists service.validateMenuExistence(menuId); // Search for menu item - for (var itemIndex in service.menus[menuId].items) { - if (service.menus[menuId].items[itemIndex].state === parentItemState) { - // Push new submenu item - service.menus[menuId].items[itemIndex].items.push({ - title: options.title || '', - state: options.state || '', - params: options.params || {}, - roles: ((options.roles === null || typeof options.roles === 'undefined') ? service.menus[menuId].items[itemIndex].roles : options.roles), - position: options.position || 0, - shouldRender: shouldRender - }); - } - } + service.menus[menuId].items.filter(function (item) { + return item.state === parentItemState; + }).forEach(function (item) { + item.items.push({ + title: options.title || '', + state: options.state || '', + params: options.params || {}, + roles: ((options.roles === null || typeof options.roles === 'undefined') ? item.roles : options.roles), + position: options.position || 0, + shouldRender: shouldRender + }); + }); // Return the menu object return service.menus[menuId]; } // Get the menu object by menu id - function getMenu(menuId) { + function getMenu (menuId) { // Validate that the menu exists service.validateMenuExistence(menuId); @@ -106,28 +103,22 @@ return service.menus[menuId]; } - function init() { + function init () { // A private function for rendering decision shouldRender = function (user) { if (this.roles.indexOf('*') !== -1) { return true; - } else { - if (!user) { - return false; - } - - for (var userRoleIndex in user.roles) { - if (user.roles.hasOwnProperty(userRoleIndex)) { - for (var roleIndex in this.roles) { - if (this.roles.hasOwnProperty(roleIndex) && this.roles[roleIndex] === user.roles[userRoleIndex]) { - return true; - } - } - } - } } - return false; + if (!user) { + return false; + } + + var matchingRoles = user.roles.filter(function (userRole) { + return this.roles.indexOf(userRole) !== -1; + }, this); + + return matchingRoles.length > 0; }; // Adding the topbar menu @@ -137,7 +128,7 @@ } // Remove existing menu object by menu id - function removeMenu(menuId) { + function removeMenu (menuId) { // Validate that the menu exists service.validateMenuExistence(menuId); @@ -145,52 +136,44 @@ } // Remove existing menu object by menu id - function removeMenuItem(menuId, menuItemState) { + function removeMenuItem (menuId, menuItemState) { // Validate that the menu exists service.validateMenuExistence(menuId); - // Search for menu item to remove - for (var itemIndex in service.menus[menuId].items) { - if (service.menus[menuId].items.hasOwnProperty(itemIndex) && service.menus[menuId].items[itemIndex].state === menuItemState) { - service.menus[menuId].items.splice(itemIndex, 1); - } - } + // Filter out menu items that do not match the current menu item state. + service.menus[menuId].items = service.menus[menuId].items.filter(function (item) { + return item.state !== menuItemState; + }); // Return the menu object return service.menus[menuId]; } // Remove existing menu object by menu id - function removeSubMenuItem(menuId, submenuItemState) { + function removeSubMenuItem (menuId, subMenuItemState) { // Validate that the menu exists service.validateMenuExistence(menuId); - // Search for menu item to remove - for (var itemIndex in service.menus[menuId].items) { - if (this.menus[menuId].items.hasOwnProperty(itemIndex)) { - for (var subitemIndex in service.menus[menuId].items[itemIndex].items) { - if (this.menus[menuId].items[itemIndex].items.hasOwnProperty(subitemIndex) && service.menus[menuId].items[itemIndex].items[subitemIndex].state === submenuItemState) { - service.menus[menuId].items[itemIndex].items.splice(subitemIndex, 1); - } - } - } - } + // Filter out sub-menu items that do not match the current subMenuItemState + service.menus[menuId].items.forEach(function (parentMenuItem) { + parentMenuItem.items = parentMenuItem.items.filter(function (subMenuItem) { + return subMenuItem.state !== subMenuItemState; + }); + }); // Return the menu object return service.menus[menuId]; } - // Validate menu existance - function validateMenuExistence(menuId) { - if (menuId && menuId.length) { - if (service.menus[menuId]) { - return true; - } else { - throw new Error('Menu does not exist'); - } - } else { + // Validate menu existence + function validateMenuExistence (menuId) { + if (!(menuId && menuId.length)) { throw new Error('MenuId was not provided'); } + if (!service.menus[menuId]) { + throw new Error('Menu does not exist'); + } + return true; } } }()); From 27f5065d52c005c62cb414a80a4454e5b35f23ba Mon Sep 17 00:00:00 2001 From: Snyk bot Date: Wed, 2 Aug 2017 00:42:23 +0300 Subject: [PATCH 03/14] fix(mocha): update mochajs version to reduce vulnerabilities (#1830) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/npm:ms:20170412 Latest report for meanjs/mean: https://snyk.io/test/github/meanjs/mean --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c2d14cf5..cd8d3c23 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "lodash": "~4.17.4", "lusca": "~1.4.1", "method-override": "~2.3.8", - "mocha": "~3.4.2", + "mocha": "~3.5.0", "mongoose": "~4.11.3", "morgan": "~1.8.1", "multer": "~1.3.0", From f65d4b90ca7343f919b4072dbb3f97d4daf17368 Mon Sep 17 00:00:00 2001 From: Mikael Korpela Date: Mon, 7 Aug 2017 16:38:48 +0300 Subject: [PATCH 04/14] feat(user): Add email support to forgot password (#1834) Adds support for recovering users account using email and username. Previously only username worked. --- .../password/forgot-password.client.view.html | 10 +-- .../users/users.password.server.controller.js | 16 +++-- .../tests/server/user.server.routes.tests.js | 70 ++++++++++++++++--- 3 files changed, 75 insertions(+), 21 deletions(-) diff --git a/modules/users/client/views/password/forgot-password.client.view.html b/modules/users/client/views/password/forgot-password.client.view.html index 47a6fa83..5a82256b 100644 --- a/modules/users/client/views/password/forgot-password.client.view.html +++ b/modules/users/client/views/password/forgot-password.client.view.html @@ -1,17 +1,17 @@

Restore your password

-

Enter your account username.

- -
-

Enter a username.

+ + +
+

Enter a username or email.

- +
diff --git a/modules/users/server/controllers/users/users.password.server.controller.js b/modules/users/server/controllers/users/users.password.server.controller.js index c5c9be86..d6dc1a4c 100644 --- a/modules/users/server/controllers/users/users.password.server.controller.js +++ b/modules/users/server/controllers/users/users.password.server.controller.js @@ -28,17 +28,23 @@ exports.forgot = function (req, res, next) { }, // Lookup user by username function (token, done) { - if (req.body.username) { + if (req.body.usernameOrEmail) { + + var usernameOrEmail = String(req.body.usernameOrEmail).toLowerCase(); + User.findOne({ - username: req.body.username.toLowerCase() + $or: [ + { username: usernameOrEmail }, + { email: usernameOrEmail } + ] }, '-salt -password', function (err, user) { if (err || !user) { return res.status(400).send({ - message: 'No account with that username has been found' + message: 'No account with that username or email has been found' }); } else if (user.provider !== 'local') { return res.status(400).send({ - message: 'It seems like you signed up using your ' + user.provider + ' account' + message: 'It seems like you signed up using your ' + user.provider + ' account, please sign in using that provider.' }); } else { user.resetPasswordToken = token; @@ -51,7 +57,7 @@ exports.forgot = function (req, res, next) { }); } else { return res.status(422).send({ - message: 'Username field must not be blank' + message: 'Username/email field must not be blank' }); } }, diff --git a/modules/users/tests/server/user.server.routes.tests.js b/modules/users/tests/server/user.server.routes.tests.js index 2dd8441d..2613a540 100644 --- a/modules/users/tests/server/user.server.routes.tests.js +++ b/modules/users/tests/server/user.server.routes.tests.js @@ -325,7 +325,7 @@ describe('User CRUD tests', function () { should.not.exist(err); agent.post('/api/auth/forgot') .send({ - username: 'some_username_that_doesnt_exist' + usernameOrEmail: 'some_username_that_doesnt_exist' }) .expect(400) .end(function (err, res) { @@ -334,13 +334,13 @@ describe('User CRUD tests', function () { return done(err); } - res.body.message.should.equal('No account with that username has been found'); + res.body.message.should.equal('No account with that username or email has been found'); return done(); }); }); }); - it('forgot password should return 400 for no username provided', function (done) { + it('forgot password should return 400 for empty username/email', function (done) { var provider = 'facebook'; user.provider = provider; user.roles = ['user']; @@ -349,7 +349,7 @@ describe('User CRUD tests', function () { should.not.exist(err); agent.post('/api/auth/forgot') .send({ - username: '' + usernameOrEmail: '' }) .expect(422) .end(function (err, res) { @@ -358,7 +358,29 @@ describe('User CRUD tests', function () { return done(err); } - res.body.message.should.equal('Username field must not be blank'); + res.body.message.should.equal('Username/email field must not be blank'); + return done(); + }); + }); + }); + + it('forgot password should return 400 for no username or email provided', function (done) { + var provider = 'facebook'; + user.provider = provider; + user.roles = ['user']; + + user.save(function (err) { + should.not.exist(err); + agent.post('/api/auth/forgot') + .send({}) + .expect(422) + .end(function (err, res) { + // Handle error + if (err) { + return done(err); + } + + res.body.message.should.equal('Username/email field must not be blank'); return done(); }); }); @@ -373,7 +395,7 @@ describe('User CRUD tests', function () { should.not.exist(err); agent.post('/api/auth/forgot') .send({ - username: user.username + usernameOrEmail: user.username }) .expect(400) .end(function (err, res) { @@ -382,20 +404,46 @@ describe('User CRUD tests', function () { return done(err); } - res.body.message.should.equal('It seems like you signed up using your ' + user.provider + ' account'); + res.body.message.should.equal('It seems like you signed up using your ' + user.provider + ' account, please sign in using that provider.'); return done(); }); }); }); - it('forgot password should be able to reset password for user password reset request', function (done) { + it('forgot password should be able to reset password for user password reset request using username', function (done) { user.roles = ['user']; user.save(function (err) { should.not.exist(err); agent.post('/api/auth/forgot') .send({ - username: user.username + usernameOrEmail: user.username + }) + .expect(400) + .end(function (err, res) { + // Handle error + if (err) { + return done(err); + } + + User.findOne({ username: user.username.toLowerCase() }, function(err, userRes) { + userRes.resetPasswordToken.should.not.be.empty(); + should.exist(userRes.resetPasswordExpires); + res.body.message.should.be.equal('Failure sending email'); + return done(); + }); + }); + }); + }); + + it('forgot password should be able to reset password for user password reset request using email', function (done) { + user.roles = ['user']; + + user.save(function (err) { + should.not.exist(err); + agent.post('/api/auth/forgot') + .send({ + usernameOrEmail: user.email }) .expect(400) .end(function (err, res) { @@ -421,7 +469,7 @@ describe('User CRUD tests', function () { should.not.exist(err); agent.post('/api/auth/forgot') .send({ - username: user.username + usernameOrEmail: user.username }) .expect(400) .end(function (err, res) { @@ -458,7 +506,7 @@ describe('User CRUD tests', function () { should.not.exist(err); agent.post('/api/auth/forgot') .send({ - username: user.username + usernameOrEmail: user.username }) .expect(400) .end(function (err, res) { From c69644db6556af443dd2cd640edd740c8ff02909 Mon Sep 17 00:00:00 2001 From: Pierre Brisorgueil Date: Thu, 10 Aug 2017 07:47:01 +0200 Subject: [PATCH 05/14] feat(core): Enhancement page title directive (#1686) * fix(users): test for usernameOrEmail * Add comment to remind change for mongo replicaset connection * clean comment .. * Generic pageTitle concept * Revert "Generic pageTitle concept" This reverts commit ff00ec950f085ca3b6d1abb564eab1965ab0a56e. * align on meanjs state * fix atom beautify newline * align to mean indent * pageTitle directive automatic pageTitle directive fix loads add .. clean lodash dependencies clean code clean code & indent clean lodash * pageTitle directive automatic pageTitle directive fix loads add .. clean lodash dependencies clean code clean code & indent clean lodash --- .../client/config/articles-admin.client.routes.js | 3 ++- .../client/config/articles.client.routes.js | 7 ++----- modules/chat/client/config/chat.client.routes.js | 3 +-- modules/core/client/config/core.client.routes.js | 9 +++------ .../directives/page-title.client.directive.js | 14 +++++++++----- .../client/config/users-admin.client.routes.js | 9 +++------ 6 files changed, 20 insertions(+), 25 deletions(-) diff --git a/modules/articles/client/config/articles-admin.client.routes.js b/modules/articles/client/config/articles-admin.client.routes.js index b493d0e0..877aeadb 100644 --- a/modules/articles/client/config/articles-admin.client.routes.js +++ b/modules/articles/client/config/articles-admin.client.routes.js @@ -41,7 +41,8 @@ controller: 'ArticlesAdminController', controllerAs: 'vm', data: { - roles: ['admin'] + roles: ['admin'], + pageTitle: '{{ articleResolve.title }}' }, resolve: { articleResolve: getArticle diff --git a/modules/articles/client/config/articles.client.routes.js b/modules/articles/client/config/articles.client.routes.js index 4a94fa2e..2638234d 100644 --- a/modules/articles/client/config/articles.client.routes.js +++ b/modules/articles/client/config/articles.client.routes.js @@ -18,10 +18,7 @@ url: '', templateUrl: '/modules/articles/client/views/list-articles.client.view.html', controller: 'ArticlesListController', - controllerAs: 'vm', - data: { - pageTitle: 'Articles List' - } + controllerAs: 'vm' }) .state('articles.view', { url: '/:articleId', @@ -32,7 +29,7 @@ articleResolve: getArticle }, data: { - pageTitle: 'Article {{ articleResolve.title }}' + pageTitle: '{{ articleResolve.title }}' } }); } diff --git a/modules/chat/client/config/chat.client.routes.js b/modules/chat/client/config/chat.client.routes.js index f69aff61..e1418054 100644 --- a/modules/chat/client/config/chat.client.routes.js +++ b/modules/chat/client/config/chat.client.routes.js @@ -15,8 +15,7 @@ controller: 'ChatController', controllerAs: 'vm', data: { - roles: ['user', 'admin'], - pageTitle: 'Chat' + roles: ['user', 'admin'] } }); } diff --git a/modules/core/client/config/core.client.routes.js b/modules/core/client/config/core.client.routes.js index c00f8807..0df4c89f 100644 --- a/modules/core/client/config/core.client.routes.js +++ b/modules/core/client/config/core.client.routes.js @@ -44,8 +44,7 @@ } }, data: { - ignoreState: true, - pageTitle: 'Not Found' + ignoreState: true } }) .state('bad-request', { @@ -59,16 +58,14 @@ } }, data: { - ignoreState: true, - pageTitle: 'Bad Request' + ignoreState: true } }) .state('forbidden', { url: '/forbidden', templateUrl: '/modules/core/client/views/403.client.view.html', data: { - ignoreState: true, - pageTitle: 'Forbidden' + ignoreState: true } }); } diff --git a/modules/core/client/directives/page-title.client.directive.js b/modules/core/client/directives/page-title.client.directive.js index 857d0b36..a2d05f2d 100644 --- a/modules/core/client/directives/page-title.client.directive.js +++ b/modules/core/client/directives/page-title.client.directive.js @@ -19,13 +19,17 @@ function listener(event, toState) { var applicationCoreTitle = 'MEAN.js', - separeteBy = ' - '; + separator = ' - ', + stateTitle = applicationCoreTitle + separator; + + toState.name.split('.').forEach(function(value, index) { + stateTitle = stateTitle + value.charAt(0).toUpperCase() + value.slice(1) + separator; + }); if (toState.data && toState.data.pageTitle) { - var stateTitle = $interpolate(toState.data.pageTitle)($state.$current.locals.globals); - element.html(applicationCoreTitle + separeteBy + stateTitle); - } else { - element.html(applicationCoreTitle); + stateTitle = $interpolate(stateTitle + toState.data.pageTitle + separator)(($state.$current.locals.globals)); } + stateTitle = stateTitle.slice(0, 0 - separator.length); + element.text(stateTitle); } } } diff --git a/modules/users/client/config/users-admin.client.routes.js b/modules/users/client/config/users-admin.client.routes.js index b9784695..cc509c03 100644 --- a/modules/users/client/config/users-admin.client.routes.js +++ b/modules/users/client/config/users-admin.client.routes.js @@ -14,10 +14,7 @@ url: '/users', templateUrl: '/modules/users/client/views/admin/list-users.client.view.html', controller: 'UserListController', - controllerAs: 'vm', - data: { - pageTitle: 'Users List' - } + controllerAs: 'vm' }) .state('admin.user', { url: '/users/:userId', @@ -28,7 +25,7 @@ userResolve: getUser }, data: { - pageTitle: 'Edit {{ userResolve.displayName }}' + pageTitle: '{{ userResolve.displayName }}' } }) .state('admin.user-edit', { @@ -40,7 +37,7 @@ userResolve: getUser }, data: { - pageTitle: 'Edit User {{ userResolve.displayName }}' + pageTitle: '{{ userResolve.displayName }}' } }); From c467c84e6086cb734a8c0ec2cbe13d6f9b1f5776 Mon Sep 17 00:00:00 2001 From: gbatz Date: Sun, 13 Aug 2017 20:08:42 +0300 Subject: [PATCH 06/14] fix(gulp): fix broken test:server:watch task (#1842) Pass the path string of the changed file to `gulp-refresh` plugin, so that an exception won't be thrown when test files are reloaded. --- gulpfile.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 870d5015..122bf764 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -109,11 +109,10 @@ gulp.task('watch:server:run-tests', function () { if (filePath === path.resolve(file.path)) { changedTestFiles.push(f); + plugins.refresh.changed(f); } }); }); - - plugins.refresh.changed(); }); }); From 1e3eeb7e3b5b330cc8fd510c8adf9cee05e64815 Mon Sep 17 00:00:00 2001 From: Mikael Korpela Date: Sun, 13 Aug 2017 20:10:55 +0300 Subject: [PATCH 07/14] feat(build): Turn on mangling for uglify (#1841) Turns on mangling for uglify (minified javascript for production). Previously this might've caused issues with AngularJS, but since we are now using `ngAnnotate`, those issues are gone. https://github.com/mishoo/UglifyJS2#minify-options --- gulpfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 122bf764..4a234cd1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -152,7 +152,7 @@ gulp.task('uglify', function () { return gulp.src(assets) .pipe(plugins.ngAnnotate()) .pipe(plugins.uglify({ - mangle: false + mangle: true })) .pipe(plugins.concat('application.min.js')) .pipe(plugins.rev()) From be88a2ca1fef09145aaa5386df869c9f965e8a52 Mon Sep 17 00:00:00 2001 From: Mikael Korpela Date: Sun, 13 Aug 2017 21:52:38 +0300 Subject: [PATCH 08/14] fix(users): don't fail on missing old image on image upload (#1839) Fixes scenarios where previously when old image file would be missing, uploading new image file over it would fail because unlinking previous file fails. --- .../users/users.profile.server.controller.js | 10 +++++- .../tests/server/user.server.routes.tests.js | 34 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/modules/users/server/controllers/users/users.profile.server.controller.js b/modules/users/server/controllers/users/users.profile.server.controller.js index 52fc23cd..55bd8797 100644 --- a/modules/users/server/controllers/users/users.profile.server.controller.js +++ b/modules/users/server/controllers/users/users.profile.server.controller.js @@ -111,7 +111,15 @@ exports.changeProfilePicture = function (req, res) { if (existingImageUrl !== User.schema.path('profileImageURL').defaultValue) { fs.unlink(existingImageUrl, function (unlinkError) { if (unlinkError) { - console.log(unlinkError); + + // If file didn't exist, no need to reject promise + if (unlinkError.code === 'ENOENT') { + console.log('Removing profile image failed because file did not exist.'); + return resolve(); + } + + console.error(unlinkError); + reject({ message: 'Error occurred while deleting old profile picture' }); diff --git a/modules/users/tests/server/user.server.routes.tests.js b/modules/users/tests/server/user.server.routes.tests.js index 2613a540..0c87471e 100644 --- a/modules/users/tests/server/user.server.routes.tests.js +++ b/modules/users/tests/server/user.server.routes.tests.js @@ -6,6 +6,7 @@ var semver = require('semver'), path = require('path'), mongoose = require('mongoose'), User = mongoose.model('User'), + config = require(path.resolve('./config/config')), express = require(path.resolve('./config/lib/express')); /** @@ -1078,6 +1079,39 @@ describe('User CRUD tests', function () { }); }); + it('should be able to change profile picture and not fail if existing picture file does not exist', function (done) { + + user.profileImageURL = config.uploads.profile.image.dest + 'non-existing.png'; + + user.save(function(saveErr) { + // Handle error + if (saveErr) { + return done(saveErr); + } + + agent.post('/api/auth/signin') + .send(credentials) + .expect(200) + .end(function (signinErr) { + // Handle signin error + if (signinErr) { + return done(signinErr); + } + + agent.post('/api/users/picture') + .attach('newProfilePicture', './modules/users/client/img/profile/default.png') + .expect(200) + .end(function (userInfoErr) { + + should.not.exist(userInfoErr); + + return done(); + }); + }); + + }); + }); + afterEach(function (done) { User.remove().exec(done); }); From eb9cdd784c40ec9d98a72dab35da3de34f845e06 Mon Sep 17 00:00:00 2001 From: Michael Leanos Date: Sun, 13 Aug 2017 16:29:47 -0700 Subject: [PATCH 09/14] feat(config): Mongo Seed 2.0 (#1808) feat(config): Mongo Seed 2.0 Adds a more configurable and easily extended MongoDB Seed feature. Adds additional options at the collection, and collection item level to allow more control over how each environment config handles the seeding feature. Enforces seed order based on the order of the environment's seeding configuration object. Removes the previous SeedDB config file. Adds chalk to messages logged to the console for readability. Refactors the Mongo Seed configuration tests. Adds Gulp tasks to perform Mongo Seed operations for default, prod, and test environment configurations. Also, adds accommodating npm scripts. --- config/env/development.js | 66 +- config/env/production.js | 34 +- config/env/test.js | 54 +- config/lib/app.js | 2 +- config/lib/mongo-seed.js | 153 +++ config/lib/seed.js | 158 --- gulpfile.js | 47 + .../server/models/article.server.model.js | 106 +- .../tests/server/core.server.config.tests.js | 942 ++++++++++++------ .../users/server/models/user.server.model.js | 91 +- package.json | 5 +- 11 files changed, 1122 insertions(+), 536 deletions(-) create mode 100644 config/lib/mongo-seed.js delete mode 100644 config/lib/seed.js diff --git a/config/env/development.js b/config/env/development.js index 42cbecf3..a125670a 100644 --- a/config/env/development.js +++ b/config/env/development.js @@ -70,25 +70,53 @@ module.exports = { seedDB: { seed: process.env.MONGO_SEED === 'true', options: { - logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false', - seedUser: { - username: process.env.MONGO_SEED_USER_USERNAME || 'seeduser', - provider: 'local', - email: process.env.MONGO_SEED_USER_EMAIL || 'user@localhost.com', - firstName: 'User', - lastName: 'Local', - displayName: 'User Local', - roles: ['user'] + logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false' + }, + // Order of collections in configuration will determine order of seeding. + // i.e. given these settings, the User seeds will be complete before + // Article seed is performed. + collections: [{ + model: 'User', + docs: [{ + data: { + username: 'local-admin', + email: 'admin@localhost.com', + firstName: 'Admin', + lastName: 'Local', + roles: ['admin', 'user'] + } + }, { + // Set to true to overwrite this document + // when it already exists in the collection. + // If set to false, or missing, the seed operation + // will skip this document to avoid overwriting it. + overwrite: true, + data: { + username: 'local-user', + email: 'user@localhost.com', + firstName: 'User', + lastName: 'Local', + roles: ['user'] + } + }] + }, { + model: 'Article', + options: { + // Override log results setting at the + // collection level. + logResults: true }, - seedAdmin: { - username: process.env.MONGO_SEED_ADMIN_USERNAME || 'seedadmin', - provider: 'local', - email: process.env.MONGO_SEED_ADMIN_EMAIL || 'admin@localhost.com', - firstName: 'Admin', - lastName: 'Local', - displayName: 'Admin Local', - roles: ['user', 'admin'] - } - } + skip: { + // Skip collection when this query returns results. + // e.g. {}: Only seeds collection when it is empty. + when: {} // Mongoose qualified query + }, + docs: [{ + data: { + title: 'First Article', + content: 'This is a seeded Article for the development environment' + } + }] + }] } }; diff --git a/config/env/production.js b/config/env/production.js index c43281fa..268c1f23 100644 --- a/config/env/production.js +++ b/config/env/production.js @@ -91,25 +91,19 @@ module.exports = { seedDB: { seed: process.env.MONGO_SEED === 'true', options: { - logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false', - seedUser: { - username: process.env.MONGO_SEED_USER_USERNAME || 'seeduser', - provider: 'local', - email: process.env.MONGO_SEED_USER_EMAIL || 'user@localhost.com', - firstName: 'User', - lastName: 'Local', - displayName: 'User Local', - roles: ['user'] - }, - seedAdmin: { - username: process.env.MONGO_SEED_ADMIN_USERNAME || 'seedadmin', - provider: 'local', - email: process.env.MONGO_SEED_ADMIN_EMAIL || 'admin@localhost.com', - firstName: 'Admin', - lastName: 'Local', - displayName: 'Admin Local', - roles: ['user', 'admin'] - } - } + logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false' + }, + collections: [{ + model: 'User', + docs: [{ + data: { + username: 'local-admin', + email: 'admin@localhost.com', + firstName: 'Admin', + lastName: 'Local', + roles: ['admin', 'user'] + } + }] + }] } }; diff --git a/config/env/test.js b/config/env/test.js index 0cbfb6bc..e2c74f01 100644 --- a/config/env/test.js +++ b/config/env/test.js @@ -80,25 +80,39 @@ module.exports = { seedDB: { seed: process.env.MONGO_SEED === 'true', options: { - logResults: process.env.MONGO_SEED_LOG_RESULTS !== 'false', - seedUser: { - username: process.env.MONGO_SEED_USER_USERNAME || 'seeduser', - provider: 'local', - email: process.env.MONGO_SEED_USER_EMAIL || 'user@localhost.com', - firstName: 'User', - lastName: 'Local', - displayName: 'User Local', - roles: ['user'] - }, - seedAdmin: { - username: process.env.MONGO_SEED_ADMIN_USERNAME || 'seedadmin', - provider: 'local', - email: process.env.MONGO_SEED_ADMIN_EMAIL || 'admin@localhost.com', - firstName: 'Admin', - lastName: 'Local', - displayName: 'Admin Local', - roles: ['user', 'admin'] - } - } + // Default to not log results for tests + logResults: process.env.MONGO_SEED_LOG_RESULTS === 'true' + }, + collections: [{ + model: 'User', + docs: [{ + overwrite: true, + data: { + username: 'seedadmin', + email: 'admin@localhost.com', + firstName: 'Admin', + lastName: 'Local', + roles: ['admin', 'user'] + } + }, { + overwrite: true, + data: { + username: 'seeduser', + email: 'user@localhost.com', + firstName: 'User', + lastName: 'Local', + roles: ['user'] + } + }] + }, { + model: 'Article', + docs: [{ + overwrite: true, + data: { + title: 'Test Article', + content: 'Code coverage test article!' + } + }] + }] } }; diff --git a/config/lib/app.js b/config/lib/app.js index 3321e78e..e9f38b08 100644 --- a/config/lib/app.js +++ b/config/lib/app.js @@ -7,7 +7,7 @@ var config = require('../config'), mongooseService = require('./mongoose'), express = require('./express'), chalk = require('chalk'), - seed = require('./seed'); + seed = require('./mongo-seed'); function seedDB() { if (config.seedDB && config.seedDB.seed) { diff --git a/config/lib/mongo-seed.js b/config/lib/mongo-seed.js new file mode 100644 index 00000000..8762d1e8 --- /dev/null +++ b/config/lib/mongo-seed.js @@ -0,0 +1,153 @@ +'use strict'; + +var _ = require('lodash'), + config = require('../config'), + mongoose = require('mongoose'), + chalk = require('chalk'); + +exports.start = start; + +function start(seedConfig) { + return new Promise(function (resolve, reject) { + seedConfig = seedConfig || {}; + + var options = seedConfig.options || (config.seedDB ? _.clone(config.seedDB.options, true) : {}); + var collections = seedConfig.collections || (config.seedDB ? _.clone(config.seedDB.collections, true) : []); + + if (!collections.length) { + return resolve(); + } + + var seeds = collections + .filter(function (collection) { + return collection.model; + }); + + // Use the reduction pattern to ensure we process seeding in desired order. + seeds.reduce(function (p, item) { + return p.then(function () { + return seed(item, options); + }); + }, Promise.resolve()) // start with resolved promise for initial previous (p) item + .then(onSuccessComplete) + .catch(onError); + + // Local Promise handlers + + function onSuccessComplete() { + if (options.logResults) { + console.log(); + console.log(chalk.bold.green('Database Seeding: Mongo Seed complete!')); + console.log(); + } + + return resolve(); + } + + function onError(err) { + if (options.logResults) { + console.log(); + console.log(chalk.bold.red('Database Seeding: Mongo Seed Failed!')); + console.log(chalk.bold.red('Database Seeding: ' + err)); + console.log(); + } + + return reject(err); + } + + }); +} + +function seed(collection, options) { + // Merge options with collection options + options = _.merge(options || {}, collection.options || {}); + + return new Promise(function (resolve, reject) { + const Model = mongoose.model(collection.model); + const docs = collection.docs; + + var skipWhen = collection.skip ? collection.skip.when : null; + + if (!Model.seed) { + return reject(new Error('Database Seeding: Invalid Model Configuration - ' + collection.model + '.seed() not implemented')); + } + + if (!docs || !docs.length) { + return resolve(); + } + + // First check if we should skip this collection + // based on the collection's "skip.when" option. + // NOTE: If it exists, "skip.when" should be a qualified + // Mongoose query that will be used with Model.find(). + skipCollection() + .then(seedDocuments) + .then(function () { + return resolve(); + }) + .catch(function (err) { + return reject(err); + }); + + function skipCollection() { + return new Promise(function (resolve, reject) { + if (!skipWhen) { + return resolve(false); + } + + Model + .find(skipWhen) + .exec(function (err, results) { + if (err) { + return reject(err); + } + + if (results && results.length) { + return resolve(true); + } + + return resolve(false); + }); + }); + } + + function seedDocuments(skipCollection) { + return new Promise(function (resolve, reject) { + + if (skipCollection) { + return onComplete([{ message: chalk.yellow('Database Seeding: ' + collection.model + ' collection skipped') }]); + } + + var workload = docs + .filter(function (doc) { + return doc.data; + }) + .map(function (doc) { + return Model.seed(doc.data, { overwrite: doc.overwrite }); + }); + + Promise.all(workload) + .then(onComplete) + .catch(onError); + + // Local Closures + + function onComplete(responses) { + if (options.logResults) { + responses.forEach(function (response) { + if (response.message) { + console.log(chalk.magenta(response.message)); + } + }); + } + + return resolve(); + } + + function onError(err) { + return reject(err); + } + }); + } + }); +} diff --git a/config/lib/seed.js b/config/lib/seed.js deleted file mode 100644 index eca41888..00000000 --- a/config/lib/seed.js +++ /dev/null @@ -1,158 +0,0 @@ -'use strict'; - -var _ = require('lodash'), - config = require('../config'), - mongoose = require('mongoose'), - chalk = require('chalk'), - crypto = require('crypto'); - -// global seed options object -var seedOptions = {}; - -function removeUser (user) { - return new Promise(function (resolve, reject) { - var User = mongoose.model('User'); - User.find({ username: user.username }).remove(function (err) { - if (err) { - reject(new Error('Failed to remove local ' + user.username)); - } - resolve(); - }); - }); -} - -function saveUser (user) { - return function() { - return new Promise(function (resolve, reject) { - // Then save the user - user.save(function (err, theuser) { - if (err) { - reject(new Error('Failed to add local ' + user.username)); - } else { - resolve(theuser); - } - }); - }); - }; -} - -function checkUserNotExists (user) { - return new Promise(function (resolve, reject) { - var User = mongoose.model('User'); - User.find({ username: user.username }, function (err, users) { - if (err) { - reject(new Error('Failed to find local account ' + user.username)); - } - - if (users.length === 0) { - resolve(); - } else { - reject(new Error('Failed due to local account already exists: ' + user.username)); - } - }); - }); -} - -function reportSuccess (password) { - return function (user) { - return new Promise(function (resolve, reject) { - if (seedOptions.logResults) { - console.log(chalk.bold.red('Database Seeding:\t\t\tLocal ' + user.username + ' added with password set to ' + password)); - } - resolve(); - }); - }; -} - -// save the specified user with the password provided from the resolved promise -function seedTheUser (user) { - return function (password) { - return new Promise(function (resolve, reject) { - - var User = mongoose.model('User'); - // set the new password - user.password = password; - - if (user.username === seedOptions.seedAdmin.username && process.env.NODE_ENV === 'production') { - checkUserNotExists(user) - .then(saveUser(user)) - .then(reportSuccess(password)) - .then(function () { - resolve(); - }) - .catch(function (err) { - reject(err); - }); - } else { - removeUser(user) - .then(saveUser(user)) - .then(reportSuccess(password)) - .then(function () { - resolve(); - }) - .catch(function (err) { - reject(err); - }); - } - }); - }; -} - -// report the error -function reportError (reject) { - return function (err) { - if (seedOptions.logResults) { - console.log(); - console.log('Database Seeding:\t\t\t' + err); - console.log(); - } - reject(err); - }; -} - -module.exports.start = function start(options) { - // Initialize the default seed options - seedOptions = _.clone(config.seedDB.options, true); - - // Check for provided options - - if (_.has(options, 'logResults')) { - seedOptions.logResults = options.logResults; - } - - if (_.has(options, 'seedUser')) { - seedOptions.seedUser = options.seedUser; - } - - if (_.has(options, 'seedAdmin')) { - seedOptions.seedAdmin = options.seedAdmin; - } - - var User = mongoose.model('User'); - return new Promise(function (resolve, reject) { - - var adminAccount = new User(seedOptions.seedAdmin); - var userAccount = new User(seedOptions.seedUser); - - // If production only seed admin if it does not exist - if (process.env.NODE_ENV === 'production') { - User.generateRandomPassphrase() - .then(seedTheUser(adminAccount)) - .then(function () { - resolve(); - }) - .catch(reportError(reject)); - } else { - // Add both Admin and User account - - User.generateRandomPassphrase() - .then(seedTheUser(userAccount)) - .then(User.generateRandomPassphrase) - .then(seedTheUser(adminAccount)) - .then(function () { - resolve(); - }) - .catch(reportError(reject)); - } - }); -}; diff --git a/gulpfile.js b/gulpfile.js index 4a234cd1..1d4784f5 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -380,6 +380,39 @@ gulp.task('dropdb', function (done) { }); }); +// Seed Mongo database based on configuration +gulp.task('mongo-seed', function (done) { + var db = require('./config/lib/mongoose'); + var seed = require('./config/lib/mongo-seed'); + + // Open mongoose database connection + db.connect(function () { + db.loadModels(); + + seed + .start({ + options: { + logResults: true + } + }) + .then(function () { + // Disconnect and finish task + db.disconnect(done); + }) + .catch(function (err) { + db.disconnect(function (disconnectError) { + if (disconnectError) { + console.log('Error disconnecting from the database, but was preceded by a Mongo Seed error.'); + } + + // Finish task with error + done(err); + }); + }); + }); + +}); + // Downloads the selenium webdriver if protractor version is compatible gulp.task('webdriver_update', webdriver_update); @@ -451,3 +484,17 @@ gulp.task('default', function (done) { gulp.task('prod', function (done) { runSequence(['copyLocalEnvConfig', 'makeUploadsDir', 'templatecache'], 'build', 'env:prod', 'lint', ['nodemon-nodebug', 'watch'], done); }); + +// Run Mongo Seed with default environment config +gulp.task('seed', function (done) { + runSequence('env:dev', 'mongo-seed', done); +}); + +// Run Mongo Seed with production environment config +gulp.task('seed:prod', function (done) { + runSequence('env:prod', 'mongo-seed', done); +}); + +gulp.task('seed:test', function (done) { + runSequence('env:test', 'mongo-seed', done); +}); diff --git a/modules/articles/server/models/article.server.model.js b/modules/articles/server/models/article.server.model.js index 8a557394..cc8f689d 100644 --- a/modules/articles/server/models/article.server.model.js +++ b/modules/articles/server/models/article.server.model.js @@ -4,7 +4,10 @@ * Module dependencies */ var mongoose = require('mongoose'), - Schema = mongoose.Schema; + Schema = mongoose.Schema, + path = require('path'), + config = require(path.resolve('./config/config')), + chalk = require('chalk'); /** * Article Schema @@ -31,4 +34,105 @@ var ArticleSchema = new Schema({ } }); +ArticleSchema.statics.seed = seed; + mongoose.model('Article', ArticleSchema); + +/** +* Seeds the User collection with document (Article) +* and provided options. +*/ +function seed(doc, options) { + var Article = mongoose.model('Article'); + + return new Promise(function (resolve, reject) { + + skipDocument() + .then(findAdminUser) + .then(add) + .then(function (response) { + return resolve(response); + }) + .catch(function (err) { + return reject(err); + }); + + function findAdminUser(skip) { + var User = mongoose.model('User'); + + return new Promise(function (resolve, reject) { + if (skip) { + return resolve(true); + } + + User + .findOne({ + roles: { $in: ['admin'] } + }) + .exec(function (err, admin) { + if (err) { + return reject(err); + } + + doc.user = admin; + + return resolve(); + }); + }); + } + + function skipDocument() { + return new Promise(function (resolve, reject) { + Article + .findOne({ + title: doc.title + }) + .exec(function (err, existing) { + if (err) { + return reject(err); + } + + if (!existing) { + return resolve(false); + } + + if (existing && !options.overwrite) { + return resolve(true); + } + + // Remove Article (overwrite) + + existing.remove(function (err) { + if (err) { + return reject(err); + } + + return resolve(false); + }); + }); + }); + } + + function add(skip) { + return new Promise(function (resolve, reject) { + if (skip) { + return resolve({ + message: chalk.yellow('Database Seeding: Article\t' + doc.title + ' skipped') + }); + } + + var article = new Article(doc); + + article.save(function (err) { + if (err) { + return reject(err); + } + + return resolve({ + message: 'Database Seeding: Article\t' + article.title + ' added' + }); + }); + }); + } + }); +} diff --git a/modules/core/tests/server/core.server.config.tests.js b/modules/core/tests/server/core.server.config.tests.js index c4f3159e..f5e83043 100644 --- a/modules/core/tests/server/core.server.config.tests.js +++ b/modules/core/tests/server/core.server.config.tests.js @@ -12,8 +12,9 @@ var _ = require('lodash'), request = require('supertest'), config = require(path.resolve('./config/config')), logger = require(path.resolve('./config/lib/logger')), - seed = require(path.resolve('./config/lib/seed')), - express = require(path.resolve('./config/lib/express')); + seed = require(path.resolve('./config/lib/mongo-seed')), + express = require(path.resolve('./config/lib/express')), + Article = mongoose.model('Article'); /** * Globals @@ -28,355 +29,666 @@ var app, describe('Configuration Tests:', function () { - describe('Testing default seedDB', function () { - before(function(done) { - User.remove(function(err) { - should.not.exist(err); + describe('Testing Mongo Seed', function () { + var _seedConfig = _.clone(config.seedDB, true); + var articleSeedConfig; + var userSeedConfig; + var _admin; + var _user; + var _article; - user1 = { - username: 'user_config_test', - provider: 'local', - email: 'user_config_test_@localhost.com', - firstName: 'User', - lastName: 'Local', - displayName: 'User Local', - roles: ['user'] - }; + before(function (done) { + _admin = { + username: 'test-seed-admin', + email: 'test-admin@localhost.com', + firstName: 'Admin', + lastName: 'Test', + roles: ['admin', 'user'] + }; - admin1 = { - username: 'admin_config_test', - provider: 'local', - email: 'admin_config_test_@localhost.com', - firstName: 'Admin', - lastName: 'Local', - displayName: 'Admin Local', - roles: ['user', 'admin'] - }; + _user = { + username: 'test-seed-user', + email: 'test-user@localhost.com', + firstName: 'User', + lastName: 'Test', + roles: ['user'] + }; - userFromSeedConfig = config.seedDB.options.seedUser; - adminFromSeedConfig = config.seedDB.options.seedAdmin; - - return done(); + _article = { + title: 'Testing Database Seed Article', + content: 'Testing Article Seed right now!' + }; + var articleCollections = _.filter(_seedConfig.collections, function (collection) { + return collection.model === 'Article'; }); - }); - after(function(done) { - User.remove(function(err) { - should.not.exist(err); - return done(); + // articleCollections.should.be.instanceof(Array).and.have.lengthOf(1); + articleSeedConfig = articleCollections[0]; + + var userCollections = _.filter(_seedConfig.collections, function (collection) { + return collection.model === 'User'; }); + + // userCollections.should.be.instanceof(Array).and.have.lengthOf(1); + userSeedConfig = userCollections[0]; + + return done(); }); - it('should have seedDB configuration set for "regular" user', function() { - (typeof userFromSeedConfig).should.not.equal('undefined'); - should.exist(userFromSeedConfig.username); - should.exist(userFromSeedConfig.email); - }); - - it('should have seedDB configuration set for admin user', function() { - (typeof adminFromSeedConfig).should.not.equal('undefined'); - should.exist(adminFromSeedConfig.username); - should.exist(adminFromSeedConfig.email); - }); - - it('should not be an admin user to begin with', function(done) { - User.find({ username: 'seedadmin' }, function(err, users) { - should.not.exist(err); - users.should.be.instanceof(Array).and.have.lengthOf(0); - return done(); - }); - }); - - it('should not be a "regular" user to begin with', function(done) { - User.find({ username: 'seeduser' }, function(err, users) { - should.not.exist(err); - users.should.be.instanceof(Array).and.have.lengthOf(0); - return done(); - }); - }); - - it('should seed ONLY the admin user account when NODE_ENV is set to "production"', function(done) { - - // Save original value - var nodeEnv = process.env.NODE_ENV; - // Set node env ro production environment - process.env.NODE_ENV = 'production'; - - User.find({ username: adminFromSeedConfig.username }, function(err, users) { - - // There shouldn't be any errors - should.not.exist(err); - users.should.be.instanceof(Array).and.have.lengthOf(0); - - seed - .start({ logResults: false }) - .then(function() { - User.find({ username: adminFromSeedConfig.username }, function(err, users) { - should.not.exist(err); - users.should.be.instanceof(Array).and.have.lengthOf(1); - - var _admin = users.pop(); - _admin.username.should.equal(adminFromSeedConfig.username); - - // Restore original NODE_ENV environment variable - process.env.NODE_ENV = nodeEnv; - - User.remove(function(err) { - should.not.exist(err); - return done(); - }); - }); - }); - }); - }); - - it('should seed admin, and "regular" user accounts when NODE_ENV is set to "test"', function(done) { - - // Save original value - var nodeEnv = process.env.NODE_ENV; - // Set node env ro production environment - process.env.NODE_ENV = 'test'; - - User.find({ username: adminFromSeedConfig.username }, function(err, users) { - - // There shouldn't be any errors - should.not.exist(err); - users.should.be.instanceof(Array).and.have.lengthOf(0); - - seed - .start({ logResults: false }) - .then(function() { - User.find({ username: adminFromSeedConfig.username }, function(err, users) { - should.not.exist(err); - users.should.be.instanceof(Array).and.have.lengthOf(1); - - var _admin = users.pop(); - _admin.username.should.equal(adminFromSeedConfig.username); - - User.find({ username: userFromSeedConfig.username }, function(err, users) { - - should.not.exist(err); - users.should.be.instanceof(Array).and.have.lengthOf(1); - - var _user = users.pop(); - _user.username.should.equal(userFromSeedConfig.username); - - // Restore original NODE_ENV environment variable - process.env.NODE_ENV = nodeEnv; - - User.remove(function(err) { - should.not.exist(err); - return done(); - }); - }); - }); - }); - }); - }); - - it('should seed admin, and "regular" user accounts when NODE_ENV is set to "test" when they already exist', function (done) { - - // Save original value - var nodeEnv = process.env.NODE_ENV; - // Set node env ro production environment - process.env.NODE_ENV = 'test'; - - var _user = new User(userFromSeedConfig); - var _admin = new User(adminFromSeedConfig); - - _admin.save(function (err) { - // There shouldn't be any errors - should.not.exist(err); - _user.save(function (err) { - // There shouldn't be any errors - should.not.exist(err); - - User.find({ username: { $in: [adminFromSeedConfig.username, userFromSeedConfig.username] } }, function (err, users) { - - // There shouldn't be any errors - should.not.exist(err); - users.should.be.instanceof(Array).and.have.lengthOf(2); - - seed - .start({ logResults: false }) - .then(function () { - User.find({ username: { $in: [adminFromSeedConfig.username, userFromSeedConfig.username] } }, function (err, users) { - should.not.exist(err); - users.should.be.instanceof(Array).and.have.lengthOf(2); - - // Restore original NODE_ENV environment variable - process.env.NODE_ENV = nodeEnv; - - User.remove(function (err) { - should.not.exist(err); - return done(); - }); - }); - }); - }); + afterEach(function (done) { + Article.remove().exec() + .then(function () { + return User.remove().exec(); + }) + .then(function () { + return done(); + }) + .catch(function (err) { + return done(err); }); - }); }); - it('should ONLY seed admin user account when NODE_ENV is set to "production" with custom admin', function(done) { + it('should have default seed configuration set for articles', function (done) { + articleSeedConfig.should.be.instanceof(Object); + articleSeedConfig.docs.should.be.instanceof(Array).and.have.lengthOf(1); + should.exist(articleSeedConfig.docs[0].data.title); + should.exist(articleSeedConfig.docs[0].data.content); - // Save original value - var nodeEnv = process.env.NODE_ENV; - // Set node env ro production environment - process.env.NODE_ENV = 'production'; + return done(); + }); - User.find({ username: admin1.username }, function(err, users) { + it('should have default seed configuration set for users', function (done) { + userSeedConfig.should.be.instanceof(Object); + userSeedConfig.docs.should.be.instanceof(Array).and.have.lengthOf(2); - // There shouldn't be any errors - should.not.exist(err); - users.should.be.instanceof(Array).and.have.lengthOf(0); + should.exist(userSeedConfig.docs[0].data.username); + should.exist(userSeedConfig.docs[0].data.email); + should.exist(userSeedConfig.docs[0].data.firstName); + should.exist(userSeedConfig.docs[0].data.lastName); + should.exist(userSeedConfig.docs[0].data.roles); - seed - .start({ logResults: false, seedAdmin: admin1 }) - .then(function() { - User.find({ username: admin1.username }, function(err, users) { - should.not.exist(err); - users.should.be.instanceof(Array).and.have.lengthOf(1); + should.exist(userSeedConfig.docs[1].data.username); + should.exist(userSeedConfig.docs[1].data.email); + should.exist(userSeedConfig.docs[1].data.firstName); + should.exist(userSeedConfig.docs[1].data.lastName); + should.exist(userSeedConfig.docs[1].data.roles); - var _admin = users.pop(); - _admin.username.should.equal(admin1.username); + return done(); + }); - // Restore original NODE_ENV environment variable - process.env.NODE_ENV = nodeEnv; + it('should seed data from default config', function (done) { - User.remove(function(err) { - should.not.exist(err); - return done(); - }); - }); + seed.start() + .then(function () { + // Check Articles Seed + return Article.find().exec(); + }) + .then(function (articles) { + articles.should.be.instanceof(Array).and.have.lengthOf(articleSeedConfig.docs.length); + // Check Users Seed + return User.find().exec(); + }) + .then(function (users) { + users.should.be.instanceof(Array).and.have.lengthOf(userSeedConfig.docs.length); + return done(); + }) + .catch(done); + }); + + it('should overwrite existing article by default', function (done) { + articleSeedConfig.docs.should.be.instanceof(Array).and.have.lengthOf(1); + + var article = new Article(articleSeedConfig.docs[0].data); + article.content = '_temp_test_article_'; + + // save temp article + article.save() + .then(function () { + return seed.start(); + }) + .then(function () { + return Article.find().exec(); + }) + .then(function (articles) { + articles.should.be.instanceof(Array).and.have.lengthOf(1); + + var newArticle = articles.pop(); + articleSeedConfig.docs[0].data.title.should.equal(newArticle.title); + articleSeedConfig.docs[0].data.content.should.equal(newArticle.content); + + return done(); + }) + .catch(done); + }); + + it('should overwrite existing users by default', function (done) { + userSeedConfig.docs.should.be.instanceof(Array).and.have.lengthOf(2); + + var admin = new User(userSeedConfig.docs[0].data); + admin.email = 'temp-admin@localhost.com'; + admin.provider = 'local'; + + var user = new User(userSeedConfig.docs[1].data); + user.email = 'temp-user@localhost.com'; + user.provider = 'local'; + + admin.save() + .then(function () { + return user.save(); + }) + .then(function () { + return User.find().exec(); + }) + .then(function (users) { + users.should.be.instanceof(Array).and.have.lengthOf(2); + // Start Seed + return seed.start(); + }) + .then(function () { + return User.find().exec(); + }) + .then(function (users) { + // Should still only be two users, since we removed + // the existing users before seeding again. + users.should.be.instanceof(Array).and.have.lengthOf(2); + + return User.find({ username: admin.username }).exec(); + }) + .then(function (users) { + users.should.be.instanceof(Array).and.have.lengthOf(1); + + var newAdmin = users.pop(); + userSeedConfig.docs[0].data.username.should.equal(newAdmin.username); + userSeedConfig.docs[0].data.email.should.equal(newAdmin.email); + + return User.find({ username: user.username }).exec(); + }) + .then(function (users) { + users.should.be.instanceof(Array).and.have.lengthOf(1); + + var newUser = users.pop(); + userSeedConfig.docs[1].data.username.should.equal(newUser.username); + userSeedConfig.docs[1].data.email.should.equal(newUser.email); + + return done(); + }) + .catch(done); + }); + + it('should seed single article with custom options', function (done) { + seed + .start({ + collections: [{ + model: 'Article', + docs: [{ + overwrite: true, + data: _article + }] + }] + }) + .then(function () { + return Article.find().exec(); + }) + .then(function (articles) { + articles.should.be.instanceof(Array).and.have.lengthOf(1); + + var newArticle = articles.pop(); + _article.title.should.equal(newArticle.title); + _article.content.should.equal(newArticle.content); + + return done(); + }) + .catch(done); + }); + + it('should seed single article with user set to custom seeded admin user', function (done) { + seed + .start({ + collections: [{ + model: 'User', + docs: [{ + data: _admin + }] + }, { + model: 'Article', + docs: [{ + overwrite: true, + data: _article + }] + }] + }) + .then(function () { + return User.find().exec(); + }) + .then(function (users) { + users.should.be.instanceof(Array).and.have.lengthOf(1); + + return Article + .find() + .populate('user', 'firstName lastName username email roles') + .exec(); + }) + .then(function (articles) { + articles.should.be.instanceof(Array).and.have.lengthOf(1); + + var newArticle = articles.pop(); + _article.title.should.equal(newArticle.title); + _article.content.should.equal(newArticle.content); + + should.exist(newArticle.user); + should.exist(newArticle.user._id); + + _admin.username.should.equal(newArticle.user.username); + _admin.email.should.equal(newArticle.user.email); + _admin.firstName.should.equal(newArticle.user.firstName); + _admin.lastName.should.equal(newArticle.user.lastName); + + should.exist(newArticle.user.roles); + newArticle.user.roles.indexOf('admin').should.equal(_admin.roles.indexOf('admin')); + + return done(); + }) + .catch(done); + }); + + it('should seed single article with NO user set due to seed order', function (done) { + seed + .start({ + collections: [{ + model: 'Article', + docs: [{ + overwrite: true, + data: _article + }] + }, { + model: 'User', + docs: [{ + data: _admin + }] + }] + }) + .then(function () { + return User.find().exec(); + }) + .then(function (users) { + users.should.be.instanceof(Array).and.have.lengthOf(1); + + return Article + .find() + .populate('user', 'firstName lastName username email roles') + .exec(); + }) + .then(function (articles) { + articles.should.be.instanceof(Array).and.have.lengthOf(1); + + var newArticle = articles.pop(); + _article.title.should.equal(newArticle.title); + _article.content.should.equal(newArticle.content); + + should.not.exist(newArticle.user); + + return done(); + }) + .catch(done); + }); + + it('should seed admin and user accounts with custom options', function (done) { + seed + .start({ + collections: [{ + model: 'User', + docs: [{ + data: _admin + }, { + data: _user + }] + }] + }) + .then(function () { + return User.find().exec(); + }) + .then(function (users) { + users.should.be.instanceof(Array).and.have.lengthOf(2); + return User.find({ username: _admin.username }).exec(); + }) + .then(function (users) { + users.should.be.instanceof(Array).and.have.lengthOf(1); + + var newAdmin = users.pop(); + _admin.username.should.equal(newAdmin.username); + _admin.email.should.equal(newAdmin.email); + + return User.find({ username: _user.username }).exec(); + }) + .then(function (users) { + users.should.be.instanceof(Array).and.have.lengthOf(1); + + var newUser = users.pop(); + _user.username.should.equal(newUser.username); + _user.email.should.equal(newUser.email); + + return done(); + }) + .catch(done); + }); + + it('should NOT overwrite existing article with custom options', function (done) { + + var article = new Article(_article); + article.content = '_temp_article_content_'; + + article.save() + .then(function () { + return seed.start({ + collections: [{ + model: 'Article', + docs: [{ + overwrite: false, + data: _article + }] + }] }); - }); + }) + .then(function () { + return Article.find().exec(); + }) + .then(function (articles) { + articles.should.be.instanceof(Array).and.have.lengthOf(1); + + var existingArticle = articles.pop(); + article.title.should.equal(existingArticle.title); + article.content.should.equal(existingArticle.content); + + return done(); + }) + .catch(done); }); - it('should seed admin, and "regular" user accounts when NODE_ENV is set to "test" with custom options', function(done) { + it('should NOT overwrite existing user with custom options', function (done) { + var user = new User(_user); + user.provider = 'local'; + user.email = 'temp-test-user@localhost.com'; - // Save original value - var nodeEnv = process.env.NODE_ENV; - // Set node env ro production environment - process.env.NODE_ENV = 'test'; - - User.find({ username: admin1.username }, function(err, users) { - - // There shouldn't be any errors - should.not.exist(err); - users.should.be.instanceof(Array).and.have.lengthOf(0); - - seed - .start({ logResults: false, seedAdmin: admin1, seedUser: user1 }) - .then(function() { - User.find({ username: admin1.username }, function(err, users) { - should.not.exist(err); - users.should.be.instanceof(Array).and.have.lengthOf(1); - - var _admin = users.pop(); - _admin.username.should.equal(admin1.username); - - User.find({ username: user1.username }, function(err, users) { - - should.not.exist(err); - users.should.be.instanceof(Array).and.have.lengthOf(1); - - var _user = users.pop(); - _user.username.should.equal(user1.username); - - // Restore original NODE_ENV environment variable - process.env.NODE_ENV = nodeEnv; - - User.remove(function(err) { - should.not.exist(err); - return done(); - }); - }); - }); + user.save() + .then(function () { + return seed.start({ + collections: [{ + model: 'User', + docs: [{ + overwrite: false, + data: _user + }] + }] }); - }); + }) + .then(function () { + return User.find().exec(); + }) + .then(function (users) { + users.should.be.instanceof(Array).and.have.lengthOf(1); + + var existingUser = users.pop(); + user.username.should.equal(existingUser.username); + user.email.should.equal(existingUser.email); + + return done(); + }) + .catch(done); }); - it('should NOT seed admin user account if it already exists when NODE_ENV is set to "production"', function(done) { - - // Save original value - var nodeEnv = process.env.NODE_ENV; - // Set node env ro production environment - process.env.NODE_ENV = 'production'; - - var _admin = new User(adminFromSeedConfig); - - _admin.save(function(err, user) { - // There shouldn't be any errors - should.not.exist(err); - user.username.should.equal(adminFromSeedConfig.username); - - seed - .start({ logResults: false }) - .then(function () { - // we don't ever expect to make it here but we don't want to timeout - User.remove(function(err) { - should.not.exist(err); - // force this test to fail since we should never be here - should.exist(undefined); - // Restore original NODE_ENV environment variable - process.env.NODE_ENV = nodeEnv; - - return done(); - }); - }) - .catch(function (err) { - should.exist(err); - err.message.should.equal('Failed due to local account already exists: ' + adminFromSeedConfig.username); - - // Restore original NODE_ENV environment variable - process.env.NODE_ENV = nodeEnv; - - User.remove(function(removeErr) { - should.not.exist(removeErr); - - return done(); - }); - }); - }); - }); - - it('should NOT seed "regular" user account if missing email when NODE_ENV set to "test"', function (done) { - - // Save original value - var nodeEnv = process.env.NODE_ENV; - // Set node env ro test environment - process.env.NODE_ENV = 'test'; - - var _user = new User(user1); - _user.email = ''; + it('should NOT seed article when missing title with custom options', function (done) { + var invalid = { + content: '_temp_article_content_' + }; seed - .start({ logResults: false, seedUser: _user }) + .start({ + collections: [{ + model: 'Article', + docs: [{ + data: invalid + }] + }] + }) .then(function () { - // we don't ever expect to make it here but we don't want to timeout - User.remove(function(err) { - // force this test to fail since we should never be here - should.exist(undefined); - // Restore original NODE_ENV environment variable - process.env.NODE_ENV = nodeEnv; - - return done(); - }); + // We should not make it here so we + // force an assert failure to prevent hangs. + should.exist(undefined); + return done(); }) .catch(function (err) { should.exist(err); - err.message.should.equal('Failed to add local ' + user1.username); + err.message.should.equal('Article validation failed: title: Title cannot be blank'); - // Restore original NODE_ENV environment variable - process.env.NODE_ENV = nodeEnv; + return done(); + }); + }); - User.remove(function(removeErr) { - should.not.exist(removeErr); + it('should NOT seed user when missing username with custom options', function (done) { + var invalid = _.clone(_user, true); + invalid.username = undefined; - return done(); + seed + .start({ + collections: [{ + model: 'User', + docs: [{ + data: invalid + }] + }] + }) + .then(function () { + // We should not make it here so we + // force an assert failure to prevent hangs. + should.exist(undefined); + return done(); + }) + .catch(function (err) { + should.exist(err); + err.message.should.equal('User validation failed: username: Please fill in a username'); + + return done(); + }); + }); + + it('should NOT seed user when missing email with custom options', function (done) { + var invalid = _.clone(_user, true); + invalid.email = undefined; + + seed + .start({ + collections: [{ + model: 'User', + docs: [{ + data: invalid + }] + }] + }) + .then(function () { + // We should not make it here so we + // force an assert failure to prevent hangs. + should.exist(undefined); + return done(); + }) + .catch(function (err) { + should.exist(err); + err.message.should.equal('User validation failed: email: Please fill a valid email address'); + + return done(); + }); + }); + + it('should NOT seed user with invalid email with custom options', function (done) { + var invalid = _.clone(_user, true); + invalid.email = '...invalid-email...'; + + seed + .start({ + collections: [{ + model: 'User', + docs: [{ + data: invalid + }] + }] + }) + .then(function () { + // We should not make it here so we + // force an assert failure to prevent hangs. + should.exist(undefined); + return done(); + }) + .catch(function (err) { + should.exist(err); + err.message.should.equal('User validation failed: email: Please fill a valid email address'); + + return done(); + }); + }); + + it('should NOT continue seed when empty collections config', function (done) { + seed + .start({ + collections: [] + }) + .then(function () { + return Article.find().exec(); + }) + .then(function (articles) { + articles.should.be.instanceof(Array).and.have.lengthOf(0); + + return User.find().exec(); + }) + .then(function (users) { + users.should.be.instanceof(Array).and.have.lengthOf(0); + + return done(); + }) + .catch(done); + }); + + it('should NOT seed any data when empty docs config', function (done) { + seed + .start({ + collections: [{ + model: 'Article', + docs: [] + }] + }) + .then(function () { + return Article.find().exec(); + }) + .then(function (articles) { + articles.should.be.instanceof(Array).and.have.lengthOf(0); + + return User.find().exec(); + }) + .then(function (users) { + users.should.be.instanceof(Array).and.have.lengthOf(0); + + return done(); + }) + .catch(done); + }); + + it('should seed article with custom options & skip.when results are empty', function (done) { + seed + .start({ + collections: [{ + model: 'Article', + skip: { + when: { title: 'should-not-find-this-title' } + }, + docs: [{ + data: _article + }] + }] + }) + .then(function () { + return Article.find().exec(); + }) + .then(function (articles) { + articles.should.be.instanceof(Array).and.have.lengthOf(1); + + var newArticle = articles.pop(); + _article.title.should.be.equal(newArticle.title); + _article.content.should.be.equal(newArticle.content); + + return done(); + }) + .catch(done); + }); + + it('should skip seed on collection with custom options & skip.when has results', function (done) { + var article = new Article({ + title: 'temp-article-title', + content: 'temp-article-content' + }); + + article + .save() + .then(function () { + return Article.find().exec(); + }) + .then(function (articles) { + articles.should.be.instanceof(Array).and.have.lengthOf(1); + + var newArticle = articles.pop(); + article.title.should.equal(newArticle.title); + article.content.should.equal(newArticle.content); + + return seed.start({ + collections: [{ + model: 'Article', + skip: { + when: { title: newArticle.title } + }, + docs: [{ + data: _article + }] + }] }); + }) + .then(function () { + return Article.find().exec(); + }) + .then(function (articles) { + // We should have the same article added at start of this unit test. + articles.should.be.instanceof(Array).and.have.lengthOf(1); + + var existingArticle = articles.pop(); + article.title.should.equal(existingArticle.title); + article.content.should.equal(existingArticle.content); + + return done(); + }) + .catch(done); + }); + + it('should fail seed with custom options & invalid skip.when query', function (done) { + seed + .start({ + collections: [{ + model: 'Article', + skip: { + when: { created: 'not-a-valid-date' } + }, + docs: [{ + data: _article + }] + }] + }) + .then(function () { + // We should not get here + should.exist(undefined); + return done(); + }) + .catch(function (err) { + should.exist(err); + // We expect the error message to include + err.message.indexOf('Cast to date failed').should.equal(0); + + return done(); }); }); }); diff --git a/modules/users/server/models/user.server.model.js b/modules/users/server/models/user.server.model.js index 1dc4fc24..940085c4 100644 --- a/modules/users/server/models/user.server.model.js +++ b/modules/users/server/models/user.server.model.js @@ -10,7 +10,8 @@ var mongoose = require('mongoose'), crypto = require('crypto'), validator = require('validator'), generatePassword = require('generate-password'), - owasp = require('owasp-password-strength-test'); + owasp = require('owasp-password-strength-test'), + chalk = require('chalk'); owasp.config(config.shared.owasp); @@ -230,4 +231,92 @@ UserSchema.statics.generateRandomPassphrase = function () { }); }; +UserSchema.statics.seed = seed; + mongoose.model('User', UserSchema); + +/** +* Seeds the User collection with document (User) +* and provided options. +*/ +function seed(doc, options) { + var User = mongoose.model('User'); + + return new Promise(function (resolve, reject) { + + skipDocument() + .then(add) + .then(function (response) { + return resolve(response); + }) + .catch(function (err) { + return reject(err); + }); + + function skipDocument() { + return new Promise(function (resolve, reject) { + User + .findOne({ + username: doc.username + }) + .exec(function (err, existing) { + if (err) { + return reject(err); + } + + if (!existing) { + return resolve(false); + } + + if (existing && !options.overwrite) { + return resolve(true); + } + + // Remove User (overwrite) + + existing.remove(function (err) { + if (err) { + return reject(err); + } + + return resolve(false); + }); + }); + }); + } + + function add(skip) { + return new Promise(function (resolve, reject) { + + if (skip) { + return resolve({ + message: chalk.yellow('Database Seeding: User\t\t' + doc.username + ' skipped') + }); + } + + User.generateRandomPassphrase() + .then(function (passphrase) { + var user = new User(doc); + + user.provider = 'local'; + user.displayName = user.firstName + ' ' + user.lastName; + user.password = passphrase; + + user.save(function (err) { + if (err) { + return reject(err); + } + + return resolve({ + message: 'Database Seeding: User\t\t' + user.username + ' added with password set to ' + passphrase + }); + }); + }) + .catch(function (err) { + return reject(err); + }); + }); + } + + }); +} diff --git a/package.json b/package.json index cd8d3c23..5e04ed1a 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,10 @@ "test:e2e": "gulp test:e2e", "test:coverage": "gulp test:coverage", "postinstall": "npm run bower", - "generate-ssl-certs": "scripts/generate-ssl-certs.sh" + "generate-ssl-certs": "scripts/generate-ssl-certs.sh", + "seed": "gulp seed", + "seed:prod": "gulp seed:prod", + "seed:test": "gulp seed:test" }, "dependencies": { "acl": "~0.4.10", From c6d4a17ab23dd36c789e2c1fde640c92c93fd1fb Mon Sep 17 00:00:00 2001 From: Mikael Korpela Date: Mon, 14 Aug 2017 11:09:56 +0300 Subject: [PATCH 10/14] Remove deprecated crypto package (#1843) `crypto` is now part of NodeJS core: https://www.npmjs.com/package/crypto --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 5e04ed1a..7fa4811d 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "connect-flash": "~0.1.1", "connect-mongo": "~1.3.2", "cookie-parser": "~1.4.1", - "crypto": "0.0.3", "express": "~4.15.2", "express-hbs": "^1.0.4", "express-session": "~1.15.2", From 4fcf24055020f0add85aa809b19ec5ae9d1a8fd9 Mon Sep 17 00:00:00 2001 From: Liran Tal Date: Mon, 14 Aug 2017 23:47:52 +0300 Subject: [PATCH 11/14] fix(mongodb): update ssl connection settings (#1809) --- config/env/production.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/config/env/production.js b/config/env/production.js index 268c1f23..414eaf02 100644 --- a/config/env/production.js +++ b/config/env/production.js @@ -16,19 +16,19 @@ module.exports = { uri: process.env.MONGOHQ_URL || process.env.MONGODB_URI || 'mongodb://' + (process.env.DB_1_PORT_27017_TCP_ADDR || 'localhost') + '/mean', options: { /** - * Uncomment to enable ssl certificate based authentication to mongodb - * servers. Adjust the settings below for your specific certificate - * setup. - * for connect to a replicaset, rename server:{...} to replset:{...} - server: { - ssl: true, - sslValidate: false, - checkServerIdentity: false, - sslCA: fs.readFileSync('./config/sslcerts/ssl-ca.pem'), - sslCert: fs.readFileSync('./config/sslcerts/ssl-cert.pem'), - sslKey: fs.readFileSync('./config/sslcerts/ssl-key.pem'), - sslPass: '1234' - } + * Uncomment to enable ssl certificate based authentication to mongodb + * servers. Adjust the settings below for your specific certificate + * setup. + * for connect to a replicaset, rename server:{...} to replset:{...} + + ssl: true, + sslValidate: false, + checkServerIdentity: false, + sslCA: fs.readFileSync('./config/sslcerts/ssl-ca.pem'), + sslCert: fs.readFileSync('./config/sslcerts/ssl-cert.pem'), + sslKey: fs.readFileSync('./config/sslcerts/ssl-key.pem'), + sslPass: '1234' + */ }, // Enable mongoose debug mode From f44c9bce71eb7fe52a0d87a22abbad199f597f21 Mon Sep 17 00:00:00 2001 From: Mikael Korpela Date: Mon, 14 Aug 2017 23:50:33 +0300 Subject: [PATCH 12/14] fix(eslint): Inconsistent spacing before function parentheses (#1844) Defines `space-before-function-paren` eslint rule and changes files accordingly. --- .eslintrc.js | 6 +- config/lib/logger.js | 2 +- gulpfile.js | 6 +- .../admin/article.client.controller.js | 4 +- modules/core/client/app/config.js | 2 +- .../core/client/config/core.client.routes.js | 4 +- .../directives/auto-focus.client.directive.js | 2 +- .../directives/page-title.client.directive.js | 2 +- .../client/services/menu.client.service.js | 20 +- .../auth-interceptor.client.tests.js | 22 +-- .../client/menus.client.service.tests.js | 184 +++++++++--------- .../client/socket.io.client.service.tests.js | 2 +- .../tests/server/core.server.config.tests.js | 2 +- .../users/users.profile.server.controller.js | 8 +- .../users/server/models/user.server.model.js | 2 +- ...ssword-validator.client.directive.tests.js | 36 ++-- .../password-verify.client.directive.tests.js | 10 +- .../password.client.controller.tests.js | 58 +++--- modules/users/tests/e2e/users.e2e.tests.js | 2 +- .../tests/server/user.server.model.tests.js | 42 ++-- .../tests/server/user.server.routes.tests.js | 8 +- 21 files changed, 214 insertions(+), 210 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index bb693600..c94123ae 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -40,7 +40,11 @@ module.exports = { 'one-var': [0, 'never'], 'one-var-declaration-per-line': [2, 'always'], 'padded-blocks': 0, - 'space-before-function-paren': 0, + 'space-before-function-paren': ['error', { + 'anonymous': 'always', + 'named': 'never', + 'asyncArrow': 'always' + }], 'space-in-parens': [2, 'never'], 'spaced-comment': [2, 'always'], strict: 0, diff --git a/config/lib/logger.js b/config/lib/logger.js index d4f3774e..a388a520 100644 --- a/config/lib/logger.js +++ b/config/lib/logger.js @@ -29,7 +29,7 @@ var logger = new winston.Logger({ // Useful for integrating with stream-related mechanism like Morgan's stream // option to log all HTTP requests to a file logger.stream = { - write: function(msg) { + write: function (msg) { logger.info(msg); } }; diff --git a/gulpfile.js b/gulpfile.js index 1d4784f5..ca79cb57 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -336,7 +336,7 @@ gulp.task('karma', function (done) { }); // Run karma with coverage options set and write report -gulp.task('karma:coverage', function(done) { +gulp.task('karma:coverage', function (done) { new KarmaServer({ configFile: __dirname + '/karma.conf.js', preprocessors: { @@ -427,12 +427,12 @@ gulp.task('protractor', ['webdriver_update'], function () { .pipe(protractor({ configFile: 'protractor.conf.js' })) - .on('end', function() { + .on('end', function () { console.log('E2E Testing complete'); // exit with success. process.exit(0); }) - .on('error', function(err) { + .on('error', function (err) { console.error('E2E Tests failed:'); console.error(err); process.exit(1); diff --git a/modules/articles/client/controllers/admin/article.client.controller.js b/modules/articles/client/controllers/admin/article.client.controller.js index 97f730d3..6fd3438e 100644 --- a/modules/articles/client/controllers/admin/article.client.controller.js +++ b/modules/articles/client/controllers/admin/article.client.controller.js @@ -1,4 +1,4 @@ -(function () { +(function () { 'use strict'; angular @@ -19,7 +19,7 @@ // Remove existing Article function remove() { if ($window.confirm('Are you sure you want to delete?')) { - vm.article.$remove(function() { + vm.article.$remove(function () { $state.go('admin.articles.list'); Notification.success({ message: ' Article deleted successfully!' }); }); diff --git a/modules/core/client/app/config.js b/modules/core/client/app/config.js index 318721fd..b25b905e 100644 --- a/modules/core/client/app/config.js +++ b/modules/core/client/app/config.js @@ -22,7 +22,7 @@ } // Angular-ui-notification configuration - angular.module('ui-notification').config(function(NotificationProvider) { + angular.module('ui-notification').config(function (NotificationProvider) { NotificationProvider.setOptions({ delay: 2000, startTop: 20, diff --git a/modules/core/client/config/core.client.routes.js b/modules/core/client/config/core.client.routes.js index 0df4c89f..69593f05 100644 --- a/modules/core/client/config/core.client.routes.js +++ b/modules/core/client/config/core.client.routes.js @@ -39,7 +39,7 @@ controller: 'ErrorController', controllerAs: 'vm', params: { - message: function($stateParams) { + message: function ($stateParams) { return $stateParams.message; } }, @@ -53,7 +53,7 @@ controller: 'ErrorController', controllerAs: 'vm', params: { - message: function($stateParams) { + message: function ($stateParams) { return $stateParams.message; } }, diff --git a/modules/core/client/directives/auto-focus.client.directive.js b/modules/core/client/directives/auto-focus.client.directive.js index a8307fcb..e01aef02 100644 --- a/modules/core/client/directives/auto-focus.client.directive.js +++ b/modules/core/client/directives/auto-focus.client.directive.js @@ -19,7 +19,7 @@ function link(scope, element, attrs) { if ($window.innerWidth >= 800) { - $timeout(function() { + $timeout(function () { var el = element[0]; el.focus(); el.selectionStart = el.selectionEnd = el.value.length; diff --git a/modules/core/client/directives/page-title.client.directive.js b/modules/core/client/directives/page-title.client.directive.js index a2d05f2d..bd94938b 100644 --- a/modules/core/client/directives/page-title.client.directive.js +++ b/modules/core/client/directives/page-title.client.directive.js @@ -22,7 +22,7 @@ separator = ' - ', stateTitle = applicationCoreTitle + separator; - toState.name.split('.').forEach(function(value, index) { + toState.name.split('.').forEach(function (value, index) { stateTitle = stateTitle + value.charAt(0).toUpperCase() + value.slice(1) + separator; }); if (toState.data && toState.data.pageTitle) { diff --git a/modules/core/client/services/menu.client.service.js b/modules/core/client/services/menu.client.service.js index 773180e0..e73b386a 100644 --- a/modules/core/client/services/menu.client.service.js +++ b/modules/core/client/services/menu.client.service.js @@ -5,7 +5,7 @@ .module('core') .factory('menuService', menuService); - function menuService () { + function menuService() { var shouldRender; var service = { addMenu: addMenu, @@ -25,7 +25,7 @@ return service; // Add new menu object by menu id - function addMenu (menuId, options) { + function addMenu(menuId, options) { options = options || {}; // Create the new menu @@ -40,7 +40,7 @@ } // Add menu item object - function addMenuItem (menuId, options) { + function addMenuItem(menuId, options) { // Validate that the menu exists service.validateMenuExistence(menuId); @@ -70,7 +70,7 @@ } // Add submenu item object - function addSubMenuItem (menuId, parentItemState, options) { + function addSubMenuItem(menuId, parentItemState, options) { options = options || {}; // Validate that the menu exists @@ -95,7 +95,7 @@ } // Get the menu object by menu id - function getMenu (menuId) { + function getMenu(menuId) { // Validate that the menu exists service.validateMenuExistence(menuId); @@ -103,7 +103,7 @@ return service.menus[menuId]; } - function init () { + function init() { // A private function for rendering decision shouldRender = function (user) { if (this.roles.indexOf('*') !== -1) { @@ -128,7 +128,7 @@ } // Remove existing menu object by menu id - function removeMenu (menuId) { + function removeMenu(menuId) { // Validate that the menu exists service.validateMenuExistence(menuId); @@ -136,7 +136,7 @@ } // Remove existing menu object by menu id - function removeMenuItem (menuId, menuItemState) { + function removeMenuItem(menuId, menuItemState) { // Validate that the menu exists service.validateMenuExistence(menuId); @@ -150,7 +150,7 @@ } // Remove existing menu object by menu id - function removeSubMenuItem (menuId, subMenuItemState) { + function removeSubMenuItem(menuId, subMenuItemState) { // Validate that the menu exists service.validateMenuExistence(menuId); @@ -166,7 +166,7 @@ } // Validate menu existence - function validateMenuExistence (menuId) { + function validateMenuExistence(menuId) { if (!(menuId && menuId.length)) { throw new Error('MenuId was not provided'); } diff --git a/modules/core/tests/client/interceptors/auth-interceptor.client.tests.js b/modules/core/tests/client/interceptors/auth-interceptor.client.tests.js index 0d877b61..ba6ae0a3 100644 --- a/modules/core/tests/client/interceptors/auth-interceptor.client.tests.js +++ b/modules/core/tests/client/interceptors/auth-interceptor.client.tests.js @@ -1,7 +1,7 @@ 'use strict'; -(function() { - describe('authInterceptor', function() { +(function () { + describe('authInterceptor', function () { // Initialize global variables var authInterceptor, $q, @@ -13,11 +13,11 @@ beforeEach(module(ApplicationConfiguration.applicationModuleName)); // Load httpProvider - beforeEach(module(function($httpProvider) { + beforeEach(module(function ($httpProvider) { httpProvider = $httpProvider; })); - beforeEach(inject(function(_authInterceptor_, _$q_, _$state_, _Authentication_) { + beforeEach(inject(function (_authInterceptor_, _$q_, _$state_, _Authentication_) { authInterceptor = _authInterceptor_; $q = _$q_; $state = _$state_; @@ -26,19 +26,19 @@ spyOn($state, 'transitionTo'); })); - it('Auth Interceptor should be object', function() { + it('Auth Interceptor should be object', function () { expect(typeof authInterceptor).toEqual('object'); }); - it('Auth Interceptor should contain responseError function', function() { + it('Auth Interceptor should contain responseError function', function () { expect(typeof authInterceptor.responseError).toEqual('function'); }); - it('httpProvider Interceptor should have authInterceptor', function() { + it('httpProvider Interceptor should have authInterceptor', function () { expect(httpProvider.interceptors).toContain('authInterceptor'); }); - describe('Forbidden Interceptor', function() { + describe('Forbidden Interceptor', function () { it('should redirect to forbidden route', function () { var response = { status: 403, @@ -50,7 +50,7 @@ }); }); - describe('Authorization Interceptor', function() { + describe('Authorization Interceptor', function () { it('should redirect to signIn page for unauthorized access', function () { var response = { status: 401, @@ -63,9 +63,9 @@ }); }); - describe('Unresponsive Interceptor', function() { + describe('Unresponsive Interceptor', function () { var Notification; - beforeEach(inject(function(_Notification_) { + beforeEach(inject(function (_Notification_) { Notification = _Notification_; spyOn(Notification, 'error'); })); diff --git a/modules/core/tests/client/menus.client.service.tests.js b/modules/core/tests/client/menus.client.service.tests.js index 11773539..4e253ebc 100644 --- a/modules/core/tests/client/menus.client.service.tests.js +++ b/modules/core/tests/client/menus.client.service.tests.js @@ -1,7 +1,7 @@ 'use strict'; -(function() { - describe('Menus', function() { +(function () { + describe('Menus', function () { // Initialize global variables var scope, menuService; @@ -9,78 +9,78 @@ // Load the main application module beforeEach(module(ApplicationConfiguration.applicationModuleName)); - beforeEach(inject(function(_menuService_) { + beforeEach(inject(function (_menuService_) { menuService = _menuService_; })); - it('should have topbar added', function() { + it('should have topbar added', function () { expect(menuService.menus.topbar).toBeDefined(); }); - it('should have default roles to user and admin', function() { + it('should have default roles to user and admin', function () { expect(menuService.defaultRoles).toEqual(['user', 'admin']); }); - describe('addMenu', function() { - describe('with no options', function() { + describe('addMenu', function () { + describe('with no options', function () { var menuId = 'menu1', menu; - beforeEach(function() { + beforeEach(function () { menu = menuService.addMenu(menuId); }); - it('should return menu object', function() { + it('should return menu object', function () { expect(menu).toBeDefined(); }); - it('should default roles', function() { + it('should default roles', function () { expect(menu.roles).toEqual(menuService.defaultRoles); }); - it('should have empty items', function() { + it('should have empty items', function () { expect(menu.items).toEqual([]); }); - it('should set shouldRender to shouldRender function handle', function() { + it('should set shouldRender to shouldRender function handle', function () { expect(menu.shouldRender()).toBeFalsy(); }); }); - describe('with options', function() { + describe('with options', function () { var menu, options = { roles: ['a', 'b', 'c'], items: ['d', 'e', 'f'] }; - beforeEach(function() { + beforeEach(function () { menu = menuService.addMenu('menu1', options); }); - it('should set items to options.items list', function() { + it('should set items to options.items list', function () { expect(menu.items).toBe(options.items); }); - it('should set roles to options.roles list', function() { + it('should set roles to options.roles list', function () { expect(menu.roles).toBe(options.roles); }); }); }); - describe('shouldRender', function() { + describe('shouldRender', function () { var menuOptions = { roles: ['*', 'menurole'] }, menu; - beforeEach(function() { + beforeEach(function () { menu = menuService.addMenu('menu1', menuOptions); }); - describe('when logged out', function() { - it('should render if menu is public', function() { + describe('when logged out', function () { + it('should render if menu is public', function () { expect(menu.shouldRender()).toBeTruthy(); }); - it('should not render if menu is private', function() { + it('should not render if menu is private', function () { menu = menuService.addMenu('menu1', { isPublic: false }); @@ -88,28 +88,28 @@ }); }); - describe('when logged in', function() { + describe('when logged in', function () { var user = { roles: ['1', 'menurole', '2'] }; - describe('menu with * role', function() { - it('should render', function() { + describe('menu with * role', function () { + it('should render', function () { expect(menu.shouldRender(user)).toBeTruthy(); }); }); - describe('menu without * role', function() { - beforeEach(function() { + describe('menu without * role', function () { + beforeEach(function () { menu = menuService.addMenu('menu1', { roles: ['b', 'menurole', 'c'] }); }); - it('should render if user has same role as menu', function() { + it('should render if user has same role as menu', function () { expect(menu.shouldRender(user)).toBeTruthy(); }); - it('should not render if user has different roles', function() { + it('should not render if user has different roles', function () { user = { roles: ['1', '2', '3'] }; @@ -119,54 +119,54 @@ }); }); - describe('validateMenuExistence', function() { - describe('when menuId not provided', function() { - it('should throw menuId error', function() { + describe('validateMenuExistence', function () { + describe('when menuId not provided', function () { + it('should throw menuId error', function () { expect(menuService.validateMenuExistence).toThrowError('MenuId was not provided'); }); }); - describe('when menu does not exist', function() { - it('should throw no menu error', function() { - var target = function() { + describe('when menu does not exist', function () { + it('should throw no menu error', function () { + var target = function () { menuService.validateMenuExistence('noMenuId'); }; expect(target).toThrowError('Menu does not exist'); }); }); - describe('when menu exists', function() { + describe('when menu exists', function () { var menuId = 'menuId'; - beforeEach(function() { + beforeEach(function () { menuService.menus[menuId] = {}; }); - it('should return truthy', function() { + it('should return truthy', function () { expect(menuService.validateMenuExistence(menuId)).toBeTruthy(); }); }); }); - describe('removeMenu', function() { + describe('removeMenu', function () { var menu = { id: 'menuId' }; - beforeEach(function() { + beforeEach(function () { menuService.menus[menu.id] = menu; menuService.validateMenuExistence = jasmine.createSpy(); menuService.removeMenu(menu.id); }); - it('should remove existing menu from menus', function() { + it('should remove existing menu from menus', function () { expect(menuService.menus).not.toContain(menu.id); }); - it('validates menu existance before removing', function() { + it('validates menu existance before removing', function () { expect(menuService.validateMenuExistence).toHaveBeenCalledWith(menu.id); }); }); - describe('addMenuItem', function() { + describe('addMenuItem', function () { var menuId = 'menu1', subMenuItem1 = { title: 'sub1' @@ -187,7 +187,7 @@ menu, menuItem; - beforeEach(function() { + beforeEach(function () { menuService.validateMenuExistence = jasmine.createSpy(); menuService.addSubMenuItem = jasmine.createSpy(); menuService.addMenu(menuId, { @@ -197,84 +197,84 @@ menuItem = menu.items[0]; }); - it('should validate menu existance', function() { + it('should validate menu existance', function () { expect(menuService.validateMenuExistence).toHaveBeenCalledWith(menuId); }); - it('should return the menu', function() { + it('should return the menu', function () { expect(menu).toBeDefined(); }); - it('should set menu item shouldRender function', function() { + it('should set menu item shouldRender function', function () { expect(menuItem.shouldRender).toBeDefined(); }); - describe('with options set', function() { - it('should add menu item to menu', function() { + describe('with options set', function () { + it('should add menu item to menu', function () { expect(menu.items.length).toBe(1); }); - it('should set menu item title to options title', function() { + it('should set menu item title to options title', function () { expect(menuItem.title).toBe(menuItemOptions.title); }); - it('should set menu item state to options state', function() { + it('should set menu item state to options state', function () { expect(menuItem.state).toBe(menuItemOptions.state); }); - it('should set menu item type to options type', function() { + it('should set menu item type to options type', function () { expect(menuItem.type).toBe(menuItemOptions.type); }); - it('should set menu item class to options class', function() { + it('should set menu item class to options class', function () { expect(menuItem.class).toBe(menuItemOptions.class); }); - it('should set menu item position to options position', function() { + it('should set menu item position to options position', function () { expect(menuItem.position).toBe(menuItemOptions.position); }); - it('should call addSubMenuItem for each item in options', function() { + it('should call addSubMenuItem for each item in options', function () { expect(menuService.addSubMenuItem).toHaveBeenCalledWith(menuId, menuItemOptions.state, subMenuItem1); expect(menuService.addSubMenuItem).toHaveBeenCalledWith(menuId, menuItemOptions.state, subMenuItem2); }); }); - describe('without options set', function() { - beforeEach(function() { + describe('without options set', function () { + beforeEach(function () { menu = menuService.addMenuItem(menuId); menuItem = menu.items[1]; }); - it('should set menu item type to item', function() { + it('should set menu item type to item', function () { expect(menuItem.type).toBe('item'); }); - it('should set menu item title to empty', function() { + it('should set menu item title to empty', function () { expect(menuItem.title).toBe(''); }); - it('should set menu item isPublic to false', function() { + it('should set menu item isPublic to false', function () { expect(menuItem.isPublic).toBeFalsy(); }); - it('should set menu item roles to default roles', function() { + it('should set menu item roles to default roles', function () { expect(menuItem.roles).toEqual(menuService.defaultRoles); }); - it('should set menu item position to 0', function() { + it('should set menu item position to 0', function () { expect(menuItem.position).toBe(0); }); }); }); - describe('removeMenuItem', function() { + describe('removeMenuItem', function () { var menuId = 'menuId', menuItemState = 'menu.state1', menuItemState2 = 'menu.state2', menu; - beforeEach(function() { + beforeEach(function () { menuService.addMenu(menuId); menuService.addMenuItem(menuId, { state: menuItemState }); menuService.addMenuItem(menuId, { state: menuItemState2 }); @@ -282,21 +282,21 @@ menu = menuService.removeMenuItem(menuId, menuItemState); }); - it('should return menu object', function() { + it('should return menu object', function () { expect(menu).not.toBeNull(); }); - it('should validate menu existance', function() { + it('should validate menu existance', function () { expect(menuService.validateMenuExistence).toHaveBeenCalledWith(menuId); }); - it('should remove sub menu items with same state', function() { + it('should remove sub menu items with same state', function () { expect(menu.items.length).toBe(1); expect(menu.items[0].state).toBe(menuItemState2); }); }); - describe('addSubMenuItem', function() { + describe('addSubMenuItem', function () { var subItemOptions = { title: 'title', state: 'sub.state', @@ -324,7 +324,7 @@ subItem2, menu; - beforeEach(function() { + beforeEach(function () { menuService.validateMenuExistence = jasmine.createSpy(); menuService.addMenu(menuId); menuService.addMenuItem(menuId, menuItem1Options); @@ -339,93 +339,93 @@ subItem2 = menuItem1.items[1]; }); - afterEach(function() { + afterEach(function () { menuService.removeMenu(menuId); }); - it('should return menu object', function() { + it('should return menu object', function () { expect(menu).not.toBeNull(); }); - it('should validate menu existance', function() { + it('should validate menu existance', function () { expect(menuService.validateMenuExistence).toHaveBeenCalledWith(menuId); }); - it('should not add sub menu item to menu item of different state', function() { + it('should not add sub menu item to menu item of different state', function () { expect(menuItem3.items.length).toBe(0); }); - it('should set shouldRender', function() { + it('should set shouldRender', function () { expect(subItem1.shouldRender).toBeDefined(); }); - describe('with options set', function() { - it('should add sub menu item to menu item', function() { + describe('with options set', function () { + it('should add sub menu item to menu item', function () { expect(subItem1).toBeDefined(); }); - it('should set title to options title', function() { + it('should set title to options title', function () { expect(subItem1.title).toBe(subItemOptions.title); }); - it('should set state to options state', function() { + it('should set state to options state', function () { expect(subItem1.state).toBe(subItemOptions.state); }); - it('should set roles to options roles', function() { + it('should set roles to options roles', function () { expect(subItem1.roles).toEqual(subItemOptions.roles); }); - it('should set position to options position', function() { + it('should set position to options position', function () { expect(subItem1.position).toEqual(subItemOptions.position); }); - it('should set params to options params', function() { + it('should set params to options params', function () { expect(subItem1.params).toEqual(subItemOptions.params); }); }); - describe('without optoins set', function() { - it('should add sub menu item to menu item', function() { + describe('without optoins set', function () { + it('should add sub menu item to menu item', function () { expect(subItem2).toBeDefined(); }); - it('should set isPublic to parent isPublic', function() { + it('should set isPublic to parent isPublic', function () { expect(subItem2.isPublic).toBe(menuItem1.isPublic); }); - it('should set title to blank', function() { + it('should set title to blank', function () { expect(subItem2.title).toBe(''); }); - it('should set state to blank', function() { + it('should set state to blank', function () { expect(subItem2.state).toBe(''); }); - it('should set roles to parent roles', function() { + it('should set roles to parent roles', function () { expect(subItem2.roles).toEqual(menuItem1.roles); }); - it('should set position to 0', function() { + it('should set position to 0', function () { expect(subItem2.position).toBe(0); }); }); - describe('then removeSubMenuItem', function() { - beforeEach(function() { + describe('then removeSubMenuItem', function () { + beforeEach(function () { menuService.validateMenuExistence = jasmine.createSpy(); menu = menuService.removeSubMenuItem(menuId, subItem1.state); }); - it('should validate menu existance', function() { + it('should validate menu existance', function () { expect(menuService.validateMenuExistence).toHaveBeenCalledWith(menuId); }); - it('should return menu object', function() { + it('should return menu object', function () { expect(menu).toBeDefined(); }); - it('should remove sub menu item', function() { + it('should remove sub menu item', function () { expect(menuItem1.items.length).toBe(1); expect(menuItem1.items[0].state).toEqual(subItem2.state); }); diff --git a/modules/core/tests/client/socket.io.client.service.tests.js b/modules/core/tests/client/socket.io.client.service.tests.js index 02def5d0..802b42ff 100644 --- a/modules/core/tests/client/socket.io.client.service.tests.js +++ b/modules/core/tests/client/socket.io.client.service.tests.js @@ -1,4 +1,4 @@ -(function() { +(function () { 'use strict'; /* Creates a mock of socket.io for the browser. diff --git a/modules/core/tests/server/core.server.config.tests.js b/modules/core/tests/server/core.server.config.tests.js index f5e83043..3509906c 100644 --- a/modules/core/tests/server/core.server.config.tests.js +++ b/modules/core/tests/server/core.server.config.tests.js @@ -823,7 +823,7 @@ describe('Configuration Tests:', function () { describe('Testing exposing environment as a variable to layout', function () { - ['development', 'production', 'test'].forEach(function(env) { + ['development', 'production', 'test'].forEach(function (env) { it('should expose environment set to ' + env, function (done) { // Set env to development for this test process.env.NODE_ENV = env; diff --git a/modules/users/server/controllers/users/users.profile.server.controller.js b/modules/users/server/controllers/users/users.profile.server.controller.js index 55bd8797..4e7962dd 100644 --- a/modules/users/server/controllers/users/users.profile.server.controller.js +++ b/modules/users/server/controllers/users/users.profile.server.controller.js @@ -81,7 +81,7 @@ exports.changeProfilePicture = function (req, res) { }); } - function uploadImage () { + function uploadImage() { return new Promise(function (resolve, reject) { upload(req, res, function (uploadError) { if (uploadError) { @@ -93,7 +93,7 @@ exports.changeProfilePicture = function (req, res) { }); } - function updateUser () { + function updateUser() { return new Promise(function (resolve, reject) { user.profileImageURL = config.uploads.profile.image.dest + req.file.filename; user.save(function (err, theuser) { @@ -106,7 +106,7 @@ exports.changeProfilePicture = function (req, res) { }); } - function deleteOldImage () { + function deleteOldImage() { return new Promise(function (resolve, reject) { if (existingImageUrl !== User.schema.path('profileImageURL').defaultValue) { fs.unlink(existingImageUrl, function (unlinkError) { @@ -133,7 +133,7 @@ exports.changeProfilePicture = function (req, res) { }); } - function login () { + function login() { return new Promise(function (resolve, reject) { req.login(user, function (err) { if (err) { diff --git a/modules/users/server/models/user.server.model.js b/modules/users/server/models/user.server.model.js index 940085c4..1874ffc2 100644 --- a/modules/users/server/models/user.server.model.js +++ b/modules/users/server/models/user.server.model.js @@ -40,7 +40,7 @@ var validateLocalStrategyEmail = function (email) { * - not begin or end with "." */ -var validateUsername = function(username) { +var validateUsername = function (username) { var usernameRegex = /^(?=[\w.-]+$)(?!.*[._-]{2})(?!\.)(?!.*\.$).{3,34}$/; return ( this.provider !== 'local' || diff --git a/modules/users/tests/client/password-validator.client.directive.tests.js b/modules/users/tests/client/password-validator.client.directive.tests.js index c6f31834..8ab12315 100644 --- a/modules/users/tests/client/password-validator.client.directive.tests.js +++ b/modules/users/tests/client/password-validator.client.directive.tests.js @@ -1,8 +1,8 @@ 'use strict'; -(function() { +(function () { // Password Validator Directive Spec - describe('PasswordValidatorDirective', function() { + describe('PasswordValidatorDirective', function () { // Initialize global variables var scope, element, @@ -12,7 +12,7 @@ // Load the main application module beforeEach(module(ApplicationConfiguration.applicationModuleName)); - beforeEach(inject(function(_$rootScope_, _$compile_) { + beforeEach(inject(function (_$rootScope_, _$compile_) { // Set a new global scope scope = _$rootScope_.$new(); $compile = _$compile_; @@ -30,7 +30,7 @@ // inject allows you to use AngularJS dependency injection // to retrieve and use other services - inject(function($compile) { + inject(function ($compile) { var form = $compile(template)(scope); element = form.find('div'); @@ -39,7 +39,7 @@ }); } - describe('Initialize', function() { + describe('Initialize', function () { beforeEach(function () { compileDirective(); }); @@ -65,7 +65,7 @@ expect(scope.requirementsProgress).toEqual(undefined); }); - it('should be valid when password meets requirements - "P@ssw0rd!!""', function() { + it('should be valid when password meets requirements - "P@ssw0rd!!""', function () { scope.passwordMock.password = 'P@ssw0rd!!'; compileDirective(); scope.$digest(); @@ -76,7 +76,7 @@ expect(scope.requirementsProgress).toEqual('100'); }); - it('should be valid when password meets requirements with a passphrase', function() { + it('should be valid when password meets requirements with a passphrase', function () { scope.passwordMock.password = 'Open-Source Full-Stack Solution for MEAN'; compileDirective(); scope.$digest(); @@ -87,7 +87,7 @@ expect(scope.requirementsProgress).toEqual('100'); }); - it('should not allow a less than 10 characters long - "P@$$w0rd!"', function() { + it('should not allow a less than 10 characters long - "P@$$w0rd!"', function () { scope.passwordMock.password = 'P@$$w0rd!'; compileDirective(); scope.$digest(); @@ -99,7 +99,7 @@ expect(scope.requirementsProgress).toEqual('80'); }); - it('should not allow a greater than 128 characters long', function() { + it('should not allow a greater than 128 characters long', function () { scope.passwordMock.password = ')!/uLT="lh&:`6X!]|15o!$!TJf,.13l?vG].-j],lFPe/QhwN#{Z<[*1nX@n1^?WW-%_.*D)m$toB+N7z}kcN#B_d(f41h%w@0F!]igtSQ1gl~6sEV&r~}~1ub>If1c+'; compileDirective(); scope.$digest(); @@ -111,7 +111,7 @@ expect(scope.requirementsProgress).toEqual('80'); }); - it('should not allow more than 3 or more repeating characters - "P@$$w0rd!!!"', function() { + it('should not allow more than 3 or more repeating characters - "P@$$w0rd!!!"', function () { scope.passwordMock.password = 'P@$$w0rd!!!'; compileDirective(); scope.$digest(); @@ -123,7 +123,7 @@ expect(scope.requirementsProgress).toEqual('80'); }); - it('should not allow a password with no uppercase letters - "p@$$w0rd!!"', function() { + it('should not allow a password with no uppercase letters - "p@$$w0rd!!"', function () { scope.passwordMock.password = 'p@$$w0rd!!'; compileDirective(); scope.$digest(); @@ -135,7 +135,7 @@ expect(scope.requirementsProgress).toEqual('80'); }); - it('should not allow a password with less than one number - "P@$$word!!"', function() { + it('should not allow a password with less than one number - "P@$$word!!"', function () { scope.passwordMock.password = 'P@$$word!!'; compileDirective(); scope.$digest(); @@ -147,7 +147,7 @@ expect(scope.requirementsProgress).toEqual('80'); }); - it('should not allow a password with less than one special character - "Passw0rdss"', function() { + it('should not allow a password with less than one special character - "Passw0rdss"', function () { scope.passwordMock.password = 'Passw0rdss'; compileDirective(); scope.$digest(); @@ -159,7 +159,7 @@ expect(scope.requirementsProgress).toEqual('80'); }); - it('should show 20% progress and "danger" color', function() { + it('should show 20% progress and "danger" color', function () { scope.passwordMock.password = 'P'; compileDirective(); scope.$digest(); @@ -168,7 +168,7 @@ expect(scope.requirementsProgress).toEqual('20'); }); - it('should show 40% progress and "warning" color', function() { + it('should show 40% progress and "warning" color', function () { scope.passwordMock.password = 'Pa'; compileDirective(); scope.$digest(); @@ -177,7 +177,7 @@ expect(scope.requirementsProgress).toEqual('40'); }); - it('should show 60% progress and "info" color', function() { + it('should show 60% progress and "info" color', function () { scope.passwordMock.password = 'Pa$'; compileDirective(); scope.$digest(); @@ -186,7 +186,7 @@ expect(scope.requirementsProgress).toEqual('60'); }); - it('should show 80% progress and "primary" color', function() { + it('should show 80% progress and "primary" color', function () { scope.passwordMock.password = 'Pa$$w0rd'; compileDirective(); scope.$digest(); @@ -195,7 +195,7 @@ expect(scope.requirementsProgress).toEqual('80'); }); - it('should show 100% progress and "success" color', function() { + it('should show 100% progress and "success" color', function () { scope.passwordMock.password = 'Pa$$w0rd!!'; compileDirective(); scope.$digest(); diff --git a/modules/users/tests/client/password-verify.client.directive.tests.js b/modules/users/tests/client/password-verify.client.directive.tests.js index 24b41dcd..29b35bcd 100644 --- a/modules/users/tests/client/password-verify.client.directive.tests.js +++ b/modules/users/tests/client/password-verify.client.directive.tests.js @@ -1,8 +1,8 @@ 'use strict'; -(function() { +(function () { // Password Verify Directive Spec - describe('PasswordVerifyDirective', function() { + describe('PasswordVerifyDirective', function () { // Initialize global variables var scope, element, @@ -12,7 +12,7 @@ // Load the main application module beforeEach(module(ApplicationConfiguration.applicationModuleName)); - beforeEach(inject(function(_$rootScope_, _$compile_) { + beforeEach(inject(function (_$rootScope_, _$compile_) { // Set a new global scope scope = _$rootScope_.$new(); $compile = _$compile_; @@ -32,7 +32,7 @@ // inject allows you to use AngularJS dependency injection // to retrieve and use other services - inject(function($compile) { + inject(function ($compile) { var form = $compile(template)(scope); element = form.find('div'); @@ -41,7 +41,7 @@ }); } - describe('Initialize', function() { + describe('Initialize', function () { beforeEach(function () { compileDirective(); }); diff --git a/modules/users/tests/client/password.client.controller.tests.js b/modules/users/tests/client/password.client.controller.tests.js index 3bb87cc5..43d855a1 100644 --- a/modules/users/tests/client/password.client.controller.tests.js +++ b/modules/users/tests/client/password.client.controller.tests.js @@ -1,8 +1,8 @@ 'use strict'; -(function() { +(function () { // Password controller Spec - describe('PasswordController', function() { + describe('PasswordController', function () { // Initialize global variables var PasswordController, scope, @@ -12,11 +12,11 @@ $window, Notification; - beforeEach(function() { + beforeEach(function () { jasmine.addMatchers({ - toEqualData: function(util, customEqualityTesters) { + toEqualData: function (util, customEqualityTesters) { return { - compare: function(actual, expected) { + compare: function (actual, expected) { return { pass: angular.equals(actual, expected) }; @@ -29,8 +29,8 @@ // Load the main application module beforeEach(module(ApplicationConfiguration.applicationModuleName)); - describe('Logged in user', function() { - beforeEach(inject(function($controller, $rootScope, _UsersService_, _Authentication_, _$stateParams_, _$httpBackend_, _$location_) { + describe('Logged in user', function () { + beforeEach(inject(function ($controller, $rootScope, _UsersService_, _Authentication_, _$stateParams_, _$httpBackend_, _$location_) { // Set a new global scope scope = $rootScope.$new(); @@ -55,13 +55,13 @@ }); })); - it('should redirect logged in user to home', function() { + it('should redirect logged in user to home', function () { expect($location.path).toHaveBeenCalledWith('/'); }); }); - describe('Logged out user', function() { - beforeEach(inject(function($controller, $rootScope, _$window_, _$stateParams_, _$httpBackend_, _$location_, _Notification_) { + describe('Logged out user', function () { + beforeEach(inject(function ($controller, $rootScope, _$window_, _$stateParams_, _$httpBackend_, _$location_, _Notification_) { // Set a new global scope scope = $rootScope.$new(); @@ -87,22 +87,22 @@ }); })); - it('should not redirect to home', function() { + it('should not redirect to home', function () { expect($location.path).not.toHaveBeenCalledWith('/'); }); - describe('askForPasswordReset', function() { + describe('askForPasswordReset', function () { var credentials = { username: 'test', password: 'P@ssw0rd!!' }; - beforeEach(function() { + beforeEach(function () { scope.vm.credentials = credentials; }); - describe('POST error', function() { + describe('POST error', function () { var errorMessage = 'No account with that username has been found'; - beforeEach(function() { + beforeEach(function () { $httpBackend.when('POST', '/api/auth/forgot', credentials).respond(400, { 'message': errorMessage }); @@ -111,18 +111,18 @@ $httpBackend.flush(); }); - it('should clear form', function() { + it('should clear form', function () { expect(scope.vm.credentials).toBe(null); }); - it('should call Notification.error with response message', function() { + it('should call Notification.error with response message', function () { expect(Notification.error).toHaveBeenCalledWith({ message: errorMessage, title: ' Failed to send password reset email!', delay: 4000 }); }); }); - describe('POST success', function() { + describe('POST success', function () { var successMessage = 'An email has been sent to the provided email with further instructions.'; - beforeEach(function() { + beforeEach(function () { $httpBackend.when('POST', '/api/auth/forgot', credentials).respond({ 'message': successMessage }); @@ -131,27 +131,27 @@ $httpBackend.flush(); }); - it('should clear form', function() { + it('should clear form', function () { expect(scope.vm.credentials).toBe(null); }); - it('should call Notification.success with response message', function() { + it('should call Notification.success with response message', function () { expect(Notification.success).toHaveBeenCalledWith({ message: successMessage, title: ' Password reset email sent successfully!' }); }); }); }); - describe('resetUserPassword', function() { + describe('resetUserPassword', function () { var token = 'testToken'; var passwordDetails = { password: 'test' }; - beforeEach(function() { + beforeEach(function () { $stateParams.token = token; scope.vm.passwordDetails = passwordDetails; }); - it('POST error should call Notification.error with response message', function() { + it('POST error should call Notification.error with response message', function () { var errorMessage = 'Passwords do not match'; $httpBackend.when('POST', '/api/auth/reset/' + token, passwordDetails).respond(400, { 'message': errorMessage @@ -163,26 +163,26 @@ expect(Notification.error).toHaveBeenCalledWith({ message: errorMessage, title: ' Password reset failed!', delay: 4000 }); }); - describe('POST success', function() { + describe('POST success', function () { var user = { username: 'test' }; - beforeEach(function() { + beforeEach(function () { $httpBackend.when('POST', '/api/auth/reset/' + token, passwordDetails).respond(user); scope.vm.resetUserPassword(true); $httpBackend.flush(); }); - it('should clear password form', function() { + it('should clear password form', function () { expect(scope.vm.passwordDetails).toBe(null); }); - it('should attach user profile', function() { + it('should attach user profile', function () { expect(scope.vm.authentication.user.username).toEqual(user.username); }); - it('should redirect to password reset success view with Notification.success', function() { + it('should redirect to password reset success view with Notification.success', function () { expect(Notification.success).toHaveBeenCalledWith({ message: ' Password reset successful!' }); expect($location.path).toHaveBeenCalledWith('/password/reset/success'); }); diff --git a/modules/users/tests/e2e/users.e2e.tests.js b/modules/users/tests/e2e/users.e2e.tests.js index 02d27ede..05141305 100644 --- a/modules/users/tests/e2e/users.e2e.tests.js +++ b/modules/users/tests/e2e/users.e2e.tests.js @@ -421,7 +421,7 @@ describe('Users E2E Tests:', function () { expect(element.all(by.css('.error-text')).get(1).getText()).toBe('Password is required.'); }); - it('Verify that the user is logged in', function() { + it('Verify that the user is logged in', function () { // Make sure user is signed out first signout(); // Sign in diff --git a/modules/users/tests/server/user.server.model.tests.js b/modules/users/tests/server/user.server.model.tests.js index bac9e8b2..43c8168e 100644 --- a/modules/users/tests/server/user.server.model.tests.js +++ b/modules/users/tests/server/user.server.model.tests.js @@ -186,7 +186,7 @@ describe('User Model Unit Tests:', function () { _user3.email = _user1.email; _user3.save(function (err) { should.exist(err); - _user1.remove(function(err) { + _user1.remove(function (err) { should.not.exist(err); done(); }); @@ -224,7 +224,7 @@ describe('User Model Unit Tests:', function () { _user1.save(function (err) { should.not.exist(err); _user1.password.should.not.equal(passwordBeforeSave); - _user1.remove(function(err) { + _user1.remove(function (err) { should.not.exist(err); done(); }); @@ -238,7 +238,7 @@ describe('User Model Unit Tests:', function () { _user1.save(function (err) { should.not.exist(err); _user1.password.should.not.equal(passwordBeforeSave); - _user1.remove(function(err) { + _user1.remove(function (err) { should.not.exist(err); done(); }); @@ -246,7 +246,7 @@ describe('User Model Unit Tests:', function () { }); }); - describe('User Password Validation Tests', function() { + describe('User Password Validation Tests', function () { it('should validate when the password strength passes - "P@$$w0rd!!"', function () { var _user1 = new User(user1); _user1.password = 'P@$$w0rd!!'; @@ -351,7 +351,7 @@ describe('User Model Unit Tests:', function () { }); }); - describe('User E-mail Validation Tests', function() { + describe('User E-mail Validation Tests', function () { it('should not allow invalid email address - "123"', function (done) { var _user1 = new User(user1); @@ -645,12 +645,12 @@ describe('User Model Unit Tests:', function () { }); - describe('Username Validation', function() { - it('should show error to save username beginning with .', function(done) { + describe('Username Validation', function () { + it('should show error to save username beginning with .', function (done) { var _user = new User(user1); _user.username = '.login'; - _user.save(function(err) { + _user.save(function (err) { should.exist(err); done(); }); @@ -660,67 +660,67 @@ describe('User Model Unit Tests:', function () { var _user = new User(user1); _user.username = config.illegalUsernames[Math.floor(Math.random() * config.illegalUsernames.length)]; - _user.save(function(err) { + _user.save(function (err) { should.exist(err); done(); }); }); - it('should show error to save username end with .', function(done) { + it('should show error to save username end with .', function (done) { var _user = new User(user1); _user.username = 'login.'; - _user.save(function(err) { + _user.save(function (err) { should.exist(err); done(); }); }); - it('should show error to save username with ..', function(done) { + it('should show error to save username with ..', function (done) { var _user = new User(user1); _user.username = 'log..in'; - _user.save(function(err) { + _user.save(function (err) { should.exist(err); done(); }); }); - it('should show error to save username shorter than 3 character', function(done) { + it('should show error to save username shorter than 3 character', function (done) { var _user = new User(user1); _user.username = 'lo'; - _user.save(function(err) { + _user.save(function (err) { should.exist(err); done(); }); }); - it('should show error saving a username without at least one alphanumeric character', function(done) { + it('should show error saving a username without at least one alphanumeric character', function (done) { var _user = new User(user1); _user.username = '-_-'; - _user.save(function(err) { + _user.save(function (err) { should.exist(err); done(); }); }); - it('should show error saving a username longer than 34 characters', function(done) { + it('should show error saving a username longer than 34 characters', function (done) { var _user = new User(user1); _user.username = 'l'.repeat(35); - _user.save(function(err) { + _user.save(function (err) { should.exist(err); done(); }); }); - it('should save username with dot', function(done) { + it('should save username with dot', function (done) { var _user = new User(user1); _user.username = 'log.in'; - _user.save(function(err) { + _user.save(function (err) { should.not.exist(err); done(); }); diff --git a/modules/users/tests/server/user.server.routes.tests.js b/modules/users/tests/server/user.server.routes.tests.js index 0c87471e..6142c966 100644 --- a/modules/users/tests/server/user.server.routes.tests.js +++ b/modules/users/tests/server/user.server.routes.tests.js @@ -427,7 +427,7 @@ describe('User CRUD tests', function () { return done(err); } - User.findOne({ username: user.username.toLowerCase() }, function(err, userRes) { + User.findOne({ username: user.username.toLowerCase() }, function (err, userRes) { userRes.resetPasswordToken.should.not.be.empty(); should.exist(userRes.resetPasswordExpires); res.body.message.should.be.equal('Failure sending email'); @@ -453,7 +453,7 @@ describe('User CRUD tests', function () { return done(err); } - User.findOne({ username: user.username.toLowerCase() }, function(err, userRes) { + User.findOne({ username: user.username.toLowerCase() }, function (err, userRes) { userRes.resetPasswordToken.should.not.be.empty(); should.exist(userRes.resetPasswordExpires); res.body.message.should.be.equal('Failure sending email'); @@ -479,7 +479,7 @@ describe('User CRUD tests', function () { return done(err); } - User.findOne({ username: user.username.toLowerCase() }, function(err, userRes) { + User.findOne({ username: user.username.toLowerCase() }, function (err, userRes) { userRes.resetPasswordToken.should.not.be.empty(); should.exist(userRes.resetPasswordExpires); @@ -1083,7 +1083,7 @@ describe('User CRUD tests', function () { user.profileImageURL = config.uploads.profile.image.dest + 'non-existing.png'; - user.save(function(saveErr) { + user.save(function (saveErr) { // Handle error if (saveErr) { return done(saveErr); From 9dd0a7b3488d9a7124ea7268936175f1be0d5520 Mon Sep 17 00:00:00 2001 From: Mikael Korpela Date: Tue, 15 Aug 2017 00:06:03 +0300 Subject: [PATCH 13/14] fix(travis): Fix Travis failing on webdriver issues (#1845) --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 6d89aaa6..e794832a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,7 @@ addons: - clang - google-chrome-stable before_install: + - npm install webdriver-manager -g && webdriver-manager update - npm install nsp -g # - npm install snyk -g - 'export PATH=$PATH:/usr/lib/chromium-browser/' From b43c80e2c097b11114f4e4f01b9718321721a89b Mon Sep 17 00:00:00 2001 From: Mikael Korpela Date: Tue, 15 Aug 2017 14:57:32 +0300 Subject: [PATCH 14/14] feat(build): Update dependencies (#1847) Remove dependencies which are already installed via other dependencies: - eslint (gulp-eslint) - istanbul (gulp-eslint) - mocha (gulp-mocha) Remove unused dependency: - gulp-util Move installing `lcov-result-merger` to Travis config since it's the only place where it's ever used. Update a bunch of outdated dependencies. --- .travis.yml | 3 ++- package.json | 31 +++++++++++++------------------ 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index e794832a..15ccf8dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,7 +45,8 @@ after_script: - nsp check # - snyk test - gulp test:coverage - - node_modules/.bin/lcov-result-merger 'coverage/**/lcov.info' | node_modules/coveralls/bin/coveralls.js + - npm install lcov-result-merger@~1.2.0 -g + - lcov-result-merger 'coverage/**/lcov.info' | node_modules/coveralls/bin/coveralls.js notifications: webhooks: urls: diff --git a/package.json b/package.json index 7fa4811d..dcadc2f9 100644 --- a/package.json +++ b/package.json @@ -38,10 +38,10 @@ }, "dependencies": { "acl": "~0.4.10", - "async": "~2.3.0", + "async": "~2.5.0", "body-parser": "~1.17.1", "bower": "~1.8.0", - "chalk": "~2.0.1", + "chalk": "~2.1.0", "compression": "~1.7.0", "connect-flash": "~0.1.1", "connect-mongo": "~1.3.2", @@ -51,12 +51,11 @@ "express-session": "~1.15.2", "generate-password": "~1.3.0", "glob": "~7.1.1", - "helmet": "~3.6.1", - "jasmine-core": "~2.5.2", + "helmet": "~3.8.1", + "jasmine-core": "~2.7.0", "lodash": "~4.17.4", - "lusca": "~1.4.1", + "lusca": "~1.5.1", "method-override": "~2.3.8", - "mocha": "~3.5.0", "mongoose": "~4.11.3", "morgan": "~1.8.1", "multer": "~1.3.0", @@ -79,16 +78,15 @@ "devDependencies": { "coveralls": "~2.13.0", "del": "^3.0.0", - "eslint": "~2.2.0", "eslint-config-airbnb": "~6.0.2", "gulp": "~3.9.1", "gulp-angular-templatecache": "~2.0.0", - "gulp-autoprefixer": "~3.1.0", + "gulp-autoprefixer": "~4.0.0", "gulp-concat": "~2.6.0", "gulp-csslint": "~1.0.0", "gulp-csso": "~3.0.0", "gulp-eslint": "~3.0.1", - "gulp-imagemin": "~3.2.0", + "gulp-imagemin": "~3.3.0", "gulp-istanbul": "~1.1.1", "gulp-less": "~3.3.0", "gulp-load-plugins": "~1.5.0", @@ -98,21 +96,18 @@ "gulp-protractor": "^4.0.0", "gulp-refresh": "~1.1.0", "gulp-rename": "~1.2.2", - "gulp-rev": "^7.1.2", + "gulp-rev": "~8.0.0", "gulp-sass": "~3.1.0", - "gulp-uglify": "~2.1.2", - "gulp-util": "~3.0.7", + "gulp-uglify": "~3.0.0", "imagemin-pngquant": "~5.0.0", - "istanbul": "~0.4.2", - "karma": "~1.6.0", - "karma-chrome-launcher": "~2.0.0", + "karma": "~1.7.0", + "karma-chrome-launcher": "~2.2.0", "karma-coverage": "~1.1.1", "karma-jasmine": "~1.1.0", "karma-mocha-reporter": "~2.2.3", "karma-ng-html2js-preprocessor": "~1.0.0", - "lcov-result-merger": "~1.2.0", - "run-sequence": "~2.0.0", - "semver": "~5.3.0", + "run-sequence": "~2.1.0", + "semver": "~5.4.1", "should": "~11.2.1", "supertest": "~3.0.0" }