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 { doubleFollow } from '../server/follows'
9 import { flushAndRunMultipleServers, ServerInfo } from '../server/servers'
10 import { setAccessTokensToServers } from './login'
12 function getAllNotificationsSettings (): UserNotificationSetting {
14 newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
15 newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
16 abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
17 videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
18 blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
19 myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
20 myVideoPublished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
21 commentMention: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
22 newFollow: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
23 newUserRegistration: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
24 newInstanceFollower: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
25 abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
26 abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
27 autoInstanceFollowing: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
28 newPeerTubeVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
29 newPluginVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL
33 type CheckerBaseParams = {
36 socketNotifications: UserNotification[]
38 check?: { web: boolean, mail: boolean }
41 type CheckerType = 'presence' | 'absence'
43 async function checkNotification (
44 base: CheckerBaseParams,
45 notificationChecker: (notification: UserNotification, type: CheckerType) => void,
46 emailNotificationFinder: (email: object) => boolean,
47 checkType: CheckerType
49 const check = base.check || { web: true, mail: true }
52 const notification = await base.server.notifications.getLastest({ token: base.token })
54 if (notification || checkType !== 'absence') {
55 notificationChecker(notification, checkType)
58 const socketNotification = base.socketNotifications.find(n => {
60 notificationChecker(n, 'presence')
67 if (checkType === 'presence') {
68 const obj = inspect(base.socketNotifications, { depth: 5 })
69 expect(socketNotification, 'The socket notification is absent when it should be present. ' + obj).to.not.be.undefined
71 const obj = inspect(socketNotification, { depth: 5 })
72 expect(socketNotification, 'The socket notification is present when it should not be present. ' + obj).to.be.undefined
78 const email = base.emails
81 .find(e => emailNotificationFinder(e))
83 if (checkType === 'presence') {
84 const emails = base.emails.map(e => e.text)
85 expect(email, 'The email is absent when is should be present. ' + inspect(emails)).to.not.be.undefined
87 expect(email, 'The email is present when is should not be present. ' + inspect(email)).to.be.undefined
92 function checkVideo (video: any, videoName?: string, videoUUID?: string) {
94 expect(video.name).to.be.a('string')
95 expect(video.name).to.not.be.empty
96 expect(video.name).to.equal(videoName)
100 expect(video.uuid).to.be.a('string')
101 expect(video.uuid).to.not.be.empty
102 expect(video.uuid).to.equal(videoUUID)
105 expect(video.id).to.be.a('number')
108 function checkActor (actor: any) {
109 expect(actor.displayName).to.be.a('string')
110 expect(actor.displayName).to.not.be.empty
111 expect(actor.host).to.not.be.undefined
114 function checkComment (comment: any, commentId: number, threadId: number) {
115 expect(comment.id).to.equal(commentId)
116 expect(comment.threadId).to.equal(threadId)
119 async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
120 const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION
122 function notificationChecker (notification: UserNotification, type: CheckerType) {
123 if (type === 'presence') {
124 expect(notification).to.not.be.undefined
125 expect(notification.type).to.equal(notificationType)
127 checkVideo(notification.video, videoName, videoUUID)
128 checkActor(notification.video.channel)
130 expect(notification).to.satisfy((n: UserNotification) => {
131 return n === undefined || n.type !== UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION || n.video.name !== videoName
136 function emailNotificationFinder (email: object) {
137 const text = email['text']
138 return text.indexOf(videoUUID) !== -1 && text.indexOf('Your subscription') !== -1
141 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
144 async function checkVideoIsPublished (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
145 const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED
147 function notificationChecker (notification: UserNotification, type: CheckerType) {
148 if (type === 'presence') {
149 expect(notification).to.not.be.undefined
150 expect(notification.type).to.equal(notificationType)
152 checkVideo(notification.video, videoName, videoUUID)
153 checkActor(notification.video.channel)
155 expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName)
159 function emailNotificationFinder (email: object) {
160 const text: string = email['text']
161 return text.includes(videoUUID) && text.includes('Your video')
164 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
167 async function checkMyVideoImportIsFinished (
168 base: CheckerBaseParams,
175 const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR
177 function notificationChecker (notification: UserNotification, type: CheckerType) {
178 if (type === 'presence') {
179 expect(notification).to.not.be.undefined
180 expect(notification.type).to.equal(notificationType)
182 expect(notification.videoImport.targetUrl).to.equal(url)
184 if (success) checkVideo(notification.videoImport.video, videoName, videoUUID)
186 expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url)
190 function emailNotificationFinder (email: object) {
191 const text: string = email['text']
192 const toFind = success ? ' finished' : ' error'
194 return text.includes(url) && text.includes(toFind)
197 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
200 async function checkUserRegistered (base: CheckerBaseParams, username: string, type: CheckerType) {
201 const notificationType = UserNotificationType.NEW_USER_REGISTRATION
203 function notificationChecker (notification: UserNotification, type: CheckerType) {
204 if (type === 'presence') {
205 expect(notification).to.not.be.undefined
206 expect(notification.type).to.equal(notificationType)
208 checkActor(notification.account)
209 expect(notification.account.name).to.equal(username)
211 expect(notification).to.satisfy(n => n.type !== notificationType || n.account.name !== username)
215 function emailNotificationFinder (email: object) {
216 const text: string = email['text']
218 return text.includes(' registered.') && text.includes(username)
221 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
224 async function checkNewActorFollow (
225 base: CheckerBaseParams,
226 followType: 'channel' | 'account',
227 followerName: string,
228 followerDisplayName: string,
229 followingDisplayName: string,
232 const notificationType = UserNotificationType.NEW_FOLLOW
234 function notificationChecker (notification: UserNotification, type: CheckerType) {
235 if (type === 'presence') {
236 expect(notification).to.not.be.undefined
237 expect(notification.type).to.equal(notificationType)
239 checkActor(notification.actorFollow.follower)
240 expect(notification.actorFollow.follower.displayName).to.equal(followerDisplayName)
241 expect(notification.actorFollow.follower.name).to.equal(followerName)
242 expect(notification.actorFollow.follower.host).to.not.be.undefined
244 const following = notification.actorFollow.following
245 expect(following.displayName).to.equal(followingDisplayName)
246 expect(following.type).to.equal(followType)
248 expect(notification).to.satisfy(n => {
249 return n.type !== notificationType ||
250 (n.actorFollow.follower.name !== followerName && n.actorFollow.following !== followingDisplayName)
255 function emailNotificationFinder (email: object) {
256 const text: string = email['text']
258 return text.includes(followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName)
261 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
264 async function checkNewInstanceFollower (base: CheckerBaseParams, followerHost: string, type: CheckerType) {
265 const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER
267 function notificationChecker (notification: UserNotification, type: CheckerType) {
268 if (type === 'presence') {
269 expect(notification).to.not.be.undefined
270 expect(notification.type).to.equal(notificationType)
272 checkActor(notification.actorFollow.follower)
273 expect(notification.actorFollow.follower.name).to.equal('peertube')
274 expect(notification.actorFollow.follower.host).to.equal(followerHost)
276 expect(notification.actorFollow.following.name).to.equal('peertube')
278 expect(notification).to.satisfy(n => {
279 return n.type !== notificationType || n.actorFollow.follower.host !== followerHost
284 function emailNotificationFinder (email: object) {
285 const text: string = email['text']
287 return text.includes('instance has a new follower') && text.includes(followerHost)
290 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
293 async function checkAutoInstanceFollowing (base: CheckerBaseParams, followerHost: string, followingHost: string, type: CheckerType) {
294 const notificationType = UserNotificationType.AUTO_INSTANCE_FOLLOWING
296 function notificationChecker (notification: UserNotification, type: CheckerType) {
297 if (type === 'presence') {
298 expect(notification).to.not.be.undefined
299 expect(notification.type).to.equal(notificationType)
301 const following = notification.actorFollow.following
302 checkActor(following)
303 expect(following.name).to.equal('peertube')
304 expect(following.host).to.equal(followingHost)
306 expect(notification.actorFollow.follower.name).to.equal('peertube')
307 expect(notification.actorFollow.follower.host).to.equal(followerHost)
309 expect(notification).to.satisfy(n => {
310 return n.type !== notificationType || n.actorFollow.following.host !== followingHost
315 function emailNotificationFinder (email: object) {
316 const text: string = email['text']
318 return text.includes(' automatically followed a new instance') && text.includes(followingHost)
321 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
324 async function checkCommentMention (
325 base: CheckerBaseParams,
329 byAccountDisplayName: string,
332 const notificationType = UserNotificationType.COMMENT_MENTION
334 function notificationChecker (notification: UserNotification, type: CheckerType) {
335 if (type === 'presence') {
336 expect(notification).to.not.be.undefined
337 expect(notification.type).to.equal(notificationType)
339 checkComment(notification.comment, commentId, threadId)
340 checkActor(notification.comment.account)
341 expect(notification.comment.account.displayName).to.equal(byAccountDisplayName)
343 checkVideo(notification.comment.video, undefined, uuid)
345 expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId)
349 function emailNotificationFinder (email: object) {
350 const text: string = email['text']
352 return text.includes(' mentioned ') && text.includes(uuid) && text.includes(byAccountDisplayName)
355 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
358 let lastEmailCount = 0
360 async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) {
361 const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO
363 function notificationChecker (notification: UserNotification, type: CheckerType) {
364 if (type === 'presence') {
365 expect(notification).to.not.be.undefined
366 expect(notification.type).to.equal(notificationType)
368 checkComment(notification.comment, commentId, threadId)
369 checkActor(notification.comment.account)
370 checkVideo(notification.comment.video, undefined, uuid)
372 expect(notification).to.satisfy((n: UserNotification) => {
373 return n === undefined || n.comment === undefined || n.comment.id !== commentId
378 const commentUrl = `http://localhost:${base.server.port}/w/${uuid};threadId=${threadId}`
380 function emailNotificationFinder (email: object) {
381 return email['text'].indexOf(commentUrl) !== -1
384 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
386 if (type === 'presence') {
387 // We cannot detect email duplicates, so check we received another email
388 expect(base.emails).to.have.length.above(lastEmailCount)
389 lastEmailCount = base.emails.length
393 async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
394 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
396 function notificationChecker (notification: UserNotification, type: CheckerType) {
397 if (type === 'presence') {
398 expect(notification).to.not.be.undefined
399 expect(notification.type).to.equal(notificationType)
401 expect(notification.abuse.id).to.be.a('number')
402 checkVideo(notification.abuse.video, videoName, videoUUID)
404 expect(notification).to.satisfy((n: UserNotification) => {
405 return n === undefined || n.abuse === undefined || n.abuse.video.uuid !== videoUUID
410 function emailNotificationFinder (email: object) {
411 const text = email['text']
412 return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1
415 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
418 async function checkNewAbuseMessage (base: CheckerBaseParams, abuseId: number, message: string, toEmail: string, type: CheckerType) {
419 const notificationType = UserNotificationType.ABUSE_NEW_MESSAGE
421 function notificationChecker (notification: UserNotification, type: CheckerType) {
422 if (type === 'presence') {
423 expect(notification).to.not.be.undefined
424 expect(notification.type).to.equal(notificationType)
426 expect(notification.abuse.id).to.equal(abuseId)
428 expect(notification).to.satisfy((n: UserNotification) => {
429 return n === undefined || n.type !== notificationType || n.abuse === undefined || n.abuse.id !== abuseId
434 function emailNotificationFinder (email: object) {
435 const text = email['text']
436 const to = email['to'].filter(t => t.address === toEmail)
438 return text.indexOf(message) !== -1 && to.length !== 0
441 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
444 async function checkAbuseStateChange (base: CheckerBaseParams, abuseId: number, state: AbuseState, type: CheckerType) {
445 const notificationType = UserNotificationType.ABUSE_STATE_CHANGE
447 function notificationChecker (notification: UserNotification, type: CheckerType) {
448 if (type === 'presence') {
449 expect(notification).to.not.be.undefined
450 expect(notification.type).to.equal(notificationType)
452 expect(notification.abuse.id).to.equal(abuseId)
453 expect(notification.abuse.state).to.equal(state)
455 expect(notification).to.satisfy((n: UserNotification) => {
456 return n === undefined || n.abuse === undefined || n.abuse.id !== abuseId
461 function emailNotificationFinder (email: object) {
462 const text = email['text']
464 const contains = state === AbuseState.ACCEPTED
468 return text.indexOf(contains) !== -1
471 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
474 async function checkNewCommentAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
475 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
477 function notificationChecker (notification: UserNotification, type: CheckerType) {
478 if (type === 'presence') {
479 expect(notification).to.not.be.undefined
480 expect(notification.type).to.equal(notificationType)
482 expect(notification.abuse.id).to.be.a('number')
483 checkVideo(notification.abuse.comment.video, videoName, videoUUID)
485 expect(notification).to.satisfy((n: UserNotification) => {
486 return n === undefined || n.abuse === undefined || n.abuse.comment.video.uuid !== videoUUID
491 function emailNotificationFinder (email: object) {
492 const text = email['text']
493 return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1
496 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
499 async function checkNewAccountAbuseForModerators (base: CheckerBaseParams, displayName: string, type: CheckerType) {
500 const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
502 function notificationChecker (notification: UserNotification, type: CheckerType) {
503 if (type === 'presence') {
504 expect(notification).to.not.be.undefined
505 expect(notification.type).to.equal(notificationType)
507 expect(notification.abuse.id).to.be.a('number')
508 expect(notification.abuse.account.displayName).to.equal(displayName)
510 expect(notification).to.satisfy((n: UserNotification) => {
511 return n === undefined || n.abuse === undefined || n.abuse.account.displayName !== displayName
516 function emailNotificationFinder (email: object) {
517 const text = email['text']
518 return text.indexOf(displayName) !== -1 && text.indexOf('abuse') !== -1
521 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
524 async function checkVideoAutoBlacklistForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
525 const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS
527 function notificationChecker (notification: UserNotification, type: CheckerType) {
528 if (type === 'presence') {
529 expect(notification).to.not.be.undefined
530 expect(notification.type).to.equal(notificationType)
532 expect(notification.videoBlacklist.video.id).to.be.a('number')
533 checkVideo(notification.videoBlacklist.video, videoName, videoUUID)
535 expect(notification).to.satisfy((n: UserNotification) => {
536 return n === undefined || n.video === undefined || n.video.uuid !== videoUUID
541 function emailNotificationFinder (email: object) {
542 const text = email['text']
543 return text.indexOf(videoUUID) !== -1 && email['text'].indexOf('video-auto-blacklist/list') !== -1
546 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
549 async function checkNewBlacklistOnMyVideo (
550 base: CheckerBaseParams,
553 blacklistType: 'blacklist' | 'unblacklist'
555 const notificationType = blacklistType === 'blacklist'
556 ? UserNotificationType.BLACKLIST_ON_MY_VIDEO
557 : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO
559 function notificationChecker (notification: UserNotification) {
560 expect(notification).to.not.be.undefined
561 expect(notification.type).to.equal(notificationType)
563 const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video
565 checkVideo(video, videoName, videoUUID)
568 function emailNotificationFinder (email: object) {
569 const text = email['text']
570 return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1
573 await checkNotification(base, notificationChecker, emailNotificationFinder, 'presence')
576 async function checkNewPeerTubeVersion (base: CheckerBaseParams, latestVersion: string, type: CheckerType) {
577 const notificationType = UserNotificationType.NEW_PEERTUBE_VERSION
579 function notificationChecker (notification: UserNotification, type: CheckerType) {
580 if (type === 'presence') {
581 expect(notification).to.not.be.undefined
582 expect(notification.type).to.equal(notificationType)
584 expect(notification.peertube).to.exist
585 expect(notification.peertube.latestVersion).to.equal(latestVersion)
587 expect(notification).to.satisfy((n: UserNotification) => {
588 return n === undefined || n.peertube === undefined || n.peertube.latestVersion !== latestVersion
593 function emailNotificationFinder (email: object) {
594 const text = email['text']
596 return text.includes(latestVersion)
599 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
602 async function checkNewPluginVersion (base: CheckerBaseParams, pluginType: PluginType, pluginName: string, type: CheckerType) {
603 const notificationType = UserNotificationType.NEW_PLUGIN_VERSION
605 function notificationChecker (notification: UserNotification, type: CheckerType) {
606 if (type === 'presence') {
607 expect(notification).to.not.be.undefined
608 expect(notification.type).to.equal(notificationType)
610 expect(notification.plugin.name).to.equal(pluginName)
611 expect(notification.plugin.type).to.equal(pluginType)
613 expect(notification).to.satisfy((n: UserNotification) => {
614 return n === undefined || n.plugin === undefined || n.plugin.name !== pluginName
619 function emailNotificationFinder (email: object) {
620 const text = email['text']
622 return text.includes(pluginName)
625 await checkNotification(base, notificationChecker, emailNotificationFinder, type)
628 async function prepareNotificationsTest (serversCount = 3, overrideConfigArg: any = {}) {
629 const userNotifications: UserNotification[] = []
630 const adminNotifications: UserNotification[] = []
631 const adminNotificationsServer2: UserNotification[] = []
632 const emails: object[] = []
634 const port = await MockSmtpServer.Instance.collectEmails(emails)
636 const overrideConfig = {
638 hostname: 'localhost',
645 const servers = await flushAndRunMultipleServers(serversCount, Object.assign(overrideConfig, overrideConfigArg))
647 await setAccessTokensToServers(servers)
649 if (serversCount > 1) {
650 await doubleFollow(servers[0], servers[1])
653 const user = { username: 'user_1', password: 'super password' }
654 await servers[0].users.create({ ...user, videoQuota: 10 * 1000 * 1000 })
655 const userAccessToken = await servers[0].login.getAccessToken(user)
657 await servers[0].notifications.updateMySettings({ token: userAccessToken, settings: getAllNotificationsSettings() })
658 await servers[0].notifications.updateMySettings({ settings: getAllNotificationsSettings() })
660 if (serversCount > 1) {
661 await servers[1].notifications.updateMySettings({ settings: getAllNotificationsSettings() })
665 const socket = servers[0].socketIO.getUserNotificationSocket({ token: userAccessToken })
666 socket.on('new-notification', n => userNotifications.push(n))
669 const socket = servers[0].socketIO.getUserNotificationSocket()
670 socket.on('new-notification', n => adminNotifications.push(n))
673 if (serversCount > 1) {
674 const socket = servers[1].socketIO.getUserNotificationSocket()
675 socket.on('new-notification', n => adminNotificationsServer2.push(n))
678 const { videoChannels } = await servers[0].users.getMyInfo()
679 const channelId = videoChannels[0].id
684 adminNotificationsServer2,
692 // ---------------------------------------------------------------------------
695 getAllNotificationsSettings,
700 checkMyVideoImportIsFinished,
702 checkAutoInstanceFollowing,
703 checkVideoIsPublished,
704 checkNewVideoFromSubscription,
706 checkNewCommentOnMyVideo,
707 checkNewBlacklistOnMyVideo,
709 checkNewVideoAbuseForModerators,
710 checkVideoAutoBlacklistForModerators,
711 checkNewAbuseMessage,
712 checkAbuseStateChange,
713 checkNewInstanceFollower,
714 prepareNotificationsTest,
715 checkNewCommentAbuseForModerators,
716 checkNewAccountAbuseForModerators,
717 checkNewPeerTubeVersion,
718 checkNewPluginVersion