Skip to content

Commit

Permalink
#163519149 Implement email and in-app notification feature (#42)
Browse files Browse the repository at this point in the history
* feat(notification): adds email and in-app notification feature

- write unit tests
- create notification's model and migration
- implement notification for new article from people you follow
- implement notification for new interaction on article you liked or commented on
- implement new follower notification
- implement notification enpoints
- implement disable and enable notification option

[Delivers #163519149]
  • Loading branch information
mcaleb808 authored and kimotho-njoki committed Mar 19, 2019
1 parent 8100b55 commit 6cea0d4
Show file tree
Hide file tree
Showing 28 changed files with 943 additions and 146 deletions.
13 changes: 12 additions & 1 deletion __mocks__/MailController.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,15 @@ const newPasswordEmail = user => new Promise(resolve => resolve(response(user)))

const sendEmailVerified = user => new Promise(resolve => resolve(response(user)));

export { sendEmailConfirmationLink, resetPasswordEmail, newPasswordEmail, sendEmailVerified };
const newArticledEmail = user => new Promise(resolve => resolve(response(user)));

const newFollowerEmail = user => new Promise(resolve => resolve(response(user)));

export {
sendEmailConfirmationLink,
resetPasswordEmail,
newPasswordEmail,
sendEmailVerified,
newArticledEmail,
newFollowerEmail
};
20 changes: 19 additions & 1 deletion __tests__/controllers/MailController.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import {
sendEmailConfirmationLink,
resetPasswordEmail,
newPasswordEmail,
sendEmailVerified
sendEmailVerified,
newArticledEmail,
newFollowerEmail
} from '../../__mocks__/MailController';

describe('sendgrid', () => {
Expand Down Expand Up @@ -40,4 +42,20 @@ describe('sendgrid', () => {
expect(res[0].statusCode).toBe(202);
done();
});

test('should send new article notification', async done => {
expect.assertions(2);
const res = await newArticledEmail(db.mailUser);
expect(res).toBeDefined();
expect(res[0].statusCode).toBe(202);
done();
});

test('should send new article notification', async done => {
expect.assertions(2);
const res = await newFollowerEmail(db.mailUser);
expect(res).toBeDefined();
expect(res[0].statusCode).toBe(202);
done();
});
});
5 changes: 4 additions & 1 deletion __tests__/routes/article.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Op } from 'sequelize';
import bcrypt from 'bcrypt';
import app from '../../app';
import { urlPrefix } from '../mocks/variables.json';
import { User, Article, Favorite, Report } from '../../database/models';
import { User, Article, Favorite, Report, Notification } from '../../database/models';
import { createArticle, signupUser } from '../mocks/db.json';

let loginUser1;
Expand Down Expand Up @@ -70,6 +70,9 @@ describe('articles', () => {
await Article.destroy({ where: { tagList: { [Op.contains]: ['test'] } } });
await Favorite.destroy({ where: { articleId: newArticle.id } });
await Report.destroy({ where: { reason: 'test' } });
await Notification.destroy({
where: { Notification: { [Op.like]: `%${newArticle.title}%` } }
});
});

test('should return created article', async () => {
Expand Down
5 changes: 4 additions & 1 deletion __tests__/routes/follow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Op } from 'sequelize';
import bcrypt from 'bcrypt';
import app from '../../app';
import { urlPrefix } from '../mocks/variables.json';
import { User, Follow } from '../../database/models';
import { User, Follow, Notification } from '../../database/models';
import { signupUser } from '../mocks/db.json';

let loginUser1;
Expand Down Expand Up @@ -55,6 +55,9 @@ describe('articles', () => {
}
}).then(() => true);
await Follow.destroy({ where: { followee: loginUser1.id, follower: loginUser2.id } });
await Notification.destroy({
where: { userId: loginUser1.id }
});
});

