diff --git a/specs/endToEnd/apis/locale.js b/specs/endToEnd/apis/locale.js index 23f844e..531c4b2 100644 --- a/specs/endToEnd/apis/locale.js +++ b/specs/endToEnd/apis/locale.js @@ -1,11 +1,11 @@ import chai from 'chai'; -import request from 'superagent'; +import { apiEngine } from '../../utils'; +import localeAPI from '../../../build/common/api/locale'; import async from 'async'; -import constants from '../../constants'; import Errors from '../../../build/common/constants/Errors'; let expect = chai.expect; -describe('#locale', () => { +describe('#localeAPI', () => { let validLocales = [ 'en-us', 'zh-tw', @@ -17,42 +17,29 @@ describe('#locale', () => { 'fuck you', ]; - describe('#Unauthorized User', () => { - // GET /api/locale/{validLocaleName} - describe('GET /api/locales/{validLocaleName}', () => { - it('should download valid locale', (done) => { - async.eachSeries(validLocales, (validLocale, cb) => { - request - .get(constants.BASE + '/api/locales/' + validLocale) - .end((err, res) => { - expect(err).to.equal(null); - expect(res).to.not.be.undefined; - expect(res.status).to.equal(200); - expect(res.body.errors).to.be.undefined; - expect(res.body.locale).to.equal(validLocale); - expect(res.body.messages).to.be.an('object'); - cb(); - }); - }, done); - }); + describe('#read()', () => { + it('should download valid locale', (done) => { + async.eachSeries(validLocales, (validLocale, cb) => { + localeAPI(apiEngine) + .read(validLocale) + .then((json) => { + expect(json.locale).to.equal(validLocale); + expect(json.messages).to.be.an('object'); + cb(); + }); + }, done); }); - // GET /api/locale/{invalidLocaleName} - describe('GET /api/locales/{invalidLocaleName}', () => { - it('should reject invalid locale', (done) => { - async.eachSeries(invalidLocales, (invalidLocale, cb) => { - request - .get(constants.BASE + '/api/locales/' + invalidLocale) - .end((err, res) => { - expect(err).to.equal(null); - expect(res).to.not.be.undefined; - expect(res.status).to.equal(200); - expect(res.body.errors[0].code) - .to.equal(Errors.LOCALE_NOT_SUPPORTED.code); - cb(); - }); - }, done); - }); + it('should reject invalid locale', (done) => { + async.eachSeries(invalidLocales, (invalidLocale, cb) => { + localeAPI(apiEngine) + .read(invalidLocale) + .catch((err) => { + expect(err[0].code) + .to.equal(Errors.LOCALE_NOT_SUPPORTED.code); + cb(); + }); + }, done); }); }); }); diff --git a/specs/endToEnd/apis/todo.js b/specs/endToEnd/apis/todo.js index 67c904d..6b1d73f 100644 --- a/specs/endToEnd/apis/todo.js +++ b/specs/endToEnd/apis/todo.js @@ -1,11 +1,11 @@ import chai from 'chai'; -import request from 'superagent'; +import { apiEngine } from '../../utils'; +import todoAPI from '../../../build/common/api/todo'; import async from 'async'; -import constants from '../../constants'; import Todo from '../../../build/server/models/Todo'; let expect = chai.expect; -describe('#todo', () => { +describe('#todoAPI', () => { let fakeTodos = [{ text: 'this is a fake todo text', }, { @@ -13,49 +13,34 @@ describe('#todo', () => { }, { text: '~bar~', }]; - let resTodos = []; before((done) => { Todo.remove({}, done); }); - describe('#Unauthorized User', () => { - // POST /api/todo - describe('POST /api/todos', () => { - it('should create todo', (done) => { - async.eachSeries(fakeTodos, (fakeTodo, cb) => { - request - .post(constants.BASE + '/api/todos') - .send(fakeTodo) - .end((err, res) => { - expect(err).to.equal(null); - expect(res).to.not.be.undefined; - expect(res.status).to.equal(200); - expect(res.body.errors).to.be.undefined; - expect(res.body.todo).to.be.an('object'); - expect(res.body.todo.text).to.equal(fakeTodo.text); - resTodos.push(res.body.todo); - cb(); - }); - }, done); - }); + describe('#create()', () => { + it('should create todo', (done) => { + async.eachSeries(fakeTodos, (fakeTodo, cb) => { + todoAPI(apiEngine) + .create(fakeTodo) + .then((json) => { + expect(json.todo).to.be.an('object'); + expect(json.todo.text).to.equal(fakeTodo.text); + cb(); + }); + }, done); }); + }); - // GET /api/todo - describe('GET /api/todos', () => { - it('should list todos', (done) => { - request - .get(constants.BASE + '/api/todos') - .end((err, res) => { - expect(err).to.equal(null); - expect(res).to.not.be.undefined; - expect(res.status).to.equal(200); - expect(res.body.errors).to.be.undefined; - expect(res.body.todos).to.be.an('array'); - expect(res.body.todos.length).to.equal(fakeTodos.length); - done(); - }); - }); + describe('#list()', () => { + it('should list todos', (done) => { + todoAPI(apiEngine) + .list({ page: 1 }) + .then((json) => { + expect(json.todos).to.be.an('array'); + expect(json.todos).to.have.lengthOf(fakeTodos.length); + done(); + }); }); }); diff --git a/specs/endToEnd/apis/user.js b/specs/endToEnd/apis/user.js index 21cfd2f..b898c2e 100644 --- a/specs/endToEnd/apis/user.js +++ b/specs/endToEnd/apis/user.js @@ -1,152 +1,257 @@ import chai from 'chai'; -import request from 'superagent'; -import constants from '../../constants'; -import User from '../../../build/server/models/User'; +import { apiEngine, clearUsers, prepareUsers } from '../../utils'; +import ApiEngine from '../../../build/common/utils/ApiEngine'; +import userAPI from '../../../build/common/api/user'; import features from '../features'; import Errors from '../../../build/common/constants/Errors'; let expect = chai.expect; -describe('#user', () => { +describe('#userAPI', () => { + let reqs = { + users: [], + admins: [], + }; + let userInstances = { + users: [], + admins: [], + }; let fakeUser; - let resUser; - let validateUser = (user) => { expect(user).to.contain.all.keys(['_id', 'email']); expect(user).to.not.have.any.keys(['password']); }; before((done) => { - User.remove({}, done); + clearUsers(() => prepareUsers(reqs, userInstances, done)); + }); + + describe('#list()', () => { + it('[unauth user] should be rejected', (done) => { + userAPI(apiEngine) + .list({ page: 1 }) + .catch((err) => { + expect(err).to.have.lengthOf(1); + expect(err[0].code) + .to.equal(Errors.USER_UNAUTHORIZED.code); + done(); + }); + }); + it('[normal user] should be rejected', (done) => { + userAPI(new ApiEngine(reqs.users[0])) + .list({ page: 1 }) + .catch((err) => { + expect(err).to.have.lengthOf(1); + expect(err[0].code) + .to.equal(Errors.PERMISSION_DENIED.code); + done(); + }); + }); + it('[admin user] should list all users', (done) => { + userAPI(new ApiEngine(reqs.admins[0])) + .list({ page: 1 }) + .then((json) => { + expect(json).contain.all.keys(['users', 'page']); + done(); + }); + }); + }); + + describe('#register()', () => { + fakeUser = { + name: features.users.users[0].name, + email: features.users.users[0].email.value, + password: features.users.users[0].password, + }; + before((done) => { + clearUsers(done); + }); + it('should create user', (done) => { + userAPI(apiEngine) + .register(fakeUser) + .then((json) => { + validateUser(json.user); + done(); + }); + }); + it('should fail when email is duplicate', (done) => { + userAPI(apiEngine) + .register(fakeUser) + .catch((err) => { + expect(err[0].code) + .to.equal(Errors.USER_EXISTED.code); + done(); + }); + }); + after((done) => { + clearUsers(() => prepareUsers(reqs, userInstances, done)); + }); }); - describe('#Unauthorized User', () => { - // POST /api/user - describe('POST /api/users', () => { + describe('#login()', () => { + it('should auth valid user', (done) => { fakeUser = { - name: features.user[0].name, - email: features.user[0].email.value, - password: features.user[0].password, + email: features.users.users[0].email.value, + password: features.users.users[0].password, }; - it('should create user', (done) => { - request - .post(constants.BASE + '/api/users') - .send(fakeUser) - .end((err, res) => { - expect(err).to.equal(null); - expect(res).to.not.be.undefined; - expect(res.status).to.equal(200); - expect(res.body.errors).to.be.undefined; - validateUser(res.body.user); - resUser = res.body.user; - done(); - }); - }); - it('should fail when email is duplicate', (done) => { - request - .post(constants.BASE + '/api/users') - .send(fakeUser) - .end((err, res) => { - expect(err).to.equal(null); - expect(res).to.not.be.undefined; - expect(res.status).to.equal(200); - expect(res.body.errors[0].code) - .to.equal(Errors.USER_EXISTED.code); - done(); - }); - }); - }); - - // POST /api/user/login - describe('POST /api/users/login', () => { - it('should auth valid user', (done) => { - fakeUser = { - email: features.user[0].email.value, - password: features.user[0].password, - }; - request - .post(constants.BASE + '/api/users/login') - .send(fakeUser) - .end((err, res) => { - expect(err).to.equal(null); - expect(res).to.not.be.undefined; - expect(res.status).to.equal(200); - expect(res.body.errors).to.be.undefined; - expect(res.body.isAuth).to.be.true; - expect(res.body.token).to.be.a('string'); - done(); - }); - }); - it('should reject invalid user', (done) => { - request - .post(constants.BASE + '/api/users/login') - .send({}) - .end((err, res) => { - expect(err).to.equal(null); - expect(res).to.not.be.undefined; - expect(res.status).to.equal(200); - expect(res.body.errors).to.be.undefined; - expect(res.body.isAuth).to.be.false; - done(); - }); - }); - }); - - // GET /api/user/logout - describe('GET /api/user/logout', () => { - it('should unauth user', (done) => { - request - .get(constants.BASE + '/api/users/logout') - .end((err, res) => { - expect(err).to.equal(null); - expect(res).to.not.be.undefined; - expect(res.status).to.equal(200); - expect(res.body.errors).to.be.undefined; - done(); - }); - }); - }); - - // GET /api/user/me - describe('GET /api/users/me', () => { - it('should be rejected', (done) => { - request - .get(constants.BASE + '/api/users/me') - .end((err, res) => { - expect(err).to.equal(null); - expect(res).to.not.be.undefined; - expect(res.status).to.equal(200); - expect(res.body.errors[0].code) - .to.equal(Errors.USER_UNAUTHORIZED.code); - done(); - }); - }); + userAPI(apiEngine) + .login(fakeUser) + .then((json) => { + expect(json.isAuth).to.be.true; + expect(json.token).to.be.a('string'); + done(); + }); + }); + it('should reject invalid user', (done) => { + userAPI(apiEngine) + .login({}) + .then((json) => { + expect(json.isAuth).to.be.false; + done(); + }); }); }); - describe('#Authorized User', () => { - // GET /api/user/me - describe('GET /api/users/me', () => { - it('should show user', (done) => { - User.findOne({}, (err, user) => { - expect(err).to.equal(null); - let token = user.toAuthenticationToken(); - request - .get(constants.BASE + '/api/users/me') - .set('Cookie', 'token=' + token) - .end((err, res) => { - expect(err).to.equal(null); - expect(res).to.not.be.undefined; - expect(res.status).to.equal(200); - expect(res.body.errors).to.be.undefined; - validateUser(res.body.user); - done(); + describe('#logout()', () => { + it('should logout user', (done) => { + userAPI(apiEngine) + .logout({}) + .then((json) => { + expect(json).to.be.empty; + done(); + }); + }); + }); + + describe('#read()', () => { + before((done) => { + clearUsers(() => prepareUsers(reqs, userInstances, done)); + }); + it('[unauth user] should be rejected', (done) => { + userAPI(apiEngine) + .read() + .catch((err) => { + expect(err).to.have.lengthOf(1); + expect(err[0].code) + .to.equal(Errors.USER_UNAUTHORIZED.code); + done(); + }); + }); + it('[normal user] should show user detail', (done) => { + userAPI(new ApiEngine(reqs.users[0])) + .read() + .then((json) => { + validateUser(json.user); + done(); + }); + }); + }); + + describe('#update()', () => { + it('[auth user] should update user detail', (done) => { + let newName = 'foobar'; + userAPI(new ApiEngine(reqs.users[0])) + .update({ + name: newName, + }) + .then((json) => { + expect(json.user.name).to.equal(newName); + done(); + }); + }); + }); + + describe('#updateAvatarURL()', () => { + it('[auth user] should update user avatar', (done) => { + let newAvatarURL = 'http://imgur.com/gallery/A8eQsll'; + + userAPI(new ApiEngine(reqs.users[0])) + .updateAvatarURL({ + avatarURL: newAvatarURL, + }) + .then((json) => { + expect(json.user.avatarURL).to.equal(newAvatarURL); + done(); + }); + }); + }); + + describe('#updatePassword()', () => { + it('[auth user] should be able to change password', (done) => { + let oldPassword = features.users.users[0].password; + let newPassword = 'f&o%obar@#$%'; + + userAPI(new ApiEngine(reqs.users[0])) + .updatePassword({ + oldPassword: oldPassword, + newPassword: newPassword, + newPasswordConfirm: newPassword, + }) + .then((json) => { + expect(json.isAuth).to.be.true; + + return userAPI(apiEngine) + .login({ + email: json.user.email.value, + password: newPassword, }); + }) + .then((json) => { + expect(json.isAuth).to.be.true; + expect(json.token).to.be.a('string'); + done(); + }); + }); + }); + + describe('#verifyEmail()', () => { + it('should be able to verify email', (done) => { + userAPI(apiEngine) + .verifyEmail({ + token: userInstances.users[0].toVerifyEmailToken(), + }) + .then((json) => { + expect(json).to.be.empty; + + return userAPI(new ApiEngine(reqs.users[0])).read(); + }) + .then((json) => { + expect(json.user.email.isVerified).to.be.true; + done(); }); - }); }); }); + describe('#resetPassword()', () => { + it( + '[unauth user] should be able to reset password with a token', + (done) => { + let newPassword = 'SDFGHJGJK'; + + userAPI(apiEngine) + .resetPassword({ + token: userInstances.users[0].toResetPasswordToken(), + newPassword: newPassword, + newPasswordConfirm: newPassword, + }) + .then((json) => { + expect(json.user).to.be.an('object'); + + return userAPI(apiEngine) + .login({ + email: userInstances.users[0].email.value, + password: newPassword, + }); + }) + .then((json) => { + expect(json.isAuth).to.be.true; + expect(json.token).to.be.a('string'); + done(); + }); + } + ); + }); + after((done) => { - User.remove({}, done); + clearUsers(done); }); }); diff --git a/specs/endToEnd/features/index.js b/specs/endToEnd/features/index.js index 3dd1189..18bfa39 100644 --- a/specs/endToEnd/features/index.js +++ b/specs/endToEnd/features/index.js @@ -1,5 +1,5 @@ -import user from './user'; +import users from './users'; export default { - user, + users, }; diff --git a/specs/endToEnd/features/user.js b/specs/endToEnd/features/user.js deleted file mode 100644 index e91e1a2..0000000 --- a/specs/endToEnd/features/user.js +++ /dev/null @@ -1,13 +0,0 @@ -export default [{ - name: 'test1', - email: { - value: 'fakeUser1@gmail.com', - }, - password: 'fake', -}, { - name: 'test2', - email: { - value: 'fakeUser2@gmail.com', - }, - password: 'fake123', -}]; diff --git a/specs/endToEnd/features/users.js b/specs/endToEnd/features/users.js new file mode 100644 index 0000000..628a55d --- /dev/null +++ b/specs/endToEnd/features/users.js @@ -0,0 +1,27 @@ +import Roles from '../../../build/common/constants/Roles'; + +export default { + users: [{ + name: 'test1', + email: { + value: 'fakeUser1@gmail.com', + }, + password: 'fake', + role: Roles.USER, + }, { + name: 'test2', + email: { + value: 'fakeUser2@gmail.com', + }, + password: 'fake123', + role: Roles.USER, + }], + admins: [{ + name: 'admin', + email: { + value: 'admin@gmail.com', + }, + password: 'admin', + role: Roles.ADMIN, + }], +}; diff --git a/specs/endToEnd/index.js b/specs/endToEnd/index.js index a4c4e43..1a60a3c 100644 --- a/specs/endToEnd/index.js +++ b/specs/endToEnd/index.js @@ -4,7 +4,7 @@ let server; before((done) => { appPromise.then((app) => { - console.log('starting server on port', constants.PORT, '...'); + console.log('\nstarting server on port', constants.PORT, '...\n'); server = app.listen(constants.PORT, done); }); }); @@ -13,7 +13,7 @@ require('./pages'); require('./apis'); after((done) => { - console.log('closing server...'); + console.log('\nclosing server...\n'); if (server) { server.close(); } diff --git a/specs/endToEnd/pages.js b/specs/endToEnd/pages.js index f9d2ea5..affc6b6 100644 --- a/specs/endToEnd/pages.js +++ b/specs/endToEnd/pages.js @@ -1,34 +1,43 @@ import chai from 'chai'; +import { clearUsers, prepareUsers } from '../utils'; import request from 'superagent'; import constants from '../constants'; -import User from '../../build/server/models/User'; -import features from './features'; let expect = chai.expect; describe('#Pages', () => { - let userTokens = ['']; - let publicPages = [ - '/', - '/user/register', - '/user/login', - ]; - let privatePages = [ - '/user/me', - ]; + let reqs = { + users: [], + admins: [], + }; + let userInstances = { + users: [], + admins: [], + }; + let pages = { + public: [ + '/', + '/user/register', + '/user/login', + '/user/email/verify', + '/user/password/forget', + '/user/password/reset', + ], + normalUserRequired: [ + '/user/me', + '/user/me/edit', + ], + adminUserRequired: [ + '/admin/user', + ], + }; before((done) => { - User(features.user[0]).save((err, user) => { - if (err) { - return done(err); - } - userTokens[0] = user.toAuthenticationToken(); - done(); - }); + clearUsers(() => prepareUsers(reqs, userInstances, done)); }); - describe('#Unauthorized User', () => { - publicPages.forEach((page) => { - describe('GET ' + page, () => { + describe('#Unauth User', () => { + pages.public.forEach((page) => { + describe(`GET ${page}`, () => { it('should access a public page', (cb) => { request .get(constants.BASE + page) @@ -42,9 +51,9 @@ describe('#Pages', () => { }); }); - privatePages.forEach((page) => { - describe('GET ' + page, () => { - it('should redirect from a private page to login page', (cb) => { + pages.normalUserRequired.forEach((page) => { + describe(`GET ${page}`, () => { + it('should be redirected to login page', (cb) => { request .get(constants.BASE + page) .end((err, res) => { @@ -60,13 +69,13 @@ describe('#Pages', () => { }); }); - describe('#Authorized User', () => { - (publicPages.concat(privatePages)).forEach((page) => { - describe('GET ' + page, () => { - it('should access both public and private pages', (cb) => { + describe('#Normal User', () => { + (pages.public.concat(pages.normalUserRequired)).forEach((page) => { + describe(`GET ${page}`, () => { + it('should access both public and normal-user-only pages', (cb) => { request .get(constants.BASE + page) - .set('Cookie', 'token=' + userTokens[0]) + .set('Cookie', reqs.users[0].get('cookie')) .end((err, res) => { expect(err).to.equal(null); expect(res).to.not.be.undefined; @@ -78,9 +87,25 @@ describe('#Pages', () => { }); }); - after((done) => { - User.remove({ 'email.value': features.user[0].email.value }, (err) => { - done(err); + describe('#Admin User', () => { + (pages.public.concat(pages.adminUserRequired)).forEach((page) => { + describe(`GET ${page}`, () => { + it('should access both public and admin-only pages', (cb) => { + request + .get(constants.BASE + page) + .set('Cookie', reqs.users[0].get('cookie')) + .end((err, res) => { + expect(err).to.equal(null); + expect(res).to.not.be.undefined; + expect(res.status).to.equal(200); + cb(); + }); + }); + }); }); }); + + after((done) => { + clearUsers(done); + }); }); diff --git a/specs/index.js b/specs/index.js index cf7daad..9d99594 100644 --- a/specs/index.js +++ b/specs/index.js @@ -3,6 +3,10 @@ var constants = require('./constants'); process.env.NODE_ENV = constants.NODE_ENV; process.env.PORT = constants.PORT; +describe('#Unit', function() { + require('./unit'); +}); + describe('#EndToEnd', function() { require('./endToEnd'); }); diff --git a/specs/unit/apiEngine.js b/specs/unit/apiEngine.js new file mode 100644 index 0000000..d4a234d --- /dev/null +++ b/specs/unit/apiEngine.js @@ -0,0 +1,14 @@ +import chai from 'chai'; +import ApiEngine from '../../build/common/utils/ApiEngine'; +let expect = chai.expect; + +describe('#ApiEngine', () => { + let apiEngine = new ApiEngine(); + it('should provide http verb methods', () => { + expect(apiEngine.get).to.be.a('function'); + expect(apiEngine.post).to.be.a('function'); + expect(apiEngine.put).to.be.a('function'); + expect(apiEngine.patch).to.be.a('function'); + expect(apiEngine.del).to.be.a('function'); + }); +}); diff --git a/specs/unit/index.js b/specs/unit/index.js new file mode 100644 index 0000000..cdba7cf --- /dev/null +++ b/specs/unit/index.js @@ -0,0 +1 @@ +require('./apiEngine'); diff --git a/specs/utils.js b/specs/utils.js new file mode 100644 index 0000000..11959df --- /dev/null +++ b/specs/utils.js @@ -0,0 +1,52 @@ +import ApiEngine from '../build/common/utils/ApiEngine'; +import User from '../build/server/models/User'; +import features from './endToEnd/features'; + +export let serializeCookie = (cookieObject) => ( + Object + .keys(cookieObject) + .map((key) => `${key}=${cookieObject[key]}`) + .join(';') +); + +export let getReq = ({ cookie }) => ({ + get: (key) => { + if (key === 'cookie') { + return serializeCookie(cookie); + } + }, +}); + +export let clearUsers = (cb) => { + User.remove({}, cb); +}; + +export let prepareUsers = (reqs, userInstances, cb) => { + reqs.users = []; + reqs.admins = []; + userInstances.users = []; + userInstances.admins = []; + + User(features.users.users[0]).save((err, user) => { + userInstances.users.push(user); + User(features.users.admins[0]).save((err, user) => { + userInstances.admins.push(user); + + userInstances.users.forEach((normalUser) => { + reqs.users.push(getReq({ cookie: { + token: normalUser.toAuthenticationToken(), + }})); + }); + userInstances.admins.forEach((adminUser) => { + reqs.admins.push(getReq({ cookie: { + token: adminUser.toAuthenticationToken(), + }})); + }); + cb(err); + }); + }); +}; + +export let apiEngine = new ApiEngine(getReq({ + cookie: {}, +})); diff --git a/src/common/routes/user/edit.js b/src/common/routes/user/edit.js index 44f9bcd..6137389 100644 --- a/src/common/routes/user/edit.js +++ b/src/common/routes/user/edit.js @@ -5,4 +5,5 @@ export default (store) => ({ cb(null, require('../../components/pages/user/EditPage').default); }); }, + onEnter: require('../../utils/authRequired').default(store), }); diff --git a/src/server/app.js b/src/server/app.js index 8a99da9..5ae5683 100644 --- a/src/server/app.js +++ b/src/server/app.js @@ -32,9 +32,13 @@ const appPromise = new Promise((resolve, reject) => { serviceAccount: configs.firebase, databaseURL: clientConfigs.firebase.databaseURL, }); - console.log('[Service] [Firebase]\tenabled'); + if (env !== 'test') { + console.log('[Service] [Firebase]\tenabled'); + } } else { - console.log('[Service] [Firebase]\tdisabled'); + if (env !== 'test') { + console.log('[Service] [Firebase]\tdisabled'); + } } // connect to mongolab @@ -43,7 +47,9 @@ const appPromise = new Promise((resolve, reject) => { if (err) { throw err; } - console.log('[Service] [Mongo]\tenabled'); + if (env !== 'test') { + console.log('[Service] [Mongo]\tenabled'); + } middlewares({ app }); routes({ app }); // error handler for the current request @@ -58,7 +64,9 @@ const appPromise = new Promise((resolve, reject) => { return resolve(app); }); } else { - console.log('[Service] [Mongo]\tdisabled'); + if (env !== 'test') { + console.log('[Service] [Mongo]\tdisabled'); + } return reject(new Error('MongoDB URI is required')); } }); diff --git a/src/server/controllers/mail.js b/src/server/controllers/mail.js index a4de66a..acf9d21 100644 --- a/src/server/controllers/mail.js +++ b/src/server/controllers/mail.js @@ -29,7 +29,7 @@ export default { .then((info) => { res.json({ user: user, - email: info.envelope, + email: info && info.envelope, }); }); }, diff --git a/src/server/server.js b/src/server/server.js index f558175..c035711 100644 --- a/src/server/server.js +++ b/src/server/server.js @@ -12,6 +12,8 @@ appPromise if (err) { throw err; } - console.log('Listening at port', port); + if (app.get('env') !== 'test') { + console.log('Listening at port', port); + } }); });