]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - shared/extra-utils/users/user-notifications.ts
Merge branch 'release/v1.3.0' 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 expect(notification.actorFollow.following.displayName).to.equal(followingDisplayName)
283 expect(notification.actorFollow.following.type).to.equal(followType)
284 } else {
285 expect(notification).to.satisfy(n => {
286 return n.type !== notificationType ||
287 (n.actorFollow.follower.name !== followerName && n.actorFollow.following !== followingDisplayName)
288 })
289 }
290 }
291
292 function emailFinder (email: object) {
293 const text: string = email[ 'text' ]
294
295 return text.includes('Your ' + followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName)
296 }
297
298 await checkNotification(base, notificationChecker, emailFinder, type)
299 }
300
301 async function checkNewInstanceFollower (base: CheckerBaseParams, followerHost: string, type: CheckerType) {
302 const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER
303
304 function notificationChecker (notification: UserNotification, type: CheckerType) {
305 if (type === 'presence') {
306 expect(notification).to.not.be.undefined
307 expect(notification.type).to.equal(notificationType)
308
309 checkActor(notification.actorFollow.follower)
310 expect(notification.actorFollow.follower.name).to.equal('peertube')
311 expect(notification.actorFollow.follower.host).to.equal(followerHost)
312
313 expect(notification.actorFollow.following.name).to.equal('peertube')
314 } else {
315 expect(notification).to.satisfy(n => {
316 return n.type !== notificationType || n.actorFollow.follower.host !== followerHost
317 })
318 }
319 }
320
321 function emailFinder (email: object) {
322 const text: string = email[ 'text' ]
323
324 return text.includes('instance has a new follower') && text.includes(followerHost)
325 }
326
327 await checkNotification(base, notificationChecker, emailFinder, type)
328 }
329
330 async function checkCommentMention (
331 base: CheckerBaseParams,
332 uuid: string,
333 commentId: number,
334 threadId: number,
335 byAccountDisplayName: string,
336 type: CheckerType
337 ) {
338 const notificationType = UserNotificationType.COMMENT_MENTION
339
340 function notificationChecker (notification: UserNotification, type: CheckerType) {
341 if (type === 'presence') {
342 expect(notification).to.not.be.undefined
343 expect(notification.type).to.equal(notificationType)
344
345 checkComment(notification.comment, commentId, threadId)
346 checkActor(notification.comment.account)
347 expect(notification.comment.account.displayName).to.equal(byAccountDisplayName)
348
349 checkVideo(notification.comment.video, undefined, uuid)
350 } else {
351 expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId)
352 }
353 }
354
355 function emailFinder (email: object) {
356 const text: string = email[ 'text' ]
357
358 return text.includes(' mentioned ') && text.includes(uuid) && text.includes(byAccountDisplayName)
359 }
360
361 await checkNotification(base, notificationChecker, emailFinder, type)
362 }
363
364 let lastEmailCount = 0
365 async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) {
366 const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO
367
368 function notificationChecker (notification: UserNotification, type: CheckerType) {
369 if (type === 'presence') {
370 expect(notification).to.not.be.undefined
371 expect(notification.type).to.equal(notificationType)
372
373 checkComment(notification.comment, commentId, threadId)
374 checkActor(notification.comment.account)
375 checkVideo(notification.comment.video, undefined, uuid)
376 } else {
377 expect(notification).to.satisfy((n: UserNotification) => {
378 return n === undefined || n.comment === undefined || n.comment.id !== commentId
379 })
380 }
381 }
382
383 const commentUrl = `http://localhost:${base.server.port}/videos/watch/${uuid};threadId=${threadId}`
384 function emailFinder (email: object) {
385 return email[ 'text' ].indexOf(commentUrl) !== -1
386 }
387
388 await checkNotification(base, notificationChecker, emailFinder, type)
389
390 if (type === 'presence') {
391 // We cannot detect email duplicates, so check we received another email
392 expect(base.emails).to.have.length.above(lastEmailCount)
393 lastEmailCount = base.emails.length
394 }
395 }
396
397 async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
398 const notificationType = UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS
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 expect(notification.videoAbuse.id).to.be.a('number')
406 checkVideo(notification.videoAbuse.video, videoName, videoUUID)
407 } else {
408 expect(notification).to.satisfy((n: UserNotification) => {
409 return n === undefined || n.videoAbuse === undefined || n.videoAbuse.video.uuid !== videoUUID
410 })
411 }
412 }
413
414 function emailFinder (email: object) {
415 const text = email[ 'text' ]
416 return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1
417 }
418
419 await checkNotification(base, notificationChecker, emailFinder, type)
420 }
421
422 async function checkVideoAutoBlacklistForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
423 const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS
424
425 function notificationChecker (notification: UserNotification, type: CheckerType) {
426 if (type === 'presence') {
427 expect(notification).to.not.be.undefined
428 expect(notification.type).to.equal(notificationType)
429
430 expect(notification.video.id).to.be.a('number')
431 checkVideo(notification.video, videoName, videoUUID)
432 } else {
433 expect(notification).to.satisfy((n: UserNotification) => {
434 return n === undefined || n.video === undefined || n.video.uuid !== videoUUID
435 })
436 }
437 }
438
439 function emailFinder (email: object) {
440 const text = email[ 'text' ]
441 return text.indexOf(videoUUID) !== -1 && email[ 'text' ].indexOf('video-auto-blacklist/list') !== -1
442 }
443
444 await checkNotification(base, notificationChecker, emailFinder, type)
445 }
446
447 async function checkNewBlacklistOnMyVideo (
448 base: CheckerBaseParams,
449 videoUUID: string,
450 videoName: string,
451 blacklistType: 'blacklist' | 'unblacklist'
452 ) {
453 const notificationType = blacklistType === 'blacklist'
454 ? UserNotificationType.BLACKLIST_ON_MY_VIDEO
455 : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO
456
457 function notificationChecker (notification: UserNotification) {
458 expect(notification).to.not.be.undefined
459 expect(notification.type).to.equal(notificationType)
460
461 const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video
462
463 checkVideo(video, videoName, videoUUID)
464 }
465
466 function emailFinder (email: object) {
467 const text = email[ 'text' ]
468 return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1
469 }
470
471 await checkNotification(base, notificationChecker, emailFinder, 'presence')
472 }
473
474 // ---------------------------------------------------------------------------
475
476 export {
477 CheckerBaseParams,
478 CheckerType,
479 checkNotification,
480 markAsReadAllNotifications,
481 checkMyVideoImportIsFinished,
482 checkUserRegistered,
483 checkVideoIsPublished,
484 checkNewVideoFromSubscription,
485 checkNewActorFollow,
486 checkNewCommentOnMyVideo,
487 checkNewBlacklistOnMyVideo,
488 checkCommentMention,
489 updateMyNotificationSettings,
490 checkNewVideoAbuseForModerators,
491 checkVideoAutoBlacklistForModerators,
492 getUserNotifications,
493 markAsReadNotifications,
494 getLastNotification,
495 checkNewInstanceFollower
496 }