test("Should return you can't follow yourself", async () => {
Expand Down
44 changes: 31 additions & 13 deletions __tests__/routes/likeComment.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import request from 'supertest';
import bcrypt from 'bcrypt';
import { Op } from 'sequelize';
import app from '../../app';
import { urlPrefix } from '../mocks/variables.json';
import { User, Article, Comment } from '../../database/models';
import { User, Article, Comment, Notification } from '../../database/models';
import { createArticle, signupUser, createComment } from '../mocks/db.json';

let testToken, testComment, testArticle;
Expand Down Expand Up @@ -33,9 +34,12 @@ describe('likeComment', () => {
await User.destroy({ where: { email: signupUser.email } });
await Article.destroy({ where: { id: testArticle.id } });
await Comment.destroy({ where: { id: testComment.id } });
await Notification.destroy({
where: { Notification: { [Op.like]: `%${testArticle.title}%` } }
});
});

test('should like a comment', async (done) => {
test('should like a comment', async done => {
const res = await request(app)
.post(`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/like`)
.set('authorization', testToken);
Expand All @@ -57,7 +61,7 @@ describe('likeComment', () => {
expect(res.body.message).toBe('Like removed');
});

test('should like a comment in case it was disliked', async (done) => {
test('should like a comment in case it was disliked', async done => {
await request(app)
.post(`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/dislike`)
.set('authorization', testToken);
Expand All @@ -75,8 +79,9 @@ describe('likeComment', () => {

test('should not like comment without authorization', async () => {
expect.assertions(2);
const res = await request(app)
.post(`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/like`);
const res = await request(app).post(
`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/like`
);

expect(res.status).toBe(401);
expect(res.body.message).toBe('No auth token');
Expand Down Expand Up @@ -135,7 +140,9 @@ describe('likeComment', () => {

test('should not dislike comment without authorization', async () => {
expect.assertions(2);
const res = await request(app).post(`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/dislike`);
const res = await request(app).post(
`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/dislike`
);

expect(res.status).toBe(401);
expect(res.body.message).toBe('No auth token');
Expand Down Expand Up @@ -173,7 +180,9 @@ describe('likeComment', () => {
body: testComment.body,
articleId: testComment.articleId
});
const res = await request(app).get(`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/like`);
const res = await request(app).get(
`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/like`
);

expect(res.status).toBe(404);
expect(res.body.status).toBe(404);
Expand All @@ -185,8 +194,9 @@ describe('likeComment', () => {
await request(app)
.post(`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/like`)
.set('authorization', testToken);
const res = await request(app)
.get(`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/like`);
const res = await request(app).get(
`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/like`
);

expect(res.status).toBe(200);
expect(res.body.status).toBe(200);
Expand All @@ -197,7 +207,9 @@ describe('likeComment', () => {

test('should get not likes for unexisting comment', async () => {
expect.assertions(3);
const res = await request(app).get(`${urlPrefix}/articles/${testArticle.slug}/comments/4b557e5f-d3da-4ac0-a25b-cfd2b244eedc/like`);
const res = await request(app).get(
`${urlPrefix}/articles/${testArticle.slug}/comments/4b557e5f-d3da-4ac0-a25b-cfd2b244eedc/like`
);

expect(res.status).toBe(404);
expect(res.body.status).toBe(404);
Expand All @@ -206,7 +218,9 @@ describe('likeComment', () => {

test('should get not likes', async () => {
expect.assertions(3);
const res = await request(app).get(`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/dislike`);
const res = await request(app).get(
`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/dislike`
);

expect(res.status).toBe(404);
expect(res.body.status).toBe(404);
Expand All @@ -218,7 +232,9 @@ describe('likeComment', () => {
await request(app)
.post(`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/dislike`)
.set('authorization', testToken);
const res = await request(app).get(`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/dislike`);
const res = await request(app).get(
`${urlPrefix}/articles/${testArticle.slug}/comments/${testComment.id}/dislike`
);

expect(res.status).toBe(200);
expect(res.body.status).toBe(200);
Expand All @@ -230,7 +246,9 @@ describe('likeComment', () => {
test('should get not likes for unexisting comment', async () => {
expect.assertions(3);
const fakeId = '4b557e5f-d3da-4ac0-a25b-cfd2b244eedc';
const res = await request(app).get(`${urlPrefix}/articles/${testArticle.slug}/comments/${fakeId}/dislike`);
const res = await request(app).get(
`${urlPrefix}/articles/${testArticle.slug}/comments/${fakeId}/dislike`
);

expect(res.status).toBe(404);
expect(res.body.status).toBe(404);
Expand Down
Loading

0 comments on commit 6cea0d4

Please sign in to comment.