]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - shared/extra-utils/users/user-notifications.ts
Merge remote-tracking branch 'weblate/develop' into develop
[github/Chocobozzz/PeerTube.git] / shared / extra-utils / users / user-notifications.ts
1 /* tslint:disable:no-unused-expression */
2
3 import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
4 import { UserNotification, UserNotificationSetting, UserNotificationType } from '../../models/users'
5 import { ServerInfo } from '..'
6 import { expect } from 'chai'
7 import { inspect } from 'util'
8
9 function updateMyNotificationSettings (url: string, token: string, settings: UserNotificationSetting, statusCodeExpected = 204) {
10 const path = '/api/v1/users/me/notification-settings'
11
12 return makePutBodyRequest({
13 url,
14 path,
15 token,
16 fields: settings,
17 statusCodeExpected
18 })
19 }
20
21 async function getUserNotifications (
22 url: string,
23 token: string,
24 start: number,
25 count: number,
26 unread?: boolean,
27 sort = '-createdAt',
28 statusCodeExpected = 200
29 ) {
30 const path = '/api/v1/users/me/notifications'
31
32 return makeGetRequest({
33 url,
34 path,
35 token,
36 query: {
37 start,
38 count,
39 sort,
40 unread
41 },
42 statusCodeExpected
43 })
44 }
45
46 function markAsReadNotifications (url: string, token: string, ids: number[], statusCodeExpected = 204) {
47 const path = '/api/v1/users/me/notifications/read'
48
49 return makePostBodyRequest({
50 url,
51 path,
52 token,
53 fields: { ids },
54 statusCodeExpected
55 })
56 }
57 function markAsReadAllNotifications (url: string, token: string, statusCodeExpected = 204) {
58 const path = '/api/v1/users/me/notifications/read-all'
59
60 return makePostBodyRequest({
61 url,
62 path,
63 token,
64 statusCodeExpected
65 })
66 }
67
68 async function getLastNotification (serverUrl: string, accessToken: string) {
69 const res = await getUserNotifications(serverUrl, accessToken, 0, 1, undefined, '-createdAt')
70
71 if (res.body.total === 0) return undefined
72
73 return res.body.data[0] as UserNotification
74 }
75
76 type CheckerBaseParams = {
77 server: ServerInfo
78 emails: object[]
79 socketNotifications: UserNotification[]
80 token: string,
81 check?: { web: boolean, mail: boolean }
82 }
83
84 type CheckerType = 'presence' | 'absence'
85
86 async function checkNotification (
87 base: CheckerBaseParams,
88 notificationChecker: (notification: UserNotification, type: CheckerType) => void,
89 emailNotificationFinder: (email: object) => boolean,
90 checkType: CheckerType
91 ) {
92 const check = base.check || { web: true, mail: true }
93
94 if (check.web) {
95 const notification = await getLastNotification(base.server.url, base.token)
96
97 if (notification || checkType !== 'absence') {
98 notificationChecker(notification, checkType)
99 }
100
101 const socketNotification = base.socketNotifications.find(n => {
102 try {
103 notificationChecker(n, 'presence')
104 return true
105 } catch {
106 return false
107 }
108 })
109
110 if (checkType === 'presence') {
111 const obj = inspect(base.socketNotifications, { depth: 5 })
112 expect(socketNotification, 'The socket notification is absent. ' + obj).to.not.be.undefined
113 } else {
114 const obj = inspect(socketNotification, { depth: 5 })
115 expect(socketNotification, 'The socket notification is present. ' + obj).to.be.undefined
116 }
117 }
118
119 if (check.mail) {
120 // Last email
121 const email = base.emails
122 .slice()
123 .reverse()
124 .find(e => emailNotificationFinder(e))
125
126 if (checkType === 'presence') {
127 expect(email, 'The email is absent. ' + inspect(base.emails)).to.not.be.undefined
128 } else {
129 expect(email, 'The email is present. ' + inspect(email)).to.be.undefined
130 }
131 }
132 }
133
134 function checkVideo (video: any, videoName?: string, videoUUID?: string) {
135 expect(video.name).to.be.a('string')
136 expect(video.name).to.not.be.empty
137 if (videoName) expect(video.name).to.equal(videoName)
138
139 expect(video.uuid).to.be.a('string')
140 expect(video.uuid).to.not.be.empty
141 if (videoUUID) expect(video.uuid).to.equal(videoUUID)
142
143 expect(video.id).to.be.a('number')
144 }
145
146 function checkActor (actor: any) {
147 expect(actor.displayName).to.be.a('string')
148 expect(actor.displayName).to.not.be.empty
149 expect(actor.host).to.not.be.undefined
150 }
151
152 function checkComment (comment: any, commentId: number, threadId: number) {
153 expect(comment.id).to.equal(commentId)
154 expect(comment.threadId).to.equal(threadId)
155 }
156
157 async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
158 const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION
159
160 function notificationChecker (notification: UserNotification, type: CheckerType) {
161 if (type === 'presence') {
162 expect(notification).to.not.be.undefined
163 expect(notification.type).to.equal(notificationType)
164
165 checkVideo(notification.video, videoName, videoUUID)
166 checkActor(notification.video.channel)
167 } else {
168 expect(notification).to.satisfy((n: UserNotification) => {
169 return n === undefined || n.type !== UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION || n.video.name !== videoName
170 })
171 }
172 }
173
174 function emailFinder (email: object) {
175 const text = email[ 'text' ]
176 return text.indexOf(videoUUID) !== -1 && text.indexOf('Your subscription') !== -1
177 }
178
179 await checkNotification(base, notificationChecker, emailFinder, type)
180 }
181
182 async function checkVideoIsPublished (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
183 const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED
184
185 function notificationChecker (notification: UserNotification, type: CheckerType) {
186 if (type === 'presence') {
187 expect(notification).to.not.be.undefined
188 expect(notification.type).to.equal(notificationType)
189
190 checkVideo(notification.video, videoName, videoUUID)
191 checkActor(notification.video.channel)
192 } else {
193 expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName)
194 }
195 }
196
197 function emailFinder (email: object) {
198 const text: string = email[ 'text' ]
199 return text.includes(videoUUID) && text.includes('Your video')
200 }
201
202 await checkNotification(base, notificationChecker, emailFinder, type)
203 }
204
205 async function checkMyVideoImportIsFinished (
206 base: CheckerBaseParams,
207 videoName: string,
208 videoUUID: string,
209 url: string,
210 success: boolean,
211 type: CheckerType
212 ) {
213 const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR
214
215 function notificationChecker (notification: UserNotification, type: CheckerType) {
216 if (type === 'presence') {
217 expect(notification).to.not.be.undefined
218 expect(notification.type).to.equal(notificationType)
219
220 expect(notification.videoImport.targetUrl).to.equal(url)
221
222 if (success) checkVideo(notification.videoImport.video, videoName, videoUUID)
223 } else {
224 expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url)
225 }
226 }
227
228 function emailFinder (email: object) {
229 const text: string = email[ 'text' ]
230 const toFind = success ? ' finished' : ' error'
231
232 return text.includes(url) && text.includes(toFind)
233 }
234
235 await checkNotification(base, notificationChecker, emailFinder, type)
236 }
237
238 async function checkUserRegistered (base: CheckerBaseParams, username: string, type: CheckerType) {
239 const notificationType = UserNotificationType.NEW_USER_REGISTRATION
240
241 function notificationChecker (notification: UserNotification, type: CheckerType) {
242 if (type === 'presence') {
243 expect(notification).to.not.be.undefined
244 expect(notification.type).to.equal(notificationType)
245
246 checkActor(notification.account)
247 expect(notification.account.name).to.equal(username)
248 } else {
249 expect(notification).to.satisfy(n => n.type !== notificationType || n.account.name !== username)
250 }
251 }
252
253 function emailFinder (email: object) {
254 const text: string = email[ 'text' ]
255
256 return text.includes(' registered ') && text.includes(username)
257 }
258
259 await checkNotification(base, notificationChecker, emailFinder, type)
260 }
261
262 async function checkNewActorFollow (
263 base: CheckerBaseParams,
264 followType: 'channel' | 'account',
265 followerName: string,
266 followerDisplayName: string,
267 followingDisplayName: string,
268 type: CheckerType
269 ) {
270 const notificationType = UserNotificationType.NEW_FOLLOW
271
272 function notificationChecker (notification: UserNotification, type: CheckerType) {
273 if (type === 'presence') {
274 expect(notification).to.not.be.undefined
275 expect(notification.type).to.equal(notificationType)
276
277 checkActor(notification.actorFollow.follower)
278 expect(notification.actorFollow.follower.displayName).to.equal(followerDisplayName)
279 expect(notification.actorFollow.follower.name).to.equal(followerName)
280 expect(notification.actorFollow.follower.host).to.not.be.undefined
281
282 const following = notification.actorFollow.following
283 expect(following.displayName).to.equal(followingDisplayName)
284 expect(following.type).to.equal(followType)
285 } else {
286 expect(notification).to.satisfy(n => {
287 return n.type !== notificationType ||
288 (n.actorFollow.follower.name !== followerName && n.actorFollow.following !== followingDisplayName)
289 })
290 }
291 }
292
293 function emailFinder (email: object) {
294 const text: string = email[ 'text' ]
295
296 return text.includes('Your ' + followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName)
297 }
298
299 await checkNotification(base, notificationChecker, emailFinder, type)
300 }
301
302 async function checkNewInstanceFollower (base: CheckerBaseParams, followerHost: string, type: CheckerType) {
303 const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER
304
305 function notificationChecker (notification: UserNotification, type: CheckerType) {
306 if (type === 'presence') {
307 expect(notification).to.not.be.undefined
308 expect(notification.type).to.equal(notificationType)
309
310 checkActor(notification.actorFollow.follower)
311 expect(notification.actorFollow.follower.name).to.equal('peertube')
312 expect(notification.actorFollow.follower.host).to.equal(followerHost)
313
314 expect(notification.actorFollow.following.name).to.equal('peertube')
315 } else {
316 expect(notification).to.satisfy(n => {
317 return n.type !== notificationType || n.actorFollow.follower.host !== followerHost
318 })
319 }
320 }
321
322 function emailFinder (email: object) {
323 const text: string = email[ 'text' ]
324
325 return text.includes('instance has a new follower') && text.includes(followerHost)
326 }
327
328 await checkNotification(base, notificationChecker, emailFinder, type)
329 }
330
331 async function checkAutoInstanceFollowing (base: CheckerBaseParams, followerHost: string, followingHost: string, type: CheckerType) {
332 const notificationType = UserNotificationType.AUTO_INSTANCE_FOLLOWING
333
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)
338
339 const following = notification.actorFollow.following
340 checkActor(following)
341 expect(following.name).to.equal('peertube')
342 expect(following.host).to.equal(followingHost)
343
344 expect(notification.actorFollow.follower.name).to.equal('peertube')
345 expect(notification.actorFollow.follower.host).to.equal(followerHost)
346 } else {
347 expect(notification).to.satisfy(n => {
348 return n.type !== notificationType || n.actorFollow.following.host !== followingHost
349 })
350 }
351 }
352
353 function emailFinder (email: object) {
354 const text: string = email[ 'text' ]
355
356 return text.includes(' automatically followed a new instance') && text.includes(followingHost)
357 }
358
359 await checkNotification(base, notificationChecker, emailFinder, type)
360 }
361
362 async function checkCommentMention (
363 base: CheckerBaseParams,
364 uuid: string,
365 commentId: number,
366 threadId: number,
367 byAccountDisplayName: string,
368 type: CheckerType
369 ) {
370 const notificationType = UserNotificationType.COMMENT_MENTION
371
372 function notificationChecker (notification: UserNotification, type: CheckerType) {
373 if (type === 'presence') {
374 expect(notification).to.not.be.undefined
375 expect(notification.type).to.equal(notificationType)
376
377 checkComment(notification.comment, commentId, threadId)
378 checkActor(notification.comment.account)
379 expect(notification.comment.account.displayName).to.equal(byAccountDisplayName)
380
381 checkVideo(notification.comment.video, undefined, uuid)
382 } else {
383 expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId)
384 }
385 }
386
387 function emailFinder (email: object) {
388 const text: string = email[ 'text' ]
389
390 return text.includes(' mentioned ') && text.includes(uuid) && text.includes(byAccountDisplayName)
391 }
392
393 await checkNotification(base, notificationChecker, emailFinder, type)
394 }
395
396 let lastEmailCount = 0
397 async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) {
398 const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO
399
400 function notificationChecker (notification: UserNotification, type: CheckerType) {
401 if (type === 'presence') {
402 expect(notification).to.not.be.undefined
403 expect(notification.type).to.equal(notificationType)
404
405 checkComment(notification.comment, commentId, threadId)
406 checkActor(notification.comment.account)
407 checkVideo(notification.comment.video, undefined, uuid)
408 } else {
409 expect(notification).to.satisfy((n: UserNotification) => {
410 return n === undefined || n.comment === undefined || n.comment.id !== commentId
411 })
412 }
413 }
414
415 const commentUrl = `http://localhost:${base.server.port}/videos/watch/${uuid};threadId=${threadId}`
416 function emailFinder (email: object) {
417 return email[ 'text' ].indexOf(commentUrl) !== -1
418 }
419
420 await checkNotification(base, notificationChecker, emailFinder, type)
421
422 if (type === 'presence') {
423 // We cannot detect email duplicates, so check we received another email
424 expect(base.emails).to.have.length.above(lastEmailCount)
425 lastEmailCount = base.emails.length
426 }
427 }
428
429 async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
430 const notificationType = UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS
431
432 function notificationChecker (notification: UserNotification, type: CheckerType) {
433 if (type === 'presence') {
434 expect(notification).to.not.be.undefined
435 expect(notification.type).to.equal(notificationType)
436
437 expect(notification.videoAbuse.id).to.be.a('number')
438 checkVideo(notification.videoAbuse.video, videoName, videoUUID)
439 } else {
440 expect(notification).to.satisfy((n: UserNotification) => {
441 return n === undefined || n.videoAbuse === undefined || n.videoAbuse.video.uuid !== videoUUID
442 })
443 }
444 }
445
446 function emailFinder (email: object) {
447 const text = email[ 'text' ]
448 return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1
449 }
450
451 await checkNotification(base, notificationChecker, emailFinder, type)
452 }
453
454 async function checkVideoAutoBlacklistForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
455 const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS
456
457 function notificationChecker (notification: UserNotification, type: CheckerType) {
458 if (type === 'presence') {
459 expect(notification).to.not.be.undefined
460 expect(notification.type).to.equal(notificationType)
461
462 expect(notification.videoBlacklist.video.id).to.be.a('number')
463 checkVideo(notification.videoBlacklist.video, videoName, videoUUID)
464 } else {
465 expect(notification).to.satisfy((n: UserNotification) => {
466 return n === undefined || n.video === undefined || n.video.uuid !== videoUUID
467 })
468 }
469 }
470
471 function emailFinder (email: object) {
472 const text = email[ 'text' ]
473 return text.indexOf(videoUUID) !== -1 && email[ 'text' ].indexOf('video-auto-blacklist/list') !== -1
474 }
475
476 await checkNotification(base, notificationChecker, emailFinder, type)
477 }
478
479 async function checkNewBlacklistOnMyVideo (
480 base: CheckerBaseParams,
481 videoUUID: string,
482 videoName: string,
483 blacklistType: 'blacklist' | 'unblacklist'
484 ) {
485 const notificationType = blacklistType === 'blacklist'
486 ? UserNotificationType.BLACKLIST_ON_MY_VIDEO
487 : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO
488
489 function notificationChecker (notification: UserNotification) {
490 expect(notification).to.not.be.undefined
491 expect(notification.type).to.equal(notificationType)
492
493 const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video
494
495 checkVideo(video, videoName, videoUUID)
496 }
497
498 function emailFinder (email: object) {
499 const text = email[ 'text' ]
500 return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1
501 }
502
503 await checkNotification(base, notificationChecker, emailFinder, 'presence')
504 }
505
506 // ---------------------------------------------------------------------------
507
508 export {
509 CheckerBaseParams,
510 CheckerType,
511 checkNotification,
512 markAsReadAllNotifications,
513 checkMyVideoImportIsFinished,
514 checkUserRegistered,
515 checkAutoInstanceFollowing,
516 checkVideoIsPublished,
517 checkNewVideoFromSubscription,
518 checkNewActorFollow,
519 checkNewCommentOnMyVideo,
520 checkNewBlacklistOnMyVideo,
521 checkCommentMention,
522 updateMyNotificationSettings,
523 checkNewVideoAbuseForModerators,
524 checkVideoAutoBlacklistForModerators,
525 getUserNotifications,
526 markAsReadNotifications,
527 getLastNotification,
528 checkNewInstanceFollower
529 }