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, userLogin } from './login'
11 import { createUser, getMyUserInformation } from './users'
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 = {
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.notificationsCommand.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 flushAndRunMultipleServers(serversCount, Object.assign(overrideConfig, overrideConfigArg))
648 await setAccessTokensToServers(servers)
650 if (serversCount > 1) {
651 await doubleFollow(servers[0], servers[1])
656 password: 'super password'
660 accessToken: servers[0].accessToken,
661 username: user.username,
662 password: user.password,
663 videoQuota: 10 * 1000 * 1000
665 const userAccessToken = await userLogin(servers[0], user)
667 await servers[0].notificationsCommand.updateMySettings({ token: userAccessToken, settings: getAllNotificationsSettings() })
668 await servers[0].notificationsCommand.updateMySettings({ settings: getAllNotificationsSettings() })
670 if (serversCount > 1) {
671 await servers[1].notificationsCommand.updateMySettings({ settings: getAllNotificationsSettings() })
675 const socket = servers[0].socketIOCommand.getUserNotificationSocket({ token: userAccessToken })
676 socket.on('new-notification', n => userNotifications.push(n))
679 const socket = servers[0].socketIOCommand.getUserNotificationSocket()
680 socket.on('new-notification', n => adminNotifications.push(n))
683 if (serversCount > 1) {
684 const socket = servers[1].socketIOCommand.getUserNotificationSocket()
685 socket.on('new-notification', n => adminNotificationsServer2.push(n))
688 const resChannel = await getMyUserInformation(servers[0].url, servers[0].accessToken)
689 const channelId = resChannel.body.videoChannels[0].id
694 adminNotificationsServer2,
702 // ---------------------------------------------------------------------------
705 getAllNotificationsSettings,
710 checkMyVideoImportIsFinished,
712 checkAutoInstanceFollowing,
713 checkVideoIsPublished,
714 checkNewVideoFromSubscription,
716 checkNewCommentOnMyVideo,
717 checkNewBlacklistOnMyVideo,
719 checkNewVideoAbuseForModerators,
720 checkVideoAutoBlacklistForModerators,
721 checkNewAbuseMessage,
722 checkAbuseStateChange,
723 checkNewInstanceFollower,
724 prepareNotificationsTest,
725 checkNewCommentAbuseForModerators,
726 checkNewAccountAbuseForModerators,
727 checkNewPeerTubeVersion,
728 checkNewPluginVersion