diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts
index 50d655484..1b9210965 100644
--- a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts
+++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts
@@ -19,8 +19,10 @@ jest.mock('../helpers/email/sendMail', () => ({
}))
const chatMessageTemplateMock = jest.fn()
+const notificationTemplateMock = jest.fn()
jest.mock('../helpers/email/templateBuilder', () => ({
chatMessageTemplate: () => chatMessageTemplateMock(),
+ notificationTemplate: () => notificationTemplateMock(),
}))
let isUserOnlineMock = jest.fn()
@@ -85,8 +87,8 @@ afterAll(async () => {
beforeEach(async () => {
publishSpy.mockClear()
- notifiedUser = await neode.create(
- 'User',
+ notifiedUser = await Factory.build(
+ 'user',
{
id: 'you',
name: 'Al Capone',
@@ -186,6 +188,7 @@ describe('notifications', () => {
describe('commenter is not me', () => {
beforeEach(async () => {
+ jest.clearAllMocks()
commentContent = 'Commenters comment.'
commentAuthor = await neode.create(
'User',
@@ -201,25 +204,8 @@ describe('notifications', () => {
)
})
- it('sends me a notification', async () => {
+ it('sends me a notification and email', async () => {
await createCommentOnPostAction()
- const expected = expect.objectContaining({
- data: {
- notifications: [
- {
- read: false,
- createdAt: expect.any(String),
- reason: 'commented_on_post',
- from: {
- __typename: 'Comment',
- id: 'c47',
- content: commentContent,
- },
- relatedUser: null,
- },
- ],
- },
- })
await expect(
query({
query: notificationQuery,
@@ -227,24 +213,85 @@ describe('notifications', () => {
read: false,
},
}),
- ).resolves.toEqual(expected)
+ ).resolves.toMatchObject(
+ expect.objectContaining({
+ data: {
+ notifications: [
+ {
+ read: false,
+ createdAt: expect.any(String),
+ reason: 'commented_on_post',
+ from: {
+ __typename: 'Comment',
+ id: 'c47',
+ content: commentContent,
+ },
+ relatedUser: null,
+ },
+ ],
+ },
+ }),
+ )
+
+ // Mail
+ expect(sendMailMock).toHaveBeenCalledTimes(1)
+ expect(notificationTemplateMock).toHaveBeenCalledTimes(1)
})
- it('sends me no notification if I have blocked the comment author', async () => {
- await notifiedUser.relateTo(commentAuthor, 'blocked')
- await createCommentOnPostAction()
- const expected = expect.objectContaining({
- data: { notifications: [] },
- })
+ describe('if I have disabled `emailNotificationsCommentOnObservedPost`', () => {
+ it('sends me a notification but no email', async () => {
+ await notifiedUser.update({ emailNotificationsCommentOnObservedPost: false })
+ await createCommentOnPostAction()
+ await expect(
+ query({
+ query: notificationQuery,
+ variables: {
+ read: false,
+ },
+ }),
+ ).resolves.toMatchObject(
+ expect.objectContaining({
+ data: {
+ notifications: [
+ {
+ read: false,
+ createdAt: expect.any(String),
+ reason: 'commented_on_post',
+ from: {
+ __typename: 'Comment',
+ id: 'c47',
+ content: commentContent,
+ },
+ relatedUser: null,
+ },
+ ],
+ },
+ }),
+ )
- await expect(
- query({
- query: notificationQuery,
- variables: {
- read: false,
- },
- }),
- ).resolves.toEqual(expected)
+ // No Mail
+ expect(sendMailMock).not.toHaveBeenCalled()
+ expect(notificationTemplateMock).not.toHaveBeenCalled()
+ })
+ })
+
+ describe('if I have blocked the comment author', () => {
+ it('sends me no notification', async () => {
+ await notifiedUser.relateTo(commentAuthor, 'blocked')
+ await createCommentOnPostAction()
+ const expected = expect.objectContaining({
+ data: { notifications: [] },
+ })
+
+ await expect(
+ query({
+ query: notificationQuery,
+ variables: {
+ read: false,
+ },
+ }),
+ ).resolves.toEqual(expected)
+ })
})
})
@@ -273,6 +320,7 @@ describe('notifications', () => {
})
beforeEach(async () => {
+ jest.clearAllMocks()
postAuthor = await neode.create(
'User',
{
@@ -295,7 +343,7 @@ describe('notifications', () => {
'Hey @al-capone how do you do?'
})
- it('sends me a notification', async () => {
+ it('sends me a notification and email', async () => {
await createPostAction()
const expectedContent =
'Hey @al-capone how do you do?'
@@ -323,6 +371,47 @@ describe('notifications', () => {
],
},
})
+
+ // Mail
+ expect(sendMailMock).toHaveBeenCalledTimes(1)
+ expect(notificationTemplateMock).toHaveBeenCalledTimes(1)
+ })
+
+ describe('if I have disabled `emailNotificationsMention`', () => {
+ it('sends me a notification but no email', async () => {
+ await notifiedUser.update({ emailNotificationsMention: false })
+ await createPostAction()
+ const expectedContent =
+ 'Hey @al-capone how do you do?'
+ await expect(
+ query({
+ query: notificationQuery,
+ variables: {
+ read: false,
+ },
+ }),
+ ).resolves.toMatchObject({
+ errors: undefined,
+ data: {
+ notifications: [
+ {
+ read: false,
+ createdAt: expect.any(String),
+ reason: 'mentioned_in_post',
+ from: {
+ __typename: 'Post',
+ id: 'p47',
+ content: expectedContent,
+ },
+ },
+ ],
+ },
+ })
+
+ // Mail
+ expect(sendMailMock).not.toHaveBeenCalled()
+ expect(notificationTemplateMock).not.toHaveBeenCalled()
+ })
})
it('publishes `NOTIFICATION_ADDED` to me', async () => {
@@ -688,7 +777,7 @@ describe('notifications', () => {
roomId = room.data.CreateRoom.id
})
- describe('chatReceiver is online', () => {
+ describe('if the chatReceiver is online', () => {
it('sends no email', async () => {
isUserOnlineMock = jest.fn().mockReturnValue(true)
@@ -705,7 +794,7 @@ describe('notifications', () => {
})
})
- describe('chatReceiver is offline', () => {
+ describe('if the chatReceiver is offline', () => {
it('sends an email', async () => {
isUserOnlineMock = jest.fn().mockReturnValue(false)
@@ -722,7 +811,7 @@ describe('notifications', () => {
})
})
- describe('chatReceiver has blocked chatSender', () => {
+ describe('if the chatReceiver has blocked chatSender', () => {
it('sends no email', async () => {
isUserOnlineMock = jest.fn().mockReturnValue(false)
await chatReceiver.relateTo(chatSender, 'blocked')
@@ -740,10 +829,10 @@ describe('notifications', () => {
})
})
- describe('chatReceiver has disabled email notifications', () => {
+ describe('if the chatReceiver has disabled `emailNotificationsChatMessage`', () => {
it('sends no email', async () => {
isUserOnlineMock = jest.fn().mockReturnValue(false)
- await chatReceiver.update({ sendNotificationEmails: false })
+ await chatReceiver.update({ emailNotificationsChatMessage: false })
await mutate({
mutation: createMessageMutation(),
@@ -763,8 +852,8 @@ describe('notifications', () => {
let groupOwner
beforeEach(async () => {
- groupOwner = await neode.create(
- 'User',
+ groupOwner = await Factory.build(
+ 'user',
{
id: 'group-owner',
name: 'Group Owner',
@@ -791,7 +880,7 @@ describe('notifications', () => {
})
describe('user joins group', () => {
- beforeEach(async () => {
+ const joinGroupAction = async () => {
authenticatedUser = await notifiedUser.toJson()
await mutate({
mutation: joinGroupMutation(),
@@ -801,9 +890,14 @@ describe('notifications', () => {
},
})
authenticatedUser = await groupOwner.toJson()
+ }
+
+ beforeEach(async () => {
+ jest.clearAllMocks()
})
- it('has the notification in database', async () => {
+ it('sends the group owner a notification and email', async () => {
+ await joinGroupAction()
await expect(
query({
query: notificationQuery,
@@ -827,19 +921,50 @@ describe('notifications', () => {
},
errors: undefined,
})
+
+ // Mail
+ expect(sendMailMock).toHaveBeenCalledTimes(1)
+ expect(notificationTemplateMock).toHaveBeenCalledTimes(1)
+ })
+
+ describe('if the group owner has disabled `emailNotificationsGroupMemberJoined`', () => {
+ it('sends the group owner a notification but no email', async () => {
+ await groupOwner.update({ emailNotificationsGroupMemberJoined: false })
+ await joinGroupAction()
+ await expect(
+ query({
+ query: notificationQuery,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ notifications: [
+ {
+ read: false,
+ reason: 'user_joined_group',
+ createdAt: expect.any(String),
+ from: {
+ __typename: 'Group',
+ id: 'closed-group',
+ },
+ relatedUser: {
+ id: 'you',
+ },
+ },
+ ],
+ },
+ errors: undefined,
+ })
+
+ // Mail
+ expect(sendMailMock).not.toHaveBeenCalled()
+ expect(notificationTemplateMock).not.toHaveBeenCalled()
+ })
})
})
- describe('user leaves group', () => {
- beforeEach(async () => {
+ describe('user joins and leaves group', () => {
+ const leaveGroupAction = async () => {
authenticatedUser = await notifiedUser.toJson()
- await mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'closed-group',
- userId: authenticatedUser.id,
- },
- })
await mutate({
mutation: leaveGroupMutation(),
variables: {
@@ -848,9 +973,22 @@ describe('notifications', () => {
},
})
authenticatedUser = await groupOwner.toJson()
+ }
+
+ beforeEach(async () => {
+ jest.clearAllMocks()
+ authenticatedUser = await notifiedUser.toJson()
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'closed-group',
+ userId: authenticatedUser.id,
+ },
+ })
})
- it('has two the notification in database', async () => {
+ it('sends the group owner two notifications and emails', async () => {
+ await leaveGroupAction()
await expect(
query({
query: notificationQuery,
@@ -886,19 +1024,61 @@ describe('notifications', () => {
},
errors: undefined,
})
+
+ // Mail
+ expect(sendMailMock).toHaveBeenCalledTimes(2)
+ expect(notificationTemplateMock).toHaveBeenCalledTimes(2)
+ })
+
+ describe('if the group owner has disabled `emailNotificationsGroupMemberLeft`', () => {
+ it('sends the group owner two notification but only only one email', async () => {
+ await groupOwner.update({ emailNotificationsGroupMemberLeft: false })
+ await leaveGroupAction()
+ await expect(
+ query({
+ query: notificationQuery,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ notifications: [
+ {
+ read: false,
+ reason: 'user_left_group',
+ createdAt: expect.any(String),
+ from: {
+ __typename: 'Group',
+ id: 'closed-group',
+ },
+ relatedUser: {
+ id: 'you',
+ },
+ },
+ {
+ read: false,
+ reason: 'user_joined_group',
+ createdAt: expect.any(String),
+ from: {
+ __typename: 'Group',
+ id: 'closed-group',
+ },
+ relatedUser: {
+ id: 'you',
+ },
+ },
+ ],
+ },
+ errors: undefined,
+ })
+
+ // Mail
+ expect(sendMailMock).toHaveBeenCalledTimes(1)
+ expect(notificationTemplateMock).toHaveBeenCalledTimes(1)
+ })
})
})
describe('user role in group changes', () => {
- beforeEach(async () => {
- authenticatedUser = await notifiedUser.toJson()
- await mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'closed-group',
- userId: authenticatedUser.id,
- },
- })
+ const changeGroupMemberRoleAction = async () => {
authenticatedUser = await groupOwner.toJson()
await mutate({
mutation: changeGroupMemberRoleMutation(),
@@ -909,9 +1089,23 @@ describe('notifications', () => {
},
})
authenticatedUser = await notifiedUser.toJson()
+ }
+
+ beforeEach(async () => {
+ authenticatedUser = await notifiedUser.toJson()
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'closed-group',
+ userId: authenticatedUser.id,
+ },
+ })
+ // Clear after because the above generates a notification not related
+ jest.clearAllMocks()
})
- it('has notification in database', async () => {
+ it('sends the group member a notification and email', async () => {
+ await changeGroupMemberRoleAction()
await expect(
query({
query: notificationQuery,
@@ -935,19 +1129,49 @@ describe('notifications', () => {
},
errors: undefined,
})
+
+ // Mail
+ expect(sendMailMock).toHaveBeenCalledTimes(1)
+ expect(notificationTemplateMock).toHaveBeenCalledTimes(1)
+ })
+
+ describe('if the group member has disabled `emailNotificationsGroupMemberRoleChanged`', () => {
+ it('sends the group member a notification but no email', async () => {
+ notifiedUser.update({ emailNotificationsGroupMemberRoleChanged: false })
+ await changeGroupMemberRoleAction()
+ await expect(
+ query({
+ query: notificationQuery,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ notifications: [
+ {
+ read: false,
+ reason: 'changed_group_member_role',
+ createdAt: expect.any(String),
+ from: {
+ __typename: 'Group',
+ id: 'closed-group',
+ },
+ relatedUser: {
+ id: 'group-owner',
+ },
+ },
+ ],
+ },
+ errors: undefined,
+ })
+
+ // Mail
+ expect(sendMailMock).not.toHaveBeenCalled()
+ expect(notificationTemplateMock).not.toHaveBeenCalled()
+ })
})
})
describe('user is removed from group', () => {
- beforeEach(async () => {
- authenticatedUser = await notifiedUser.toJson()
- await mutate({
- mutation: joinGroupMutation(),
- variables: {
- groupId: 'closed-group',
- userId: authenticatedUser.id,
- },
- })
+ const removeUserFromGroupAction = async () => {
authenticatedUser = await groupOwner.toJson()
await mutate({
mutation: removeUserFromGroupMutation(),
@@ -957,9 +1181,23 @@ describe('notifications', () => {
},
})
authenticatedUser = await notifiedUser.toJson()
+ }
+
+ beforeEach(async () => {
+ authenticatedUser = await notifiedUser.toJson()
+ await mutate({
+ mutation: joinGroupMutation(),
+ variables: {
+ groupId: 'closed-group',
+ userId: authenticatedUser.id,
+ },
+ })
+ // Clear after because the above generates a notification not related
+ jest.clearAllMocks()
})
- it('has notification in database', async () => {
+ it('sends the previous group member a notification and email', async () => {
+ await removeUserFromGroupAction()
await expect(
query({
query: notificationQuery,
@@ -983,6 +1221,44 @@ describe('notifications', () => {
},
errors: undefined,
})
+
+ // Mail
+ expect(sendMailMock).toHaveBeenCalledTimes(1)
+ expect(notificationTemplateMock).toHaveBeenCalledTimes(1)
+ })
+
+ describe('if the previous group member has disabled `emailNotificationsGroupMemberRemoved`', () => {
+ it('sends the previous group member a notification but no email', async () => {
+ notifiedUser.update({ emailNotificationsGroupMemberRemoved: false })
+ await removeUserFromGroupAction()
+ await expect(
+ query({
+ query: notificationQuery,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ notifications: [
+ {
+ read: false,
+ reason: 'removed_user_from_group',
+ createdAt: expect.any(String),
+ from: {
+ __typename: 'Group',
+ id: 'closed-group',
+ },
+ relatedUser: {
+ id: 'group-owner',
+ },
+ },
+ ],
+ },
+ errors: undefined,
+ })
+
+ // Mail
+ expect(sendMailMock).not.toHaveBeenCalled()
+ expect(notificationTemplateMock).not.toHaveBeenCalled()
+ })
})
})
})
diff --git a/backend/src/schema/resolvers/users.spec.ts b/backend/src/schema/resolvers/users.spec.ts
index b746283b7..9c9d3e4fb 100644
--- a/backend/src/schema/resolvers/users.spec.ts
+++ b/backend/src/schema/resolvers/users.spec.ts
@@ -769,6 +769,15 @@ describe('emailNotificationSettings', () => {
},
],
},
+ {
+ type: 'chat',
+ settings: [
+ {
+ name: 'chatMessage',
+ value: true,
+ },
+ ],
+ },
{
type: 'group',
settings: [