mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-12 23:35:58 +00:00
feat(backend): emails for notifications (#8435)
* email templates with pug for all possible notification emails * more information in emails * Individual email subjects to all notification emails --------- Co-authored-by: Ulf Gebhardt <ulf.gebhardt@webcraft-media.de> Co-authored-by: mahula <lenzmath@posteo.de>
This commit is contained in:
parent
4f05b852af
commit
e4ae0dfe50
4
.github/workflows/test-e2e.yml
vendored
4
.github/workflows/test-e2e.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cp webapp/.env.template webapp/.env
|
cp webapp/.env.template webapp/.env
|
||||||
cp frontend/.env.dist frontend/.env
|
cp frontend/.env.dist frontend/.env
|
||||||
cp backend/.env.template backend/.env
|
cp backend/.env.test_e2e backend/.env
|
||||||
|
|
||||||
- name: Build docker images
|
- name: Build docker images
|
||||||
run: |
|
run: |
|
||||||
@ -77,7 +77,7 @@ jobs:
|
|||||||
docker load < /tmp/images/neo4j.tar
|
docker load < /tmp/images/neo4j.tar
|
||||||
docker load < /tmp/images/backend.tar
|
docker load < /tmp/images/backend.tar
|
||||||
docker load < /tmp/images/webapp.tar
|
docker load < /tmp/images/webapp.tar
|
||||||
docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp neo4j backend --build
|
docker compose -f docker-compose.yml -f docker-compose.test.yml up --build --detach --no-deps webapp neo4j backend mailserver
|
||||||
sleep 90s
|
sleep 90s
|
||||||
|
|
||||||
- name: Full stack tests | run tests
|
- name: Full stack tests | run tests
|
||||||
|
|||||||
41
backend/.env.test_e2e
Normal file
41
backend/.env.test_e2e
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
DEBUG=true
|
||||||
|
|
||||||
|
NEO4J_URI=bolt://localhost:7687
|
||||||
|
NEO4J_USERNAME=neo4j
|
||||||
|
NEO4J_PASSWORD=letmein
|
||||||
|
GRAPHQL_URI=http://localhost:4000
|
||||||
|
CLIENT_URI=http://localhost:3000
|
||||||
|
|
||||||
|
# E-Mail default settings
|
||||||
|
EMAIL_SUPPORT="devops@ocelot.social"
|
||||||
|
EMAIL_DEFAULT_SENDER="devops@ocelot.social"
|
||||||
|
SMTP_HOST=mailserver
|
||||||
|
SMTP_PORT=1025
|
||||||
|
SMTP_IGNORE_TLS=true
|
||||||
|
SMTP_MAX_CONNECTIONS=5
|
||||||
|
SMTP_MAX_MESSAGES=Infinity
|
||||||
|
SMTP_USERNAME=
|
||||||
|
SMTP_PASSWORD=
|
||||||
|
SMTP_SECURE="false" # true for 465, false for other ports
|
||||||
|
SMTP_DKIM_DOMAINNAME=
|
||||||
|
SMTP_DKIM_KEYSELECTOR=
|
||||||
|
SMTP_DKIM_PRIVATKEY=
|
||||||
|
|
||||||
|
JWT_SECRET="b/&&7b78BF&fv/Vd"
|
||||||
|
JWT_EXPIRES="2y"
|
||||||
|
MAPBOX_TOKEN="pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g"
|
||||||
|
|
||||||
|
PRIVATE_KEY_PASSPHRASE="a7dsf78sadg87ad87sfagsadg78"
|
||||||
|
|
||||||
|
SENTRY_DSN_BACKEND=
|
||||||
|
COMMIT=
|
||||||
|
PUBLIC_REGISTRATION=false
|
||||||
|
INVITE_REGISTRATION=true
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID=
|
||||||
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
AWS_ENDPOINT=
|
||||||
|
AWS_REGION=
|
||||||
|
AWS_BUCKET=
|
||||||
|
|
||||||
|
CATEGORIES_ACTIVE=false
|
||||||
@ -36,6 +36,7 @@
|
|||||||
"cheerio": "~1.0.0",
|
"cheerio": "~1.0.0",
|
||||||
"cross-env": "~7.0.3",
|
"cross-env": "~7.0.3",
|
||||||
"dotenv": "~16.5.0",
|
"dotenv": "~16.5.0",
|
||||||
|
"email-templates": "^12.0.2",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"graphql": "^14.6.0",
|
"graphql": "^14.6.0",
|
||||||
"graphql-middleware": "~4.0.2",
|
"graphql-middleware": "~4.0.2",
|
||||||
@ -77,6 +78,8 @@
|
|||||||
"node-fetch": "^2.7.0",
|
"node-fetch": "^2.7.0",
|
||||||
"nodemailer": "^6.10.1",
|
"nodemailer": "^6.10.1",
|
||||||
"nodemailer-html-to-text": "^3.2.0",
|
"nodemailer-html-to-text": "^3.2.0",
|
||||||
|
"preview-email": "^3.1.0",
|
||||||
|
"pug": "^3.0.3",
|
||||||
"request": "~2.88.2",
|
"request": "~2.88.2",
|
||||||
"sanitize-html": "~2.16.0",
|
"sanitize-html": "~2.16.0",
|
||||||
"slug": "~9.1.0",
|
"slug": "~9.1.0",
|
||||||
@ -88,6 +91,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint-community/eslint-plugin-eslint-comments": "^4.5.0",
|
"@eslint-community/eslint-plugin-eslint-comments": "^4.5.0",
|
||||||
"@faker-js/faker": "9.7.0",
|
"@faker-js/faker": "9.7.0",
|
||||||
|
"@types/email-templates": "^10.0.4",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/lodash": "^4.17.16",
|
"@types/lodash": "^4.17.16",
|
||||||
"@types/node": "^22.15.3",
|
"@types/node": "^22.15.3",
|
||||||
@ -119,7 +123,10 @@
|
|||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"**/**/fs-capacitor": "^6.2.0",
|
"**/**/fs-capacitor": "^6.2.0",
|
||||||
"**/graphql-upload": "^11.0.0"
|
"**/graphql-upload": "^11.0.0",
|
||||||
|
"**/strip-ansi": "6.0.1",
|
||||||
|
"**/string-width": "4.2.0",
|
||||||
|
"**/wrap-ansi": "7.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.12.1"
|
"node": ">=20.12.1"
|
||||||
|
|||||||
@ -25,6 +25,7 @@ const environment = {
|
|||||||
DISABLED_MIDDLEWARES: ['test', 'development'].includes(env.NODE_ENV as string)
|
DISABLED_MIDDLEWARES: ['test', 'development'].includes(env.NODE_ENV as string)
|
||||||
? (env.DISABLED_MIDDLEWARES?.split(',') ?? [])
|
? (env.DISABLED_MIDDLEWARES?.split(',') ?? [])
|
||||||
: [],
|
: [],
|
||||||
|
SEND_MAIL: env.NODE_ENV !== 'test',
|
||||||
}
|
}
|
||||||
|
|
||||||
const required = {
|
const required = {
|
||||||
|
|||||||
@ -26,6 +26,8 @@ if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) {
|
|||||||
throw new Error(`You cannot seed the database in a non-staging and real production environment!`)
|
throw new Error(`You cannot seed the database in a non-staging and real production environment!`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CONFIG.SEND_MAIL = true
|
||||||
|
|
||||||
const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||||
|
|
||||||
;(async function () {
|
;(async function () {
|
||||||
|
|||||||
32
backend/src/db/types/User.ts
Normal file
32
backend/src/db/types/User.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { Integer, Node } from 'neo4j-driver'
|
||||||
|
|
||||||
|
export interface UserDbProperties {
|
||||||
|
allowEmbedIframes: boolean
|
||||||
|
awaySince?: string
|
||||||
|
createdAt: string
|
||||||
|
deleted: boolean
|
||||||
|
disabled: boolean
|
||||||
|
emailNotificationsChatMessage?: boolean
|
||||||
|
emailNotificationsCommentOnObservedPost?: boolean
|
||||||
|
emailNotificationsFollowingUsers?: boolean
|
||||||
|
emailNotificationsGroupMemberJoined?: boolean
|
||||||
|
emailNotificationsGroupMemberLeft?: boolean
|
||||||
|
emailNotificationsGroupMemberRemoved?: boolean
|
||||||
|
emailNotificationsGroupMemberRoleChanged?: boolean
|
||||||
|
emailNotificationsMention?: boolean
|
||||||
|
emailNotificationsPostInGroup?: boolean
|
||||||
|
encryptedPassword: string
|
||||||
|
id: string
|
||||||
|
lastActiveAt?: string
|
||||||
|
lastOnlineStatus?: string
|
||||||
|
locale: string
|
||||||
|
name: string
|
||||||
|
role: string
|
||||||
|
showShoutsPublicly: boolean
|
||||||
|
slug: string
|
||||||
|
termsAndConditionsAgreedAt: string
|
||||||
|
termsAndConditionsAgreedVersion: string
|
||||||
|
updatedAt: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type User = Node<Integer, UserDbProperties>
|
||||||
@ -0,0 +1,247 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`sendChatMessageMail English chat_message template 1`] = `
|
||||||
|
{
|
||||||
|
"attachments": [],
|
||||||
|
"from": "ocelot.social",
|
||||||
|
"html": "<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta content="multipart/html; charset=UTF-8" http-equiv="content-type">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}</style>
|
||||||
|
<style>body{
|
||||||
|
display: block;
|
||||||
|
font-family: Lato, sans-serif;
|
||||||
|
font-size: 17px;
|
||||||
|
text-align: left;
|
||||||
|
text-align: -webkit-left;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-top: 25px;
|
||||||
|
font-size: 25px;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 22px;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 680px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head-logo {
|
||||||
|
width: 60%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #17b53e;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.button {
|
||||||
|
background: #17b53e;
|
||||||
|
font-family: Lato, sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 15px;
|
||||||
|
text-decoration: none;
|
||||||
|
text-align:center;
|
||||||
|
padding: 13px 17px;
|
||||||
|
color: #ffffff;
|
||||||
|
display: table;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-block {
|
||||||
|
margin-top: 20px;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
padding: 20px;
|
||||||
|
font-family: Lato, sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 15px;
|
||||||
|
text-align: center;
|
||||||
|
color: #888888;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<header>
|
||||||
|
<div class="head"><img class="head-logo" alt="Welcome Image" loading="lazy" src="http://webapp:3000/img/custom/logo-squared.svg">
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<h2>Hello chatReceiver,</h2>
|
||||||
|
<div class="wrapper">
|
||||||
|
<div class="content"></div>
|
||||||
|
<p>you have received a new chat message from <a class="user" href="http://webapp:3000/user/chatSender/chatsender">chatSender</a>.
|
||||||
|
</p><a class="button" href="http://webapp:3000/chat">Show Chat</a>
|
||||||
|
<div class="text-block">
|
||||||
|
<p>See you soon on <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||||
|
<p>– The ocelot.social Team</p><br>
|
||||||
|
<p>PS: If you don't want to receive e-mails anymore, change your <a class="settings" href="http://webapp:3000/settings/notifications">notification settings</a>!</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>",
|
||||||
|
"subject": "ocelot.social – Notification: New chat message",
|
||||||
|
"text": "HELLO CHATRECEIVER,
|
||||||
|
|
||||||
|
you have received a new chat message from chatSender
|
||||||
|
[http://webapp:3000/user/chatSender/chatsender].
|
||||||
|
|
||||||
|
Show Chat [http://webapp:3000/chat]
|
||||||
|
|
||||||
|
See you soon on ocelot.social [https://ocelot.social]!
|
||||||
|
|
||||||
|
– The ocelot.social Team
|
||||||
|
|
||||||
|
|
||||||
|
PS: If you don't want to receive e-mails anymore, change your notification
|
||||||
|
settings [http://webapp:3000/settings/notifications]!
|
||||||
|
|
||||||
|
|
||||||
|
ocelot.social Community [https://ocelot.social]",
|
||||||
|
"to": "user@example.org",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`sendChatMessageMail German chat_message template 1`] = `
|
||||||
|
{
|
||||||
|
"attachments": [],
|
||||||
|
"from": "ocelot.social",
|
||||||
|
"html": "<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta content="multipart/html; charset=UTF-8" http-equiv="content-type">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}</style>
|
||||||
|
<style>body{
|
||||||
|
display: block;
|
||||||
|
font-family: Lato, sans-serif;
|
||||||
|
font-size: 17px;
|
||||||
|
text-align: left;
|
||||||
|
text-align: -webkit-left;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-top: 25px;
|
||||||
|
font-size: 25px;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 22px;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 680px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head-logo {
|
||||||
|
width: 60%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #17b53e;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.button {
|
||||||
|
background: #17b53e;
|
||||||
|
font-family: Lato, sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 15px;
|
||||||
|
text-decoration: none;
|
||||||
|
text-align:center;
|
||||||
|
padding: 13px 17px;
|
||||||
|
color: #ffffff;
|
||||||
|
display: table;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-block {
|
||||||
|
margin-top: 20px;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
padding: 20px;
|
||||||
|
font-family: Lato, sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 15px;
|
||||||
|
text-align: center;
|
||||||
|
color: #888888;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<header>
|
||||||
|
<div class="head"><img class="head-logo" alt="Welcome Image" loading="lazy" src="http://webapp:3000/img/custom/logo-squared.svg">
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<h2>Hallo chatReceiver,</h2>
|
||||||
|
<div class="wrapper">
|
||||||
|
<div class="content"></div>
|
||||||
|
<p>du hast eine neue Chat-Nachricht von <a class="user" href="http://webapp:3000/user/chatSender/chatsender">chatSender</a> erhalten.
|
||||||
|
</p><a class="button" href="http://webapp:3000/chat">Chat anzeigen</a>
|
||||||
|
<div class="text-block">
|
||||||
|
<p>Bis bald bei <a class="organization" href="https://ocelot.social">ocelot.social</a>!</p>
|
||||||
|
<p>– Dein ocelot.social Team</p><br>
|
||||||
|
<p>PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine <a class="settings" href="http://webapp:3000/settings/notifications">Benachrichtigungseinstellungen</a>!</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<div class="footer"></div><a href="https://ocelot.social">ocelot.social Community</a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>",
|
||||||
|
"subject": "ocelot.social – Benachrichtigung: Neue Chat Nachricht",
|
||||||
|
"text": "HALLO CHATRECEIVER,
|
||||||
|
|
||||||
|
du hast eine neue Chat-Nachricht von chatSender
|
||||||
|
[http://webapp:3000/user/chatSender/chatsender] erhalten.
|
||||||
|
|
||||||
|
Chat anzeigen [http://webapp:3000/chat]
|
||||||
|
|
||||||
|
Bis bald bei ocelot.social [https://ocelot.social]!
|
||||||
|
|
||||||
|
– Dein ocelot.social Team
|
||||||
|
|
||||||
|
|
||||||
|
PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine
|
||||||
|
Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]!
|
||||||
|
|
||||||
|
|
||||||
|
ocelot.social Community [https://ocelot.social]",
|
||||||
|
"to": "user@example.org",
|
||||||
|
}
|
||||||
|
`;
|
||||||
2209
backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap
Normal file
2209
backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap
Normal file
File diff suppressed because it is too large
Load Diff
39
backend/src/emails/locales/de.json
Normal file
39
backend/src/emails/locales/de.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"notification": "Benachrichtigung",
|
||||||
|
"subjects": {
|
||||||
|
"changedGroupMemberRole": "Rolle in Gruppe geändert",
|
||||||
|
"chatMessage": "Neue Chat Nachricht",
|
||||||
|
"commentedOnPost": "Neuer Kommentar zu Beitrag",
|
||||||
|
"followedUserPosted": "Neuer Beitrag von gefolgtem Nutzer",
|
||||||
|
"mentionedInComment": "Erwähnung in Kommentar",
|
||||||
|
"mentionedInPost": "Erwähnung in Beitrag",
|
||||||
|
"removedUserFromGroup": "Aus Gruppe entfernt",
|
||||||
|
"postInGroup": "Neuer Beitrag in Gruppe",
|
||||||
|
"userJoinedGroup": "Nutzer tritt Gruppe bei",
|
||||||
|
"userLeftGroup": "Nutzer verlässt Gruppe"
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"viewChat": "Chat anzeigen",
|
||||||
|
"viewComment": "Kommentar ansehen",
|
||||||
|
"viewGroup": "Gruppe ansehen",
|
||||||
|
"viewPost": "Beitrag ansehen"
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"greeting": "Hallo",
|
||||||
|
"seeYou": "Bis bald bei ",
|
||||||
|
"yourTeam": "– Dein {team} Team",
|
||||||
|
"settingsHint": "PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine ",
|
||||||
|
"settingsName": "Benachrichtigungseinstellungen"
|
||||||
|
},
|
||||||
|
"changedGroupMemberRole": "deine Rolle in der Gruppe „{groupName}“ wurde geändert. Klicke auf den Knopf, um diese Gruppe zu sehen:",
|
||||||
|
"chatMessageStart": "du hast eine neue Chat-Nachricht von ",
|
||||||
|
"chatMessageEnd": " erhalten.",
|
||||||
|
"commentedOnPost": " hat einen Beitrag den du beobachtest mit dem Titel „{postTitle}“ kommentiert. Klicke auf den Knopf, um diesen Kommentar zu sehen:",
|
||||||
|
"followedUserPosted": ", ein Nutzer dem du folgst, hat einen neuen Beitrag mit dem Titel „{postTitle}“ geschrieben. Klicke auf den Knopf, um diesen Beitrag zu sehen:",
|
||||||
|
"mentionedInComment": " hat dich in einem Kommentar zu dem Beitrag mit dem Titel „{postTitle}“ erwähnt. Klicke auf den Knopf, um den Kommentar zu sehen:",
|
||||||
|
"mentionedInPost": " hat Dich in einem Beitrag mit dem Titel „{postTitle}“ erwähnt. Klicke auf den Knopf, um den Beitrag zu sehen:",
|
||||||
|
"postInGroup": "jemand hat einen neuen Beitrag mit dem Titel „{postTitle}“ in einer deiner Gruppen geschrieben. Klicke auf den Knopf, um diesen Beitrag zu sehen:",
|
||||||
|
"removedUserFromGroup": "du wurdest aus der Gruppe „{groupName}“ entfernt.",
|
||||||
|
"userJoinedGroup": " ist der Gruppe „{groupName}“ beigetreten. Klicke auf den Knopf, um diese Gruppe zu sehen:",
|
||||||
|
"userLeftGroup": " hat die Gruppe „{groupName}“ verlassen. Klicke auf den Knopf, um diese Gruppe zu sehen:"
|
||||||
|
}
|
||||||
39
backend/src/emails/locales/en.json
Normal file
39
backend/src/emails/locales/en.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"notification": "Notification",
|
||||||
|
"subjects": {
|
||||||
|
"changedGroupMemberRole": "Role in group changed",
|
||||||
|
"chatMessage": "New chat message",
|
||||||
|
"commentedOnPost": "New comment on post",
|
||||||
|
"followedUserPosted": "New post by followd user",
|
||||||
|
"mentionedInComment": "Mentioned in comment",
|
||||||
|
"mentionedInPost": "Mentioned in post",
|
||||||
|
"removedUserFromGroup": "Removed from group",
|
||||||
|
"postInGroup": "New post in group",
|
||||||
|
"userJoinedGroup": "User joined group",
|
||||||
|
"userLeftGroup": "User left group"
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"viewChat": "Show Chat",
|
||||||
|
"viewComment": "View comment",
|
||||||
|
"viewGroup": "View group",
|
||||||
|
"viewPost": "View post"
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"greeting": "Hello",
|
||||||
|
"seeYou": "See you soon on ",
|
||||||
|
"yourTeam": "– The {team} Team",
|
||||||
|
"settingsHint": "PS: If you don't want to receive e-mails anymore, change your ",
|
||||||
|
"settingsName": "notification settings"
|
||||||
|
},
|
||||||
|
"changedGroupMemberRole": "your role in the group “{groupName}” has been changed. Click on the button to view this group:",
|
||||||
|
"chatMessageStart": "you have received a new chat message from ",
|
||||||
|
"chatMessageEnd": ".",
|
||||||
|
"commentedOnPost": " commented on a post that you are observing with the title “{postTitle}”. Click on the button to view this comment:",
|
||||||
|
"followedUserPosted": ", a user you are following, wrote a new post with the title “{postTitle}”. Click on the button to view this post:",
|
||||||
|
"mentionedInComment": " mentioned you in a comment to the post with the title “{postTitle}”. Click on the button to view this comment:",
|
||||||
|
"mentionedInPost": " mentioned you in a post with the title “{postTitle}”. Click on the button to view this post:",
|
||||||
|
"removedUserFromGroup": "you have been removed from the group “{groupName}”.",
|
||||||
|
"postInGroup": "someone wrote a new post with the title “{postTitle}” in one of your groups. Click on the button to view this post:",
|
||||||
|
"userJoinedGroup": " joined the group “{groupName}”. Click on the button to view this group:",
|
||||||
|
"userLeftGroup": " left the group “{groupName}”. Click on the button to view this group:"
|
||||||
|
}
|
||||||
87
backend/src/emails/sendChatMessageMail.spec.ts
Normal file
87
backend/src/emails/sendChatMessageMail.spec.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import { sendChatMessageMail } from './sendEmail'
|
||||||
|
|
||||||
|
const senderUser = {
|
||||||
|
allowEmbedIframes: false,
|
||||||
|
createdAt: '2025-04-30T00:16:49.610Z',
|
||||||
|
deleted: false,
|
||||||
|
disabled: false,
|
||||||
|
emailNotificationsChatMessage: true,
|
||||||
|
emailNotificationsCommentOnObservedPost: true,
|
||||||
|
emailNotificationsFollowingUsers: true,
|
||||||
|
emailNotificationsGroupMemberJoined: true,
|
||||||
|
emailNotificationsGroupMemberLeft: true,
|
||||||
|
emailNotificationsGroupMemberRemoved: true,
|
||||||
|
emailNotificationsGroupMemberRoleChanged: true,
|
||||||
|
emailNotificationsMention: true,
|
||||||
|
emailNotificationsPostInGroup: true,
|
||||||
|
encryptedPassword: '$2b$10$n.WujXapJrvn498lS97MD.gn8QwjWI9xlf8ckEYYtMTOPadMidcbG',
|
||||||
|
id: 'chatSender',
|
||||||
|
locale: 'en',
|
||||||
|
name: 'chatSender',
|
||||||
|
role: 'user',
|
||||||
|
showShoutsPublicly: false,
|
||||||
|
slug: 'chatsender',
|
||||||
|
termsAndConditionsAgreedAt: '2019-08-01T10:47:19.212Z',
|
||||||
|
termsAndConditionsAgreedVersion: '0.0.1',
|
||||||
|
updatedAt: '2025-04-30T00:16:49.610Z',
|
||||||
|
}
|
||||||
|
|
||||||
|
const recipientUser = {
|
||||||
|
allowEmbedIframes: false,
|
||||||
|
createdAt: '2025-04-30T00:16:49.716Z',
|
||||||
|
deleted: false,
|
||||||
|
disabled: false,
|
||||||
|
emailNotificationsChatMessage: true,
|
||||||
|
emailNotificationsCommentOnObservedPost: true,
|
||||||
|
emailNotificationsFollowingUsers: true,
|
||||||
|
emailNotificationsGroupMemberJoined: true,
|
||||||
|
emailNotificationsGroupMemberLeft: true,
|
||||||
|
emailNotificationsGroupMemberRemoved: true,
|
||||||
|
emailNotificationsGroupMemberRoleChanged: true,
|
||||||
|
emailNotificationsMention: true,
|
||||||
|
emailNotificationsPostInGroup: true,
|
||||||
|
encryptedPassword: '$2b$10$KOrCHvEB5CM7D.P3VcX2z.pSSBZKZhPqHW/QKym6V1S6fiG..xtBq',
|
||||||
|
id: 'chatReceiver',
|
||||||
|
locale: 'en',
|
||||||
|
name: 'chatReceiver',
|
||||||
|
role: 'user',
|
||||||
|
showShoutsPublicly: false,
|
||||||
|
slug: 'chatreceiver',
|
||||||
|
termsAndConditionsAgreedAt: '2019-08-01T10:47:19.212Z',
|
||||||
|
termsAndConditionsAgreedVersion: '0.0.1',
|
||||||
|
updatedAt: '2025-04-30T00:16:49.716Z',
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('sendChatMessageMail', () => {
|
||||||
|
describe('English', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
recipientUser.locale = 'en'
|
||||||
|
})
|
||||||
|
|
||||||
|
it('chat_message template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendChatMessageMail({
|
||||||
|
email: 'user@example.org',
|
||||||
|
senderUser,
|
||||||
|
recipientUser,
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('German', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
recipientUser.locale = 'de'
|
||||||
|
})
|
||||||
|
|
||||||
|
it('chat_message template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendChatMessageMail({
|
||||||
|
email: 'user@example.org',
|
||||||
|
senderUser,
|
||||||
|
recipientUser,
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
204
backend/src/emails/sendEmail.ts
Normal file
204
backend/src/emails/sendEmail.ts
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||||
|
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||||
|
import path from 'node:path'
|
||||||
|
|
||||||
|
import Email from 'email-templates'
|
||||||
|
import { createTransport } from 'nodemailer'
|
||||||
|
// import type Email as EmailType from '@types/email-templates'
|
||||||
|
|
||||||
|
import CONFIG from '@config/index'
|
||||||
|
import logosWebapp from '@config/logos'
|
||||||
|
import metadata from '@config/metadata'
|
||||||
|
import { UserDbProperties } from '@db/types/User'
|
||||||
|
|
||||||
|
const hasAuthData = CONFIG.SMTP_USERNAME && CONFIG.SMTP_PASSWORD
|
||||||
|
const hasDKIMData =
|
||||||
|
CONFIG.SMTP_DKIM_DOMAINNAME && CONFIG.SMTP_DKIM_KEYSELECTOR && CONFIG.SMTP_DKIM_PRIVATKEY
|
||||||
|
|
||||||
|
const welcomeImageUrl = new URL(logosWebapp.LOGO_WELCOME_PATH, CONFIG.CLIENT_URI)
|
||||||
|
const settingsUrl = new URL('/settings/notifications', CONFIG.CLIENT_URI)
|
||||||
|
|
||||||
|
const defaultParams = {
|
||||||
|
welcomeImageUrl,
|
||||||
|
APPLICATION_NAME: CONFIG.APPLICATION_NAME,
|
||||||
|
ORGANIZATION_NAME: metadata.ORGANIZATION_NAME,
|
||||||
|
ORGANIZATION_URL: CONFIG.ORGANIZATION_URL,
|
||||||
|
supportUrl: CONFIG.SUPPORT_URL,
|
||||||
|
settingsUrl,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const transport = createTransport({
|
||||||
|
host: CONFIG.SMTP_HOST,
|
||||||
|
port: CONFIG.SMTP_PORT,
|
||||||
|
ignoreTLS: CONFIG.SMTP_IGNORE_TLS,
|
||||||
|
secure: CONFIG.SMTP_SECURE, // true for 465, false for other ports
|
||||||
|
pool: true,
|
||||||
|
maxConnections: CONFIG.SMTP_MAX_CONNECTIONS,
|
||||||
|
maxMessages: CONFIG.SMTP_MAX_MESSAGES,
|
||||||
|
auth: hasAuthData && {
|
||||||
|
user: CONFIG.SMTP_USERNAME,
|
||||||
|
pass: CONFIG.SMTP_PASSWORD,
|
||||||
|
},
|
||||||
|
dkim: hasDKIMData && {
|
||||||
|
domainName: CONFIG.SMTP_DKIM_DOMAINNAME,
|
||||||
|
keySelector: CONFIG.SMTP_DKIM_KEYSELECTOR,
|
||||||
|
privateKey: CONFIG.SMTP_DKIM_PRIVATKEY,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const email = new Email({
|
||||||
|
message: {
|
||||||
|
from: `${CONFIG.APPLICATION_NAME}`,
|
||||||
|
},
|
||||||
|
transport,
|
||||||
|
i18n: {
|
||||||
|
locales: ['en', 'de'],
|
||||||
|
defaultLocale: 'en',
|
||||||
|
retryInDefaultLocale: false,
|
||||||
|
directory: path.join(__dirname, 'locales'),
|
||||||
|
updateFiles: false,
|
||||||
|
objectNotation: true,
|
||||||
|
mustacheConfig: {
|
||||||
|
tags: ['{', '}'],
|
||||||
|
disable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
send: CONFIG.SEND_MAIL,
|
||||||
|
preview: false,
|
||||||
|
// This is very useful to see the emails sent by the unit tests
|
||||||
|
/*
|
||||||
|
preview: {
|
||||||
|
open: {
|
||||||
|
app: 'brave-browser',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
})
|
||||||
|
|
||||||
|
interface OriginalMessage {
|
||||||
|
to: string
|
||||||
|
from: string
|
||||||
|
attachments: string[]
|
||||||
|
subject: string
|
||||||
|
html: string
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export const sendNotificationMail = async (notification: any): Promise<OriginalMessage> => {
|
||||||
|
const locale = notification?.to?.locale
|
||||||
|
const to = notification?.email
|
||||||
|
const name = notification?.to?.name
|
||||||
|
const template = notification?.reason
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { originalMessage } = await email.send({
|
||||||
|
template: path.join(__dirname, 'templates', template),
|
||||||
|
message: {
|
||||||
|
to,
|
||||||
|
},
|
||||||
|
locals: {
|
||||||
|
...defaultParams,
|
||||||
|
locale,
|
||||||
|
name,
|
||||||
|
postTitle:
|
||||||
|
notification?.from?.__typename === 'Comment'
|
||||||
|
? notification?.from?.post?.title
|
||||||
|
: notification?.from?.title,
|
||||||
|
postUrl: new URL(
|
||||||
|
notification?.from?.__typename === 'Comment'
|
||||||
|
? `/post/${notification?.from?.post?.id}/${notification?.from?.post?.slug}`
|
||||||
|
: `/post/${notification?.from?.id}/${notification?.from?.slug}`,
|
||||||
|
CONFIG.CLIENT_URI,
|
||||||
|
),
|
||||||
|
postAuthorName:
|
||||||
|
notification?.from?.__typename === 'Comment'
|
||||||
|
? undefined
|
||||||
|
: notification?.from?.author?.name,
|
||||||
|
postAuthorUrl:
|
||||||
|
notification?.from?.__typename === 'Comment'
|
||||||
|
? undefined
|
||||||
|
: new URL(
|
||||||
|
`user/${notification?.from?.author?.id}/${notification?.from?.author?.slug}`,
|
||||||
|
CONFIG.CLIENT_URI,
|
||||||
|
),
|
||||||
|
commenterName:
|
||||||
|
notification?.from?.__typename === 'Comment'
|
||||||
|
? notification?.from?.author?.name
|
||||||
|
: undefined,
|
||||||
|
commenterUrl:
|
||||||
|
notification?.from?.__typename === 'Comment'
|
||||||
|
? new URL(
|
||||||
|
`/user/${notification?.from?.author?.id}/${notification?.from?.author?.slug}`,
|
||||||
|
CONFIG.CLIENT_URI,
|
||||||
|
)
|
||||||
|
: undefined,
|
||||||
|
commentUrl:
|
||||||
|
notification?.from?.__typename === 'Comment'
|
||||||
|
? new URL(
|
||||||
|
`/post/${notification?.from?.post?.id}/${notification?.from?.post?.slug}#commentId-${notification?.from?.id}`,
|
||||||
|
CONFIG.CLIENT_URI,
|
||||||
|
)
|
||||||
|
: undefined,
|
||||||
|
// chattingUser: 'SR-71',
|
||||||
|
// chatUrl: new URL('/chat', CONFIG.CLIENT_URI),
|
||||||
|
groupUrl:
|
||||||
|
notification?.from?.__typename === 'Group'
|
||||||
|
? new URL(
|
||||||
|
`/group/${notification?.from?.id}/${notification?.from?.slug}`,
|
||||||
|
CONFIG.CLIENT_URI,
|
||||||
|
)
|
||||||
|
: undefined,
|
||||||
|
groupName:
|
||||||
|
notification?.from?.__typename === 'Group' ? notification?.from?.name : undefined,
|
||||||
|
groupRelatedUserName:
|
||||||
|
notification?.from?.__typename === 'Group' ? notification?.relatedUser?.name : undefined,
|
||||||
|
groupRelatedUserUrl:
|
||||||
|
notification?.from?.__typename === 'Group'
|
||||||
|
? new URL(
|
||||||
|
`/user/${notification?.relatedUser?.id}/${notification?.relatedUser?.slug}`,
|
||||||
|
CONFIG.CLIENT_URI,
|
||||||
|
)
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return originalMessage as OriginalMessage
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChatMessageEmailInput {
|
||||||
|
senderUser: UserDbProperties
|
||||||
|
recipientUser: UserDbProperties
|
||||||
|
email: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sendChatMessageMail = async (
|
||||||
|
data: ChatMessageEmailInput,
|
||||||
|
): Promise<OriginalMessage> => {
|
||||||
|
const { senderUser, recipientUser } = data
|
||||||
|
const to = data.email
|
||||||
|
try {
|
||||||
|
const { originalMessage } = await email.send({
|
||||||
|
template: path.join(__dirname, 'templates', 'chat_message'),
|
||||||
|
message: {
|
||||||
|
to,
|
||||||
|
},
|
||||||
|
locals: {
|
||||||
|
...defaultParams,
|
||||||
|
locale: recipientUser.locale,
|
||||||
|
name: recipientUser.name,
|
||||||
|
chattingUser: senderUser.name,
|
||||||
|
chattingUserUrl: new URL(`/user/${senderUser.id}/${senderUser.slug}`, CONFIG.CLIENT_URI),
|
||||||
|
chatUrl: new URL('/chat', CONFIG.CLIENT_URI),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return originalMessage as OriginalMessage
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
475
backend/src/emails/sendNotificationMail.spec.ts
Normal file
475
backend/src/emails/sendNotificationMail.spec.ts
Normal file
@ -0,0 +1,475 @@
|
|||||||
|
import { sendNotificationMail } from './sendEmail'
|
||||||
|
|
||||||
|
describe('sendNotificationMail', () => {
|
||||||
|
let locale = 'en'
|
||||||
|
|
||||||
|
describe('English', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
locale = 'en'
|
||||||
|
})
|
||||||
|
|
||||||
|
it('followed_user_posted template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'followed_user_posted',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
id: 'p1',
|
||||||
|
slug: 'new-post',
|
||||||
|
title: 'New Post',
|
||||||
|
author: {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Peter Lustig',
|
||||||
|
slug: 'peter-lustig',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('post_in_group template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'post_in_group',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
id: 'p1',
|
||||||
|
slug: 'new-post',
|
||||||
|
title: 'New Post',
|
||||||
|
author: {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Peter Lustig',
|
||||||
|
slug: 'peter-lustig',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('mentioned_in_post template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'mentioned_in_post',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
id: 'p1',
|
||||||
|
slug: 'new-post',
|
||||||
|
title: 'New Post',
|
||||||
|
author: {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Peter Lustig',
|
||||||
|
slug: 'peter-lustig',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('commented_on_post template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'commented_on_post',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
__typename: 'Comment',
|
||||||
|
id: 'c1',
|
||||||
|
slug: 'new-comment',
|
||||||
|
author: {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Peter Lustig',
|
||||||
|
slug: 'peter-lustig',
|
||||||
|
},
|
||||||
|
post: {
|
||||||
|
id: 'p1',
|
||||||
|
slug: 'new-post',
|
||||||
|
title: 'New Post',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('mentioned_in_comment template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'mentioned_in_comment',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
__typename: 'Comment',
|
||||||
|
id: 'c1',
|
||||||
|
slug: 'new-comment',
|
||||||
|
author: {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Peter Lustig',
|
||||||
|
slug: 'peter-lustig',
|
||||||
|
},
|
||||||
|
post: {
|
||||||
|
id: 'p1',
|
||||||
|
slug: 'new-post',
|
||||||
|
title: 'New Post',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('changed_group_member_role template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'changed_group_member_role',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
__typename: 'Group',
|
||||||
|
id: 'g1',
|
||||||
|
slug: 'the-group',
|
||||||
|
name: 'The Group',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('user_joined_group template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'user_joined_group',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
__typename: 'Group',
|
||||||
|
id: 'g1',
|
||||||
|
slug: 'the-group',
|
||||||
|
name: 'The Group',
|
||||||
|
},
|
||||||
|
relatedUser: {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Peter Lustig',
|
||||||
|
slug: 'peter-lustig',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('user_left_group template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'user_left_group',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
__typename: 'Group',
|
||||||
|
id: 'g1',
|
||||||
|
slug: 'the-group',
|
||||||
|
name: 'The Group',
|
||||||
|
},
|
||||||
|
relatedUser: {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Peter Lustig',
|
||||||
|
slug: 'peter-lustig',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('removed_user_from_group template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'removed_user_from_group',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
__typename: 'Group',
|
||||||
|
id: 'g1',
|
||||||
|
slug: 'the-group',
|
||||||
|
name: 'The Group',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('German', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
locale = 'de'
|
||||||
|
})
|
||||||
|
|
||||||
|
it('followed_user_posted template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'followed_user_posted',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
id: 'p1',
|
||||||
|
slug: 'new-post',
|
||||||
|
title: 'New Post',
|
||||||
|
author: {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Peter Lustig',
|
||||||
|
slug: 'peter-lustig',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('post_in_group template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'post_in_group',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
id: 'p1',
|
||||||
|
slug: 'new-post',
|
||||||
|
title: 'New Post',
|
||||||
|
author: {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Peter Lustig',
|
||||||
|
slug: 'peter-lustig',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('mentioned_in_post template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'mentioned_in_post',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
id: 'p1',
|
||||||
|
slug: 'new-post',
|
||||||
|
title: 'New Post',
|
||||||
|
author: {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Peter Lustig',
|
||||||
|
slug: 'peter-lustig',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('commented_on_post template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'commented_on_post',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
__typename: 'Comment',
|
||||||
|
id: 'c1',
|
||||||
|
slug: 'new-comment',
|
||||||
|
author: {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Peter Lustig',
|
||||||
|
slug: 'peter-lustig',
|
||||||
|
},
|
||||||
|
post: {
|
||||||
|
id: 'p1',
|
||||||
|
slug: 'new-post',
|
||||||
|
title: 'New Post',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('mentioned_in_comment template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'mentioned_in_comment',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
__typename: 'Comment',
|
||||||
|
id: 'c1',
|
||||||
|
slug: 'new-comment',
|
||||||
|
author: {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Peter Lustig',
|
||||||
|
slug: 'peter-lustig',
|
||||||
|
},
|
||||||
|
post: {
|
||||||
|
id: 'p1',
|
||||||
|
slug: 'new-post',
|
||||||
|
title: 'New Post',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('changed_group_member_role template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'changed_group_member_role',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
__typename: 'Group',
|
||||||
|
id: 'g1',
|
||||||
|
slug: 'the-group',
|
||||||
|
name: 'The Group',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('user_joined_group template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'user_joined_group',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
__typename: 'Group',
|
||||||
|
id: 'g1',
|
||||||
|
slug: 'the-group',
|
||||||
|
name: 'The Group',
|
||||||
|
},
|
||||||
|
relatedUser: {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Peter Lustig',
|
||||||
|
slug: 'peter-lustig',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('user_left_group template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'user_left_group',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
__typename: 'Group',
|
||||||
|
id: 'g1',
|
||||||
|
slug: 'the-group',
|
||||||
|
name: 'The Group',
|
||||||
|
},
|
||||||
|
relatedUser: {
|
||||||
|
id: 'u2',
|
||||||
|
name: 'Peter Lustig',
|
||||||
|
slug: 'peter-lustig',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('removed_user_from_group template', async () => {
|
||||||
|
await expect(
|
||||||
|
sendNotificationMail({
|
||||||
|
reason: 'removed_user_from_group',
|
||||||
|
email: 'user@example.org',
|
||||||
|
to: {
|
||||||
|
name: 'Jenny Rostock',
|
||||||
|
id: 'u1',
|
||||||
|
slug: 'jenny-rostock',
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
__typename: 'Group',
|
||||||
|
id: 'g1',
|
||||||
|
slug: 'the-group',
|
||||||
|
name: 'The Group',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchSnapshot()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
extend ../layout.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
.content
|
||||||
|
- var groupUrl = groupUrl
|
||||||
|
p= t('changedGroupMemberRole', { groupName })
|
||||||
|
a.button(href=groupUrl)= t('buttons.viewGroup')
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.changedGroupMemberRole')}`
|
||||||
8
backend/src/emails/templates/chat_message/html.pug
Normal file
8
backend/src/emails/templates/chat_message/html.pug
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
extend ../layout.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
.content
|
||||||
|
p= t('chatMessageStart')
|
||||||
|
a.user(href=chattingUserUrl)= chattingUser
|
||||||
|
= t('chatMessageEnd')
|
||||||
|
a.button(href=chatUrl)= t('buttons.viewChat')
|
||||||
1
backend/src/emails/templates/chat_message/subject.pug
Normal file
1
backend/src/emails/templates/chat_message/subject.pug
Normal file
@ -0,0 +1 @@
|
|||||||
|
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.chatMessage')}`
|
||||||
8
backend/src/emails/templates/commented_on_post/html.pug
Normal file
8
backend/src/emails/templates/commented_on_post/html.pug
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
extend ../layout.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
.content
|
||||||
|
p
|
||||||
|
a.user(href=commenterUrl)= commenterName
|
||||||
|
= t('commentedOnPost', { postTitle})
|
||||||
|
a.button(href=commentUrl)= t('buttons.viewComment')
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.commentedOnPost')}`
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
extend ../layout.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
.content
|
||||||
|
p
|
||||||
|
a.user(href=postAuthorUrl)= postAuthorName
|
||||||
|
= t('followedUserPosted', { postTitle })
|
||||||
|
a.button(href=postUrl)= t('buttons.viewPost')
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.followedUserPosted')}`
|
||||||
5
backend/src/emails/templates/includes/footer.pug
Normal file
5
backend/src/emails/templates/includes/footer.pug
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
footer
|
||||||
|
.footer
|
||||||
|
- var organizationUrl = ORGANIZATION_URL
|
||||||
|
- var organizationName = ORGANIZATION_NAME
|
||||||
|
a(href=organizationUrl)= organizationName
|
||||||
14
backend/src/emails/templates/includes/greeting.pug
Normal file
14
backend/src/emails/templates/includes/greeting.pug
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//- This sets the greeting at the end of every e-mail
|
||||||
|
.text-block
|
||||||
|
- var organizationUrl = ORGANIZATION_URL
|
||||||
|
- var team = APPLICATION_NAME
|
||||||
|
- var settingsUrl = settingsUrl
|
||||||
|
p= t('general.seeYou')
|
||||||
|
a.organization(href=organizationUrl)= team
|
||||||
|
| !
|
||||||
|
p= t('general.yourTeam', { team })
|
||||||
|
br
|
||||||
|
p= t('general.settingsHint')
|
||||||
|
a.settings(href=settingsUrl)= t('general.settingsName')
|
||||||
|
| !
|
||||||
|
|
||||||
9
backend/src/emails/templates/includes/header.pug
Normal file
9
backend/src/emails/templates/includes/header.pug
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
header
|
||||||
|
.head
|
||||||
|
- var img = welcomeImageUrl
|
||||||
|
img.head-logo(
|
||||||
|
alt="Welcome Image"
|
||||||
|
loading="lazy"
|
||||||
|
src=img
|
||||||
|
)
|
||||||
|
|
||||||
1
backend/src/emails/templates/includes/salutation.pug
Normal file
1
backend/src/emails/templates/includes/salutation.pug
Normal file
@ -0,0 +1 @@
|
|||||||
|
h2= `${t('general.greeting')} ${name},`
|
||||||
65
backend/src/emails/templates/includes/webflow.css
Normal file
65
backend/src/emails/templates/includes/webflow.css
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
body{
|
||||||
|
display: block;
|
||||||
|
font-family: Lato, sans-serif;
|
||||||
|
font-size: 17px;
|
||||||
|
text-align: left;
|
||||||
|
text-align: -webkit-left;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-top: 25px;
|
||||||
|
font-size: 25px;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 22px;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 680px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head-logo {
|
||||||
|
width: 60%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #17b53e;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.button {
|
||||||
|
background: #17b53e;
|
||||||
|
font-family: Lato, sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 15px;
|
||||||
|
text-decoration: none;
|
||||||
|
text-align:center;
|
||||||
|
padding: 13px 17px;
|
||||||
|
color: #ffffff;
|
||||||
|
display: table;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-block {
|
||||||
|
margin-top: 20px;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
padding: 20px;
|
||||||
|
font-family: Lato, sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 15px;
|
||||||
|
text-align: center;
|
||||||
|
color: #888888;
|
||||||
|
}
|
||||||
26
backend/src/emails/templates/layout.pug
Normal file
26
backend/src/emails/templates/layout.pug
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
doctype html
|
||||||
|
html(lang=locale)
|
||||||
|
head
|
||||||
|
meta(
|
||||||
|
content="multipart/html; charset=UTF-8"
|
||||||
|
http-equiv="content-type"
|
||||||
|
)
|
||||||
|
meta(
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1"
|
||||||
|
)
|
||||||
|
style.
|
||||||
|
.wf-force-outline-none[tabindex="-1"]:focus{outline:none;}
|
||||||
|
style
|
||||||
|
include includes/webflow.css
|
||||||
|
|
||||||
|
body
|
||||||
|
div.container
|
||||||
|
include includes/header.pug
|
||||||
|
include includes/salutation.pug
|
||||||
|
|
||||||
|
.wrapper
|
||||||
|
block content
|
||||||
|
include includes/greeting.pug
|
||||||
|
|
||||||
|
include includes/footer.pug
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
extend ../layout.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
.content
|
||||||
|
p
|
||||||
|
a.user(href=commenterUrl)= commenterName
|
||||||
|
= t('mentionedInComment', { postTitle})
|
||||||
|
a.button(href=commentUrl)= t('buttons.viewComment')
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.mentionedInComment')}`
|
||||||
8
backend/src/emails/templates/mentioned_in_post/html.pug
Normal file
8
backend/src/emails/templates/mentioned_in_post/html.pug
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
extend ../layout.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
.content
|
||||||
|
p
|
||||||
|
a.user(href=postAuthorUrl)= postAuthorName
|
||||||
|
= t('mentionedInPost', { postTitle })
|
||||||
|
a.button(href=postUrl)= t('buttons.viewPost')
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.mentionedInPost')}`
|
||||||
7
backend/src/emails/templates/post_in_group/html.pug
Normal file
7
backend/src/emails/templates/post_in_group/html.pug
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
extend ../layout.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
.content
|
||||||
|
- var postUrl = postUrl
|
||||||
|
p= t('postInGroup', { postTitle})
|
||||||
|
a.button(href=postUrl)= t('buttons.viewPost')
|
||||||
1
backend/src/emails/templates/post_in_group/subject.pug
Normal file
1
backend/src/emails/templates/post_in_group/subject.pug
Normal file
@ -0,0 +1 @@
|
|||||||
|
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.postInGroup')}`
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
extend ../layout.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
.content
|
||||||
|
p= t('removedUserFromGroup', { groupName })
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.removedUserFromGroup')}`
|
||||||
8
backend/src/emails/templates/user_joined_group/html.pug
Normal file
8
backend/src/emails/templates/user_joined_group/html.pug
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
extend ../layout.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
.content
|
||||||
|
p
|
||||||
|
a.user(href=groupRelatedUserUrl)= groupRelatedUserName
|
||||||
|
= t('userJoinedGroup', { groupName })
|
||||||
|
a.button(href=groupUrl)= t('buttons.viewGroup')
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.userJoinedGroup')}`
|
||||||
8
backend/src/emails/templates/user_left_group/html.pug
Normal file
8
backend/src/emails/templates/user_left_group/html.pug
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
extend ../layout.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
.content
|
||||||
|
p
|
||||||
|
a.user(href=groupRelatedUserUrl)= groupRelatedUserName
|
||||||
|
= t('userLeftGroup', { groupName })
|
||||||
|
a.button(href=groupUrl)= t('buttons.viewGroup')
|
||||||
1
backend/src/emails/templates/user_left_group/subject.pug
Normal file
1
backend/src/emails/templates/user_left_group/subject.pug
Normal file
@ -0,0 +1 @@
|
|||||||
|
= `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.userLeftGroup')}`
|
||||||
@ -16,9 +16,9 @@ import createServer, { getContext } from '@src/server'
|
|||||||
|
|
||||||
CONFIG.CATEGORIES_ACTIVE = false
|
CONFIG.CATEGORIES_ACTIVE = false
|
||||||
|
|
||||||
const sendMailMock: (notification) => void = jest.fn()
|
const sendNotificationMailMock: (notification) => void = jest.fn()
|
||||||
jest.mock('@middleware/helpers/email/sendMail', () => ({
|
jest.mock('@src/emails/sendEmail', () => ({
|
||||||
sendMail: (notification) => sendMailMock(notification),
|
sendNotificationMail: (notification) => sendNotificationMailMock(notification),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let query, mutate, authenticatedUser, emaillessMember
|
let query, mutate, authenticatedUser, emaillessMember
|
||||||
@ -208,7 +208,13 @@ describe('emails sent for notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends only one email', () => {
|
it('sends only one email', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
reason: 'mentioned_in_post',
|
||||||
|
email: 'group.member@example.org',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sends 3 notifications', async () => {
|
it('sends 3 notifications', async () => {
|
||||||
@ -280,7 +286,13 @@ describe('emails sent for notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends only one email', () => {
|
it('sends only one email', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
reason: 'followed_user_posted',
|
||||||
|
email: 'group.member@example.org',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sends 3 notifications', async () => {
|
it('sends 3 notifications', async () => {
|
||||||
@ -353,7 +365,13 @@ describe('emails sent for notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends only one email', () => {
|
it('sends only one email', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
reason: 'post_in_group',
|
||||||
|
email: 'group.member@example.org',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sends 3 notifications', async () => {
|
it('sends 3 notifications', async () => {
|
||||||
@ -427,7 +445,7 @@ describe('emails sent for notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends NO email', () => {
|
it('sends NO email', () => {
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendNotificationMailMock).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sends 3 notifications', async () => {
|
it('sends 3 notifications', async () => {
|
||||||
@ -521,7 +539,13 @@ describe('emails sent for notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends only one email', () => {
|
it('sends only one email', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
reason: 'mentioned_in_comment',
|
||||||
|
email: 'group.member@example.org',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sends 2 notifications', async () => {
|
it('sends 2 notifications', async () => {
|
||||||
@ -603,7 +627,13 @@ describe('emails sent for notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends only one email', () => {
|
it('sends only one email', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
reason: 'mentioned_in_comment',
|
||||||
|
email: 'group.member@example.org',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sends 2 notifications', async () => {
|
it('sends 2 notifications', async () => {
|
||||||
@ -686,7 +716,7 @@ describe('emails sent for notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends NO email', () => {
|
it('sends NO email', () => {
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendNotificationMailMock).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sends 2 notifications', async () => {
|
it('sends 2 notifications', async () => {
|
||||||
|
|||||||
@ -14,9 +14,9 @@ import createServer, { getContext } from '@src/server'
|
|||||||
|
|
||||||
CONFIG.CATEGORIES_ACTIVE = false
|
CONFIG.CATEGORIES_ACTIVE = false
|
||||||
|
|
||||||
const sendMailMock: (notification) => void = jest.fn()
|
const sendNotificationMailMock: (notification) => void = jest.fn()
|
||||||
jest.mock('@middleware/helpers/email/sendMail', () => ({
|
jest.mock('@src/emails/sendEmail', () => ({
|
||||||
sendMail: (notification) => sendMailMock(notification),
|
sendNotificationMail: (notification) => sendNotificationMailMock(notification),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let query, mutate, authenticatedUser
|
let query, mutate, authenticatedUser
|
||||||
@ -268,17 +268,17 @@ describe('following users notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends only two emails, as second follower has emails disabled and email-less follower has no email', () => {
|
it('sends only two emails, as second follower has emails disabled and email-less follower has no email', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(2)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(2)
|
||||||
expect(sendMailMock).toHaveBeenCalledWith(
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
html: expect.stringContaining('Hello First Follower'),
|
email: 'first-follower@example.org',
|
||||||
to: 'first-follower@example.org',
|
reason: 'followed_user_posted',
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
expect(sendMailMock).toHaveBeenCalledWith(
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
html: expect.stringContaining('Hello Third Follower'),
|
email: 'third-follower@example.org',
|
||||||
to: 'third-follower@example.org',
|
reason: 'followed_user_posted',
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -17,9 +17,9 @@ import createServer, { getContext } from '@src/server'
|
|||||||
|
|
||||||
CONFIG.CATEGORIES_ACTIVE = false
|
CONFIG.CATEGORIES_ACTIVE = false
|
||||||
|
|
||||||
const sendMailMock: (notification) => void = jest.fn()
|
const sendNotificationMailMock: (notification) => void = jest.fn()
|
||||||
jest.mock('@middleware/helpers/email/sendMail', () => ({
|
jest.mock('@src/emails/sendEmail', () => ({
|
||||||
sendMail: (notification) => sendMailMock(notification),
|
sendNotificationMail: (notification) => sendNotificationMailMock(notification),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let query, mutate, authenticatedUser
|
let query, mutate, authenticatedUser
|
||||||
@ -394,7 +394,25 @@ describe('mentions in groups', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends only 3 emails, one for each user with an email', () => {
|
it('sends only 3 emails, one for each user with an email', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(3)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(3)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: 'group.member@example.org',
|
||||||
|
reason: 'mentioned_in_post',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: 'no.member@example.org',
|
||||||
|
reason: 'mentioned_in_post',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: 'pending.member@example.org',
|
||||||
|
reason: 'mentioned_in_post',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -490,7 +508,13 @@ describe('mentions in groups', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends only 1 email', () => {
|
it('sends only 1 email', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: 'group.member@example.org',
|
||||||
|
reason: 'mentioned_in_post',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -586,7 +610,13 @@ describe('mentions in groups', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends only 1 email', () => {
|
it('sends only 1 email', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: 'group.member@example.org',
|
||||||
|
reason: 'mentioned_in_post',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -670,7 +700,19 @@ describe('mentions in groups', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends 2 emails', () => {
|
it('sends 2 emails', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(3)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(3)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: 'group.member@example.org',
|
||||||
|
reason: 'mentioned_in_comment',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: 'no.member@example.org',
|
||||||
|
reason: 'mentioned_in_comment',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -761,7 +803,13 @@ describe('mentions in groups', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends 1 email', () => {
|
it('sends 1 email', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: 'group.member@example.org',
|
||||||
|
reason: 'mentioned_in_comment',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -852,7 +900,13 @@ describe('mentions in groups', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends 1 email', () => {
|
it('sends 1 email', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: 'group.member@example.org',
|
||||||
|
reason: 'mentioned_in_comment',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -14,9 +14,9 @@ import createServer, { getContext } from '@src/server'
|
|||||||
|
|
||||||
CONFIG.CATEGORIES_ACTIVE = false
|
CONFIG.CATEGORIES_ACTIVE = false
|
||||||
|
|
||||||
const sendMailMock: (notification) => void = jest.fn()
|
const sendNotificationMailMock: (notification) => void = jest.fn()
|
||||||
jest.mock('@middleware/helpers/email/sendMail', () => ({
|
jest.mock('@src/emails/sendEmail', () => ({
|
||||||
sendMail: (notification) => sendMailMock(notification),
|
sendNotificationMail: (notification) => sendNotificationMailMock(notification),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let query, mutate, authenticatedUser
|
let query, mutate, authenticatedUser
|
||||||
@ -213,10 +213,11 @@ describe('notifications for users that observe a post', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends one email', () => {
|
it('sends one email', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
expect(sendMailMock).toHaveBeenCalledWith(
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
to: 'post-author@example.org',
|
email: 'post-author@example.org',
|
||||||
|
reason: 'commented_on_post',
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -303,15 +304,17 @@ describe('notifications for users that observe a post', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends two emails', () => {
|
it('sends two emails', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(2)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(2)
|
||||||
expect(sendMailMock).toHaveBeenCalledWith(
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
to: 'post-author@example.org',
|
email: 'post-author@example.org',
|
||||||
|
reason: 'commented_on_post',
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
expect(sendMailMock).toHaveBeenCalledWith(
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
to: 'first-commenter@example.org',
|
email: 'first-commenter@example.org',
|
||||||
|
reason: 'commented_on_post',
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -417,10 +420,11 @@ describe('notifications for users that observe a post', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends one email', () => {
|
it('sends one email', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
expect(sendMailMock).toHaveBeenCalledWith(
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
to: 'second-commenter@example.org',
|
email: 'second-commenter@example.org',
|
||||||
|
reason: 'commented_on_post',
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -12,9 +12,9 @@ import createServer, { getContext } from '@src/server'
|
|||||||
|
|
||||||
CONFIG.CATEGORIES_ACTIVE = false
|
CONFIG.CATEGORIES_ACTIVE = false
|
||||||
|
|
||||||
const sendMailMock: (notification) => void = jest.fn()
|
const sendNotificationMailMock: (notification) => void = jest.fn()
|
||||||
jest.mock('@middleware/helpers/email/sendMail', () => ({
|
jest.mock('@src/emails/sendEmail', () => ({
|
||||||
sendMail: (notification) => sendMailMock(notification),
|
sendNotificationMail: (notification) => sendNotificationMailMock(notification),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let isUserOnlineMock = jest.fn().mockReturnValue(false)
|
let isUserOnlineMock = jest.fn().mockReturnValue(false)
|
||||||
@ -109,7 +109,7 @@ describe('online status and sending emails', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends NO email to the other user', () => {
|
it('sends NO email to the other user', () => {
|
||||||
expect(sendMailMock).not.toBeCalled()
|
expect(sendNotificationMailMock).not.toBeCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -135,7 +135,7 @@ describe('online status and sending emails', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends email to the other user', () => {
|
it('sends email to the other user', () => {
|
||||||
expect(sendMailMock).toBeCalledTimes(1)
|
expect(sendNotificationMailMock).toBeCalledTimes(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -17,9 +17,9 @@ import createServer, { getContext } from '@src/server'
|
|||||||
|
|
||||||
CONFIG.CATEGORIES_ACTIVE = false
|
CONFIG.CATEGORIES_ACTIVE = false
|
||||||
|
|
||||||
const sendMailMock: (notification) => void = jest.fn()
|
const sendNotificationMailMock: (notification) => void = jest.fn()
|
||||||
jest.mock('@middleware/helpers/email/sendMail', () => ({
|
jest.mock('@src/emails/sendEmail', () => ({
|
||||||
sendMail: (notification) => sendMailMock(notification),
|
sendNotificationMail: (notification) => sendNotificationMailMock(notification),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let query, mutate, authenticatedUser
|
let query, mutate, authenticatedUser
|
||||||
@ -137,7 +137,7 @@ describe('notify group members of new posts in group', () => {
|
|||||||
slug: 'group-member',
|
slug: 'group-member',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
email: 'test2@example.org',
|
email: 'group.member@example.org',
|
||||||
password: '1234',
|
password: '1234',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -295,7 +295,13 @@ describe('notify group members of new posts in group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends one email', () => {
|
it('sends one email', () => {
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
reason: 'post_in_group',
|
||||||
|
email: 'group.member@example.org',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('group member mutes group', () => {
|
describe('group member mutes group', () => {
|
||||||
@ -337,7 +343,7 @@ describe('notify group members of new posts in group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends NO email', () => {
|
it('sends NO email', () => {
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendNotificationMailMock).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('group member unmutes group again but disables email', () => {
|
describe('group member unmutes group again but disables email', () => {
|
||||||
@ -392,7 +398,7 @@ describe('notify group members of new posts in group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends NO email', () => {
|
it('sends NO email', () => {
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendNotificationMailMock).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -433,7 +439,7 @@ describe('notify group members of new posts in group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends NO email', () => {
|
it('sends NO email', () => {
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendNotificationMailMock).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -473,7 +479,7 @@ describe('notify group members of new posts in group', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sends NO email', () => {
|
it('sends NO email', () => {
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendNotificationMailMock).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -20,16 +20,11 @@ import { leaveGroupMutation } from '@graphql/queries/leaveGroupMutation'
|
|||||||
import { removeUserFromGroupMutation } from '@graphql/queries/removeUserFromGroupMutation'
|
import { removeUserFromGroupMutation } from '@graphql/queries/removeUserFromGroupMutation'
|
||||||
import createServer, { getContext } from '@src/server'
|
import createServer, { getContext } from '@src/server'
|
||||||
|
|
||||||
const sendMailMock: (notification) => void = jest.fn()
|
const sendChatMessageMailMock: (notification) => void = jest.fn()
|
||||||
jest.mock('@middleware/helpers/email/sendMail', () => ({
|
const sendNotificationMailMock: (notification) => void = jest.fn()
|
||||||
sendMail: (notification) => sendMailMock(notification),
|
jest.mock('@src/emails/sendEmail', () => ({
|
||||||
}))
|
sendChatMessageMail: (notification) => sendChatMessageMailMock(notification),
|
||||||
|
sendNotificationMail: (notification) => sendNotificationMailMock(notification),
|
||||||
const chatMessageTemplateMock = jest.fn()
|
|
||||||
const notificationTemplateMock = jest.fn()
|
|
||||||
jest.mock('../helpers/email/templateBuilder', () => ({
|
|
||||||
chatMessageTemplate: () => chatMessageTemplateMock(),
|
|
||||||
notificationTemplate: () => notificationTemplateMock(),
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let isUserOnlineMock = jest.fn()
|
let isUserOnlineMock = jest.fn()
|
||||||
@ -240,8 +235,13 @@ describe('notifications', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Mail
|
// Mail
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
expect(notificationTemplateMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
reason: 'commented_on_post',
|
||||||
|
email: 'test@example.org',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('if I have disabled `emailNotificationsCommentOnObservedPost`', () => {
|
describe('if I have disabled `emailNotificationsCommentOnObservedPost`', () => {
|
||||||
@ -276,8 +276,7 @@ describe('notifications', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// No Mail
|
// No Mail
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendNotificationMailMock).not.toHaveBeenCalled()
|
||||||
expect(notificationTemplateMock).not.toHaveBeenCalled()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -398,8 +397,13 @@ describe('notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Mail
|
// Mail
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
expect(notificationTemplateMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
reason: 'mentioned_in_post',
|
||||||
|
email: 'test@example.org',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('if I have disabled `emailNotificationsMention`', () => {
|
describe('if I have disabled `emailNotificationsMention`', () => {
|
||||||
@ -434,8 +438,7 @@ describe('notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Mail
|
// Mail
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendNotificationMailMock).not.toHaveBeenCalled()
|
||||||
expect(notificationTemplateMock).not.toHaveBeenCalled()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -941,8 +944,7 @@ describe('notifications', () => {
|
|||||||
userId: 'chatReceiver',
|
userId: 'chatReceiver',
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendChatMessageMailMock).not.toHaveBeenCalled()
|
||||||
expect(chatMessageTemplateMock).not.toHaveBeenCalled()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -977,8 +979,20 @@ describe('notifications', () => {
|
|||||||
userId: 'chatReceiver',
|
userId: 'chatReceiver',
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendChatMessageMailMock).toHaveBeenCalledTimes(1)
|
||||||
expect(chatMessageTemplateMock).toHaveBeenCalledTimes(1)
|
expect(sendChatMessageMailMock).toHaveBeenCalledWith({
|
||||||
|
email: 'user@example.org',
|
||||||
|
senderUser: expect.objectContaining({
|
||||||
|
name: 'chatSender',
|
||||||
|
slug: 'chatsender',
|
||||||
|
id: 'chatSender',
|
||||||
|
}),
|
||||||
|
recipientUser: expect.objectContaining({
|
||||||
|
name: 'chatReceiver',
|
||||||
|
slug: 'chatreceiver',
|
||||||
|
id: 'chatReceiver',
|
||||||
|
}),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -998,8 +1012,7 @@ describe('notifications', () => {
|
|||||||
expect(pubsubSpy).not.toHaveBeenCalled()
|
expect(pubsubSpy).not.toHaveBeenCalled()
|
||||||
expect(pubsubSpy).not.toHaveBeenCalled()
|
expect(pubsubSpy).not.toHaveBeenCalled()
|
||||||
|
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendChatMessageMailMock).not.toHaveBeenCalled()
|
||||||
expect(chatMessageTemplateMock).not.toHaveBeenCalled()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1019,8 +1032,7 @@ describe('notifications', () => {
|
|||||||
expect(pubsubSpy).not.toHaveBeenCalled()
|
expect(pubsubSpy).not.toHaveBeenCalled()
|
||||||
expect(pubsubSpy).not.toHaveBeenCalled()
|
expect(pubsubSpy).not.toHaveBeenCalled()
|
||||||
|
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendChatMessageMailMock).not.toHaveBeenCalled()
|
||||||
expect(chatMessageTemplateMock).not.toHaveBeenCalled()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1056,8 +1068,7 @@ describe('notifications', () => {
|
|||||||
userId: 'chatReceiver',
|
userId: 'chatReceiver',
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendChatMessageMailMock).not.toHaveBeenCalled()
|
||||||
expect(chatMessageTemplateMock).not.toHaveBeenCalled()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -1137,8 +1148,13 @@ describe('notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Mail
|
// Mail
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
expect(notificationTemplateMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
reason: 'user_joined_group',
|
||||||
|
email: 'owner@example.org',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('if the group owner has disabled `emailNotificationsGroupMemberJoined`', () => {
|
describe('if the group owner has disabled `emailNotificationsGroupMemberJoined`', () => {
|
||||||
@ -1170,8 +1186,7 @@ describe('notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Mail
|
// Mail
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendNotificationMailMock).not.toHaveBeenCalled()
|
||||||
expect(notificationTemplateMock).not.toHaveBeenCalled()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -1240,8 +1255,19 @@ describe('notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Mail
|
// Mail
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(2)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(2)
|
||||||
expect(notificationTemplateMock).toHaveBeenCalledTimes(2)
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
reason: 'user_joined_group',
|
||||||
|
email: 'owner@example.org',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
reason: 'user_left_group',
|
||||||
|
email: 'owner@example.org',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('if the group owner has disabled `emailNotificationsGroupMemberLeft`', () => {
|
describe('if the group owner has disabled `emailNotificationsGroupMemberLeft`', () => {
|
||||||
@ -1285,8 +1311,7 @@ describe('notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Mail
|
// Mail
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
expect(notificationTemplateMock).toHaveBeenCalledTimes(1)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -1345,8 +1370,13 @@ describe('notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Mail
|
// Mail
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
expect(notificationTemplateMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
reason: 'changed_group_member_role',
|
||||||
|
email: 'test@example.org',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('if the group member has disabled `emailNotificationsGroupMemberRoleChanged`', () => {
|
describe('if the group member has disabled `emailNotificationsGroupMemberRoleChanged`', () => {
|
||||||
@ -1378,8 +1408,7 @@ describe('notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Mail
|
// Mail
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendNotificationMailMock).not.toHaveBeenCalled()
|
||||||
expect(notificationTemplateMock).not.toHaveBeenCalled()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -1437,8 +1466,13 @@ describe('notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Mail
|
// Mail
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledTimes(1)
|
||||||
expect(notificationTemplateMock).toHaveBeenCalledTimes(1)
|
expect(sendNotificationMailMock).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
reason: 'removed_user_from_group',
|
||||||
|
email: 'test@example.org',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('if the previous group member has disabled `emailNotificationsGroupMemberRemoved`', () => {
|
describe('if the previous group member has disabled `emailNotificationsGroupMemberRemoved`', () => {
|
||||||
@ -1470,8 +1504,7 @@ describe('notifications', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Mail
|
// Mail
|
||||||
expect(sendMailMock).not.toHaveBeenCalled()
|
expect(sendNotificationMailMock).not.toHaveBeenCalled()
|
||||||
expect(notificationTemplateMock).not.toHaveBeenCalled()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -10,13 +10,9 @@ import {
|
|||||||
CHAT_MESSAGE_ADDED,
|
CHAT_MESSAGE_ADDED,
|
||||||
} from '@constants/subscriptions'
|
} from '@constants/subscriptions'
|
||||||
import { getUnreadRoomsCount } from '@graphql/resolvers/rooms'
|
import { getUnreadRoomsCount } from '@graphql/resolvers/rooms'
|
||||||
import { sendMail } from '@middleware/helpers/email/sendMail'
|
|
||||||
import {
|
|
||||||
chatMessageTemplate,
|
|
||||||
notificationTemplate,
|
|
||||||
} from '@middleware/helpers/email/templateBuilder'
|
|
||||||
import { isUserOnline } from '@middleware/helpers/isUserOnline'
|
import { isUserOnline } from '@middleware/helpers/isUserOnline'
|
||||||
import { validateNotifyUsers } from '@middleware/validation/validationMiddleware'
|
import { validateNotifyUsers } from '@middleware/validation/validationMiddleware'
|
||||||
|
import { sendNotificationMail, sendChatMessageMail } from '@src/emails/sendEmail'
|
||||||
|
|
||||||
import extractMentionedUsers from './mentions/extractMentionedUsers'
|
import extractMentionedUsers from './mentions/extractMentionedUsers'
|
||||||
|
|
||||||
@ -35,12 +31,7 @@ const publishNotifications = async (
|
|||||||
!isUserOnline(notificationAdded.to) &&
|
!isUserOnline(notificationAdded.to) &&
|
||||||
!emailsSent.includes(notificationAdded.email)
|
!emailsSent.includes(notificationAdded.email)
|
||||||
) {
|
) {
|
||||||
sendMail(
|
void sendNotificationMail(notificationAdded)
|
||||||
notificationTemplate({
|
|
||||||
email: notificationAdded.email,
|
|
||||||
variables: { notification: notificationAdded },
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
emailsSent.push(notificationAdded.email)
|
emailsSent.push(notificationAdded.email)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -496,7 +487,7 @@ const handleCreateMessage = async (resolve, root, args, context, resolveInfo) =>
|
|||||||
|
|
||||||
// Send EMail if we found a user(not blocked) and he is not considered online
|
// Send EMail if we found a user(not blocked) and he is not considered online
|
||||||
if (recipientUser.emailNotificationsChatMessage !== false && !isUserOnline(recipientUser)) {
|
if (recipientUser.emailNotificationsChatMessage !== false && !isUserOnline(recipientUser)) {
|
||||||
void sendMail(chatMessageTemplate({ email, variables: { senderUser, recipientUser } }))
|
void sendChatMessageMail({ email, senderUser, recipientUser })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1043
backend/yarn.lock
1043
backend/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user