1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
3 import { expect } from 'chai'
4 import { inspect } from 'util'
5 import { AbuseState, PluginType } from '@shared/models'
6 import { UserNotification, UserNotificationSetting, UserNotificationSettingValue, UserNotificationType } from '../../models/users'
7 import { MockSmtpServer } from '../mock-servers/mock-email'
8 import { PeerTubeServer } from '../server'
9 import { doubleFollow } from '../server/follows'
10 import { createMultipleServers } from '../server/servers'
11 import { setAccessTokensToServers } from './login'
13 function getAllNotificationsSettings (): UserNotificationSetting {
15 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
16 newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
17 abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
18 videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
19 blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
20 myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
21 myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
22 commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
23 newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
24 newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
25 newInstanceFollower: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
26 abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
27 abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
28 autoInstanceFollowing: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
29 newPeerTubeVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
30 newPluginVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
34 type CheckerBaseParams = {
35 server: PeerTubeServer
37 socketNotifications: UserNotification[]
39 check?: { web: boolean, mail: boolean }
42 type CheckerType = 'presence' | 'absence'
44 async function checkNotification (
45 base: CheckerBaseParams,
46 notificationChecker: (notification: UserNotification, type: CheckerType) => void,
47 emailNotificationFinder: (email: object) => boolean,
48 checkType: CheckerType
50 const check = base.check || { web: true, mail: true }
53 const notification = await base.server.notifications.getLastest({ token: base.token })
55 if (notification || checkType !== 'absence') {
56 notificationChecker(notification, checkType)
59 const socketNotification = base.socketNotifications.find(n => {
61 notificationChecker(n, 'presence')
68 if (checkType === 'presence') {
69 const obj = inspect(base.socketNotifications, { depth: 5 })
70 expect(socketNotification, 'The socket notification is absent when it should be present. ' + obj).to.not.be.undefined
72 const obj = inspect(socketNotification, { depth: 5 })
73 expect(socketNotification, 'The socket notification is present when it should not be present. ' + obj).to.be.undefined
79 const email = base.emails
82 .find(e => emailNotificationFinder(e))
84 if (checkType === 'presence') {
85 const emails = base.emails.map(e => e.text)
86 expect(email, 'The email is absent when is should be present. ' + inspect(emails)).to.not.be.undefined
88 expect(email, 'The email is present when is should not be present. ' + inspect(email)).to.be.undefined
93 function checkVideo (video: any, videoName?: string, videoUUID?: string) {
95 expect(video.name).to.be.a('string')
96 expect(video.name).to.not.be.empty
97 expect(video.name).to.equal(videoName)
101 expect(video.uuid).to.be.a('string')
102 expect(video.uuid).to.not.be.empty
103 expect(video.uuid).to.equal(videoUUID)
106 expect(video.id).to.be.a('number')
109 function checkActor (actor: any) {
110 expect(actor.displayName).to.be.a('string')
111 expect(actor.displayName).to.not.be.empty
112 expect(actor.host).to.not.be.undefined
115 function checkComment (comment: any, commentId: number, threadId: number) {
116 expect(comment.id).to.equal(commentId)
117 expect(comment.threadId).to.equal(threadId)
120 async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
121 const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION
123 function notificationChecker (notification: UserNotification, type: CheckerType) {
124 if (type === 'presence') {
125 expect(notification).to.not.be.undefined
126 expect(notification.type).to.equal(notificationType)
128 checkVideo(notification.video, videoName, videoUUID)
129 checkActor(notification.video.channel)
131 expect(notification).to.satisfy((n: UserNotification) => {
132 return n === undefined || n.type !== UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION || n.video.name !== videoName
137 function emailNotificationFinder (email: object) {
138 const text = email['text']
139 return text.indexOf(videoUUID) !== -1 && text.indexOf('Your subscription') !== -1
142 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
145 async function checkVideoIsPublished (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
146 const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED
148 function notificationChecker (notification: UserNotification, type: CheckerType) {
149 if (type === 'presence') {
150 expect(notification).to.not.be.undefined
151 expect(notification.type).to.equal(notificationType)
153 checkVideo(notification.video, videoName, videoUUID)
154 checkActor(notification.video.channel)
156 expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName)
160 function emailNotificationFinder (email: object) {
161 const text: string = email['text']
162 return text.includes(videoUUID) && text.includes('Your video')
165 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
168 async function checkMyVideoImportIsFinished (
169 base: CheckerBaseParams,
176 const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR
178 function notificationChecker (notification: UserNotification, type: CheckerType) {
179 if (type === 'presence') {
180 expect(notification).to.not.be.undefined
181 expect(notification.type).to.equal(notificationType)
183 expect(notification.videoImport.targetUrl).to.equal(url)
185 if (success) checkVideo(notification.videoImport.video, videoName, videoUUID)
187 expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url)
191 function emailNotificationFinder (email: object) {
192 const text: string = email['text']
193 const toFind = success ? ' finished' : ' error'
195 return text.includes(url) && text.includes(toFind)
198 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
201 async function checkUserRegistered (base: CheckerBaseParams, username: string, type: CheckerType) {
202 const notificationType = UserNotificationType.NEW_USER_REGISTRATION
204 function notificationChecker (notification: UserNotification, type: CheckerType) {
205 if (type === 'presence') {
206 expect(notification).to.not.be.undefined
207 expect(notification.type).to.equal(notificationType)
209 checkActor(notification.account)
210 expect(notification.account.name).to.equal(username)
212 expect(notification).to.satisfy(n => n.type !== notificationType || n.account.name !== username)
216 function emailNotificationFinder (email: object) {
217 const text: string = email['text']
219 return text.includes(' registered.') && text.includes(username)
222 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
225 async function checkNewActorFollow (
226 base: CheckerBaseParams,
227 followType: 'channel' | 'account',
228 followerName: string,
229 followerDisplayName: string,
230 followingDisplayName: string,
233 const notificationType = UserNotificationType.NEW_FOLLOW
235 function notificationChecker (notification: UserNotification, type: CheckerType) {
236 if (type === 'presence') {
237 expect(notification).to.not.be.undefined
238 expect(notification.type).to.equal(notificationType)
240 checkActor(notification.actorFollow.follower)
241 expect(notification.actorFollow.follower.displayName).to.equal(followerDisplayName)
242 expect(notification.actorFollow.follower.name).to.equal(followerName)
243 expect(notification.actorFollow.follower.host).to.not.be.undefined
245 const following = notification.actorFollow.following
246 expect(following.displayName).to.equal(followingDisplayName)
247 expect(following.type).to.equal(followType)
249 expect(notification).to.satisfy(n => {
250 return n.type !== notificationType ||
251 (n.actorFollow.follower.name !== followerName && n.actorFollow.following !== followingDisplayName)
256 function emailNotificationFinder (email: object) {
257 const text: string = email['text']
259 return text.includes(followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName)
262 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
265 async function checkNewInstanceFollower (base: CheckerBaseParams, followerHost: string, type: CheckerType) {
266 const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER
268 function notificationChecker (notification: UserNotification, type: CheckerType) {
269 if (type === 'presence') {
270 expect(notification).to.not.be.undefined
271 expect(notification.type).to.equal(notificationType)
273 checkActor(notification.actorFollow.follower)
274 expect(notification.actorFollow.follower.name).to.equal('peertube')
275 expect(notification.actorFollow.follower.host).to.equal(followerHost)
277 expect(notification.actorFollow.following.name).to.equal('peertube')
279 expect(notification).to.satisfy(n => {
280 return n.type !== notificationType || n.actorFollow.follower.host !== followerHost
285 function emailNotificationFinder (email: object) {
286 const text: string = email['text']
288 return text.includes('instance has a new follower') && text.includes(followerHost)
291 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
294 async function checkAutoInstanceFollowing (base: CheckerBaseParams, followerHost: string, followingHost: string, type: CheckerType) {
295 const notificationType = UserNotificationType.AUTO_INSTANCE_FOLLOWING
297 function notificationChecker (notification: UserNotification, type: CheckerType) {
298 if (type === 'presence') {
299 expect(notification).to.not.be.undefined
300 expect(notification.type).to.equal(notificationType)
302 const following = notification.actorFollow.following
303 checkActor(following)
304 expect(following.name).to.equal('peertube')
305 expect(following.host).to.equal(followingHost)
307 expect(notification.actorFollow.follower.name).to.equal('peertube')
308 expect(notification.actorFollow.follower.host).to.equal(followerHost)
310 expect(notification).to.satisfy(n => {
311 return n.type !== notificationType || n.actorFollow.following.host !== followingHost
316 function emailNotificationFinder (email: object) {
317 const text: string = email['text']
319 return text.includes(' automatically followed a new instance') && text.includes(followingHost)
322 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
325 async function checkCommentMention (
326 base: CheckerBaseParams,
330 byAccountDisplayName: string,
333 const notificationType = UserNotificationType.COMMENT_MENTION
335 function notificationChecker (notification: UserNotification, type: CheckerType) {
336 if (type === 'presence') {
337 expect(notification).to.not.be.undefined
338 expect(notification.type).to.equal(notificationType)
340 checkComment(notification.comment, commentId, threadId)
341 checkActor(notification.comment.account)
342 expect(notification.comment.account.displayName).to.equal(byAccountDisplayName)
344 checkVideo(notification.comment.video, undefined, uuid)
346 expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId)
350 function emailNotificationFinder (email: object) {
351 const text: string = email['text']
353 return text.includes(' mentioned ') && text.includes(uuid) && text.includes(byAccountDisplayName)
356 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
359 let lastEmailCount = 0
361 async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) {
362 const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO
364 function notificationChecker (notification: UserNotification, type: CheckerType) {
365 if (type === 'presence') {
366 expect(notification).to.not.be.undefined
367 expect(notification.type).to.equal(notificationType)
369 checkComment(notification.comment, commentId, threadId)
370 checkActor(notification.comment.account)
371 checkVideo(notification.comment.video, undefined, uuid)
373 expect(notification).to.satisfy((n: UserNotification) => {
374 return n === undefined || n.comment === undefined || n.comment.id !== commentId
379 const commentUrl = `http://localhost:${base.server.port}/w/${uuid};threadId=${threadId}`
381 function emailNotificationFinder (email: object) {
382 return email['text'].indexOf(commentUrl) !== -1
385 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
387 if (type === 'presence') {
388 // We cannot detect email duplicates, so check we received another email
389 expect(base.emails).to.have.length.above(lastEmailCount)
390 lastEmailCount = base.emails.length
394 async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
395 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
397 function notificationChecker (notification: UserNotification, type: CheckerType) {
398 if (type === 'presence') {
399 expect(notification).to.not.be.undefined
400 expect(notification.type).to.equal(notificationType)
402 expect(notification.abuse.id).to.be.a('number')
403 checkVideo(notification.abuse.video, videoName, videoUUID)
405 expect(notification).to.satisfy((n: UserNotification) => {
406 return n === undefined || n.abuse === undefined || n.abuse.video.uuid !== videoUUID
411 function emailNotificationFinder (email: object) {
412 const text = email['text']
413 return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1
416 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
419 async function checkNewAbuseMessage (base: CheckerBaseParams, abuseId: number, message: string, toEmail: string, type: CheckerType) {
420 const notificationType = UserNotificationType.ABUSE_NEW_MESSAGE
422 function notificationChecker (notification: UserNotification, type: CheckerType) {
423 if (type === 'presence') {
424 expect(notification).to.not.be.undefined
425 expect(notification.type).to.equal(notificationType)
427 expect(notification.abuse.id).to.equal(abuseId)
429 expect(notification).to.satisfy((n: UserNotification) => {
430 return n === undefined || n.type !== notificationType || n.abuse === undefined || n.abuse.id !== abuseId
435 function emailNotificationFinder (email: object) {
436 const text = email['text']
437 const to = email['to'].filter(t => t.address === toEmail)
439 return text.indexOf(message) !== -1 && to.length !== 0
442 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
445 async function checkAbuseStateChange (base: CheckerBaseParams, abuseId: number, state: AbuseState, type: CheckerType) {
446 const notificationType = UserNotificationType.ABUSE_STATE_CHANGE
448 function notificationChecker (notification: UserNotification, type: CheckerType) {
449 if (type === 'presence') {
450 expect(notification).to.not.be.undefined
451 expect(notification.type).to.equal(notificationType)
453 expect(notification.abuse.id).to.equal(abuseId)
454 expect(notification.abuse.state).to.equal(state)
456 expect(notification).to.satisfy((n: UserNotification) => {
457 return n === undefined || n.abuse === undefined || n.abuse.id !== abuseId
462 function emailNotificationFinder (email: object) {
463 const text = email['text']
465 const contains = state === AbuseState.ACCEPTED
469 return text.indexOf(contains) !== -1
472 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
475 async function checkNewCommentAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
476 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
478 function notificationChecker (notification: UserNotification, type: CheckerType) {
479 if (type === 'presence') {
480 expect(notification).to.not.be.undefined
481 expect(notification.type).to.equal(notificationType)
483 expect(notification.abuse.id).to.be.a('number')
484 checkVideo(notification.abuse.comment.video, videoName, videoUUID)
486 expect(notification).to.satisfy((n: UserNotification) => {
487 return n === undefined || n.abuse === undefined || n.abuse.comment.video.uuid !== videoUUID
492 function emailNotificationFinder (email: object) {
493 const text = email['text']
494 return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1
497 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
500 async function checkNewAccountAbuseForModerators (base: CheckerBaseParams, displayName: string, type: CheckerType) {
501 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
503 function notificationChecker (notification: UserNotification, type: CheckerType) {
504 if (type === 'presence') {
505 expect(notification).to.not.be.undefined
506 expect(notification.type).to.equal(notificationType)
508 expect(notification.abuse.id).to.be.a('number')
509 expect(notification.abuse.account.displayName).to.equal(displayName)
511 expect(notification).to.satisfy((n: UserNotification) => {
512 return n === undefined || n.abuse === undefined || n.abuse.account.displayName !== displayName
517 function emailNotificationFinder (email: object) {
518 const text = email['text']
519 return text.indexOf(displayName) !== -1 && text.indexOf('abuse') !== -1
522 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
525 async function checkVideoAutoBlacklistForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
526 const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS
528 function notificationChecker (notification: UserNotification, type: CheckerType) {
529 if (type === 'presence') {
530 expect(notification).to.not.be.undefined
531 expect(notification.type).to.equal(notificationType)
533 expect(notification.videoBlacklist.video.id).to.be.a('number')
534 checkVideo(notification.videoBlacklist.video, videoName, videoUUID)
536 expect(notification).to.satisfy((n: UserNotification) => {
537 return n === undefined || n.video === undefined || n.video.uuid !== videoUUID
542 function emailNotificationFinder (email: object) {
543 const text = email['text']
544 return text.indexOf(videoUUID) !== -1 && email['text'].indexOf('video-auto-blacklist/list') !== -1
547 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
550 async function checkNewBlacklistOnMyVideo (
551 base: CheckerBaseParams,
554 blacklistType: 'blacklist' | 'unblacklist'
556 const notificationType = blacklistType === 'blacklist'
557 ? UserNotificationType.BLACKLIST_ON_MY_VIDEO
558 : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO
560 function notificationChecker (notification: UserNotification) {
561 expect(notification).to.not.be.undefined
562 expect(notification.type).to.equal(notificationType)
564 const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video
566 checkVideo(video, videoName, videoUUID)
569 function emailNotificationFinder (email: object) {
570 const text = email['text']
571 return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1
574 await checkNotification(base, notificationChecker, emailNotificationFinder, 'presence')
577 async function checkNewPeerTubeVersion (base: CheckerBaseParams, latestVersion: string, type: CheckerType) {
578 const notificationType = UserNotificationType.NEW_PEERTUBE_VERSION
580 function notificationChecker (notification: UserNotification, type: CheckerType) {
581 if (type === 'presence') {
582 expect(notification).to.not.be.undefined
583 expect(notification.type).to.equal(notificationType)
585 expect(notification.peertube).to.exist
586 expect(notification.peertube.latestVersion).to.equal(latestVersion)
588 expect(notification).to.satisfy((n: UserNotification) => {
589 return n === undefined || n.peertube === undefined || n.peertube.latestVersion !== latestVersion
594 function emailNotificationFinder (email: object) {
595 const text = email['text']
597 return text.includes(latestVersion)
600 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
603 async function checkNewPluginVersion (base: CheckerBaseParams, pluginType: PluginType, pluginName: string, type: CheckerType) {
604 const notificationType = UserNotificationType.NEW_PLUGIN_VERSION
606 function notificationChecker (notification: UserNotification, type: CheckerType) {
607 if (type === 'presence') {
608 expect(notification).to.not.be.undefined
609 expect(notification.type).to.equal(notificationType)
611 expect(notification.plugin.name).to.equal(pluginName)
612 expect(notification.plugin.type).to.equal(pluginType)
614 expect(notification).to.satisfy((n: UserNotification) => {
615 return n === undefined || n.plugin === undefined || n.plugin.name !== pluginName
620 function emailNotificationFinder (email: object) {
621 const text = email['text']
623 return text.includes(pluginName)
626 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
629 async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: any = {}) {
630 const userNotifications: UserNotification[] = []
631 const adminNotifications: UserNotification[] = []
632 const adminNotificationsServer2: UserNotification[] = []
633 const emails: object[] = []
635 const port = await MockSmtpServer.Instance.collectEmails(emails)
637 const overrideConfig = {
639 hostname: 'localhost',
646 const servers = await createMultipleServers(serversCount, Object.assign(overrideConfig, overrideConfigArg))
648 await setAccessTokensToServers(servers)
650 if (serversCount > 1) {
651 await doubleFollow(servers[0], servers[1])
654 const user = { username: 'user_1', password: 'super password' }
655 await servers[0].users.create({ ...user, videoQuota: 10 * 1000 * 1000 })
656 const userAccessToken = await servers[0].login.getAccessToken(user)
658 await servers[0].notifications.updateMySettings({ token: userAccessToken, settings: getAllNotificationsSettings() })
659 await servers[0].notifications.updateMySettings({ settings: getAllNotificationsSettings() })
661 if (serversCount > 1) {
662 await servers[1].notifications.updateMySettings({ settings: getAllNotificationsSettings() })
666 const socket = servers[0].socketIO.getUserNotificationSocket({ token: userAccessToken })
667 socket.on('new-notification', n => userNotifications.push(n))
670 const socket = servers[0].socketIO.getUserNotificationSocket()
671 socket.on('new-notification', n => adminNotifications.push(n))
674 if (serversCount > 1) {
675 const socket = servers[1].socketIO.getUserNotificationSocket()
676 socket.on('new-notification', n => adminNotificationsServer2.push(n))
679 const { videoChannels } = await servers[0].users.getMyInfo()
680 const channelId = videoChannels[0].id
685 adminNotificationsServer2,
693 // ---------------------------------------------------------------------------
696 getAllNotificationsSettings,
701 checkMyVideoImportIsFinished,
703 checkAutoInstanceFollowing,
704 checkVideoIsPublished,
705 checkNewVideoFromSubscription,
707 checkNewCommentOnMyVideo,
708 checkNewBlacklistOnMyVideo,
710 checkNewVideoAbuseForModerators,
711 checkVideoAutoBlacklistForModerators,
712 checkNewAbuseMessage,
713 checkAbuseStateChange,
714 checkNewInstanceFollower,
715 prepareNotificationsTest,
716 checkNewCommentAbuseForModerators,
717 checkNewAccountAbuseForModerators,
718 checkNewPeerTubeVersion,
719 checkNewPluginVersion