]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/client.ts
Fix live quota tests
[github/Chocobozzz/PeerTube.git] / server / tests / client.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3 import { expect } from 'chai'
4 import { omit } from '@shared/core-utils'
5 import {
6 Account,
7 HTMLServerConfig,
8 HttpStatusCode,
9 ServerConfig,
10 VideoPlaylistCreateResult,
11 VideoPlaylistPrivacy,
12 VideoPrivacy
13 } from '@shared/models'
14 import {
15 cleanupTests,
16 createMultipleServers,
17 doubleFollow,
18 makeGetRequest,
19 makeHTMLRequest,
20 PeerTubeServer,
21 setAccessTokensToServers,
22 setDefaultVideoChannel,
23 waitJobs
24 } from '../../shared/server-commands'
25
26 function checkIndexTags (html: string, title: string, description: string, css: string, config: ServerConfig) {
27 expect(html).to.contain('<title>' + title + '</title>')
28 expect(html).to.contain('<meta name="description" content="' + description + '" />')
29 expect(html).to.contain('<style class="custom-css-style">' + css + '</style>')
30
31 const htmlConfig: HTMLServerConfig = omit(config, [ 'signup' ])
32 const configObjectString = JSON.stringify(htmlConfig)
33 const configEscapedString = JSON.stringify(configObjectString)
34
35 expect(html).to.contain(`<script type="application/javascript">window.PeerTubeServerConfig = ${configEscapedString}</script>`)
36 }
37
38 describe('Test a client controllers', function () {
39 let servers: PeerTubeServer[] = []
40 let account: Account
41
42 const videoName = 'my super name for server 1'
43 const videoDescription = 'my<br> super __description__ for *server* 1<p></p>'
44 const videoDescriptionPlainText = 'my super description for server 1'
45
46 const playlistName = 'super playlist name'
47 const playlistDescription = 'super playlist description'
48 let playlist: VideoPlaylistCreateResult
49
50 const channelDescription = 'my super channel description'
51
52 const watchVideoBasePaths = [ '/videos/watch/', '/w/' ]
53 const watchPlaylistBasePaths = [ '/videos/watch/playlist/', '/w/p/' ]
54
55 let videoIds: (string | number)[] = []
56 let privateVideoId: string
57 let internalVideoId: string
58 let unlistedVideoId: string
59
60 let playlistIds: (string | number)[] = []
61
62 before(async function () {
63 this.timeout(120000)
64
65 servers = await createMultipleServers(2)
66
67 await setAccessTokensToServers(servers)
68
69 await doubleFollow(servers[0], servers[1])
70
71 await setDefaultVideoChannel(servers)
72
73 await servers[0].channels.update({
74 channelName: servers[0].store.channel.name,
75 attributes: { description: channelDescription }
76 })
77
78 // Public video
79
80 {
81 const attributes = { name: videoName, description: videoDescription }
82 await servers[0].videos.upload({ attributes })
83
84 const { data } = await servers[0].videos.list()
85 expect(data.length).to.equal(1)
86
87 const video = data[0]
88 servers[0].store.video = video
89 videoIds = [ video.id, video.uuid, video.shortUUID ]
90 }
91
92 {
93 ({ uuid: privateVideoId } = await servers[0].videos.quickUpload({ name: 'private', privacy: VideoPrivacy.PRIVATE }));
94 ({ uuid: unlistedVideoId } = await servers[0].videos.quickUpload({ name: 'unlisted', privacy: VideoPrivacy.UNLISTED }));
95 ({ uuid: internalVideoId } = await servers[0].videos.quickUpload({ name: 'internal', privacy: VideoPrivacy.INTERNAL }))
96 }
97
98 // Playlist
99
100 {
101 const attributes = {
102 displayName: playlistName,
103 description: playlistDescription,
104 privacy: VideoPlaylistPrivacy.PUBLIC,
105 videoChannelId: servers[0].store.channel.id
106 }
107
108 playlist = await servers[0].playlists.create({ attributes })
109 playlistIds = [ playlist.id, playlist.shortUUID, playlist.uuid ]
110
111 await servers[0].playlists.addElement({ playlistId: playlist.shortUUID, attributes: { videoId: servers[0].store.video.id } })
112 }
113
114 // Account
115
116 {
117 await servers[0].users.updateMe({ description: 'my account description' })
118
119 account = await servers[0].accounts.get({ accountName: `${servers[0].store.user.username}@${servers[0].host}` })
120 }
121
122 await waitJobs(servers)
123 })
124
125 describe('oEmbed', function () {
126
127 it('Should have valid oEmbed discovery tags for videos', async function () {
128 for (const basePath of watchVideoBasePaths) {
129 for (const id of videoIds) {
130 const res = await makeGetRequest({
131 url: servers[0].url,
132 path: basePath + id,
133 accept: 'text/html',
134 expectedStatus: HttpStatusCode.OK_200
135 })
136
137 const expectedLink = `<link rel="alternate" type="application/json+oembed" href="${servers[0].url}/services/oembed?` +
138 `url=http%3A%2F%2F${servers[0].hostname}%3A${servers[0].port}%2Fw%2F${servers[0].store.video.shortUUID}" ` +
139 `title="${servers[0].store.video.name}" />`
140
141 expect(res.text).to.contain(expectedLink)
142 }
143 }
144 })
145
146 it('Should have valid oEmbed discovery tags for a playlist', async function () {
147 for (const basePath of watchPlaylistBasePaths) {
148 for (const id of playlistIds) {
149 const res = await makeGetRequest({
150 url: servers[0].url,
151 path: basePath + id,
152 accept: 'text/html',
153 expectedStatus: HttpStatusCode.OK_200
154 })
155
156 const expectedLink = `<link rel="alternate" type="application/json+oembed" href="${servers[0].url}/services/oembed?` +
157 `url=http%3A%2F%2F${servers[0].hostname}%3A${servers[0].port}%2Fw%2Fp%2F${playlist.shortUUID}" ` +
158 `title="${playlistName}" />`
159
160 expect(res.text).to.contain(expectedLink)
161 }
162 }
163 })
164 })
165
166 describe('Open Graph', function () {
167
168 async function accountPageTest (path: string) {
169 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
170 const text = res.text
171
172 expect(text).to.contain(`<meta property="og:title" content="${account.displayName}" />`)
173 expect(text).to.contain(`<meta property="og:description" content="${account.description}" />`)
174 expect(text).to.contain('<meta property="og:type" content="website" />')
175 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/a/${servers[0].store.user.username}" />`)
176 }
177
178 async function channelPageTest (path: string) {
179 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
180 const text = res.text
181
182 expect(text).to.contain(`<meta property="og:title" content="${servers[0].store.channel.displayName}" />`)
183 expect(text).to.contain(`<meta property="og:description" content="${channelDescription}" />`)
184 expect(text).to.contain('<meta property="og:type" content="website" />')
185 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/c/${servers[0].store.channel.name}" />`)
186 }
187
188 async function watchVideoPageTest (path: string) {
189 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
190 const text = res.text
191
192 expect(text).to.contain(`<meta property="og:title" content="${videoName}" />`)
193 expect(text).to.contain(`<meta property="og:description" content="${videoDescriptionPlainText}" />`)
194 expect(text).to.contain('<meta property="og:type" content="video" />')
195 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/${servers[0].store.video.shortUUID}" />`)
196 }
197
198 async function watchPlaylistPageTest (path: string) {
199 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
200 const text = res.text
201
202 expect(text).to.contain(`<meta property="og:title" content="${playlistName}" />`)
203 expect(text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`)
204 expect(text).to.contain('<meta property="og:type" content="video" />')
205 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/p/${playlist.shortUUID}" />`)
206 }
207
208 it('Should have valid Open Graph tags on the account page', async function () {
209 await accountPageTest('/accounts/' + servers[0].store.user.username)
210 await accountPageTest('/a/' + servers[0].store.user.username)
211 await accountPageTest('/@' + servers[0].store.user.username)
212 })
213
214 it('Should have valid Open Graph tags on the channel page', async function () {
215 await channelPageTest('/video-channels/' + servers[0].store.channel.name)
216 await channelPageTest('/c/' + servers[0].store.channel.name)
217 await channelPageTest('/@' + servers[0].store.channel.name)
218 })
219
220 it('Should have valid Open Graph tags on the watch page', async function () {
221 for (const path of watchVideoBasePaths) {
222 for (const id of videoIds) {
223 await watchVideoPageTest(path + id)
224 }
225 }
226 })
227
228 it('Should have valid Open Graph tags on the watch page with thread id Angular param', async function () {
229 for (const path of watchVideoBasePaths) {
230 for (const id of videoIds) {
231 await watchVideoPageTest(path + id + ';threadId=1')
232 }
233 }
234 })
235
236 it('Should have valid Open Graph tags on the watch playlist page', async function () {
237 for (const path of watchPlaylistBasePaths) {
238 for (const id of playlistIds) {
239 await watchPlaylistPageTest(path + id)
240 }
241 }
242 })
243 })
244
245 describe('Twitter card', async function () {
246
247 describe('Not whitelisted', function () {
248
249 async function accountPageTest (path: string) {
250 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
251 const text = res.text
252
253 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
254 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
255 expect(text).to.contain(`<meta property="twitter:title" content="${account.name}" />`)
256 expect(text).to.contain(`<meta property="twitter:description" content="${account.description}" />`)
257 }
258
259 async function channelPageTest (path: string) {
260 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
261 const text = res.text
262
263 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
264 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
265 expect(text).to.contain(`<meta property="twitter:title" content="${servers[0].store.channel.displayName}" />`)
266 expect(text).to.contain(`<meta property="twitter:description" content="${channelDescription}" />`)
267 }
268
269 async function watchVideoPageTest (path: string) {
270 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
271 const text = res.text
272
273 expect(text).to.contain('<meta property="twitter:card" content="summary_large_image" />')
274 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
275 expect(text).to.contain(`<meta property="twitter:title" content="${videoName}" />`)
276 expect(text).to.contain(`<meta property="twitter:description" content="${videoDescriptionPlainText}" />`)
277 }
278
279 async function watchPlaylistPageTest (path: string) {
280 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
281 const text = res.text
282
283 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
284 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
285 expect(text).to.contain(`<meta property="twitter:title" content="${playlistName}" />`)
286 expect(text).to.contain(`<meta property="twitter:description" content="${playlistDescription}" />`)
287 }
288
289 it('Should have valid twitter card on the watch video page', async function () {
290 for (const path of watchVideoBasePaths) {
291 for (const id of videoIds) {
292 await watchVideoPageTest(path + id)
293 }
294 }
295 })
296
297 it('Should have valid twitter card on the watch playlist page', async function () {
298 for (const path of watchPlaylistBasePaths) {
299 for (const id of playlistIds) {
300 await watchPlaylistPageTest(path + id)
301 }
302 }
303 })
304
305 it('Should have valid twitter card on the account page', async function () {
306 await accountPageTest('/accounts/' + account.name)
307 await accountPageTest('/a/' + account.name)
308 await accountPageTest('/@' + account.name)
309 })
310
311 it('Should have valid twitter card on the channel page', async function () {
312 await channelPageTest('/video-channels/' + servers[0].store.channel.name)
313 await channelPageTest('/c/' + servers[0].store.channel.name)
314 await channelPageTest('/@' + servers[0].store.channel.name)
315 })
316 })
317
318 describe('Whitelisted', function () {
319
320 before(async function () {
321 const config = await servers[0].config.getCustomConfig()
322 config.services.twitter = {
323 username: '@Kuja',
324 whitelisted: true
325 }
326
327 await servers[0].config.updateCustomConfig({ newCustomConfig: config })
328 })
329
330 async function accountPageTest (path: string) {
331 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
332 const text = res.text
333
334 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
335 expect(text).to.contain('<meta property="twitter:site" content="@Kuja" />')
336 }
337
338 async function channelPageTest (path: string) {
339 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
340 const text = res.text
341
342 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
343 expect(text).to.contain('<meta property="twitter:site" content="@Kuja" />')
344 }
345
346 async function watchVideoPageTest (path: string) {
347 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
348 const text = res.text
349
350 expect(text).to.contain('<meta property="twitter:card" content="player" />')
351 expect(text).to.contain('<meta property="twitter:site" content="@Kuja" />')
352 }
353
354 async function watchPlaylistPageTest (path: string) {
355 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', expectedStatus: HttpStatusCode.OK_200 })
356 const text = res.text
357
358 expect(text).to.contain('<meta property="twitter:card" content="player" />')
359 expect(text).to.contain('<meta property="twitter:site" content="@Kuja" />')
360 }
361
362 it('Should have valid twitter card on the watch video page', async function () {
363 for (const path of watchVideoBasePaths) {
364 for (const id of videoIds) {
365 await watchVideoPageTest(path + id)
366 }
367 }
368 })
369
370 it('Should have valid twitter card on the watch playlist page', async function () {
371 for (const path of watchPlaylistBasePaths) {
372 for (const id of playlistIds) {
373 await watchPlaylistPageTest(path + id)
374 }
375 }
376 })
377
378 it('Should have valid twitter card on the account page', async function () {
379 await accountPageTest('/accounts/' + account.name)
380 await accountPageTest('/a/' + account.name)
381 await accountPageTest('/@' + account.name)
382 })
383
384 it('Should have valid twitter card on the channel page', async function () {
385 await channelPageTest('/video-channels/' + servers[0].store.channel.name)
386 await channelPageTest('/c/' + servers[0].store.channel.name)
387 await channelPageTest('/@' + servers[0].store.channel.name)
388 })
389 })
390 })
391
392 describe('Index HTML', function () {
393
394 it('Should have valid index html tags (title, description...)', async function () {
395 const config = await servers[0].config.getConfig()
396 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
397
398 const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
399 checkIndexTags(res.text, 'PeerTube', description, '', config)
400 })
401
402 it('Should update the customized configuration and have the correct index html tags', async function () {
403 await servers[0].config.updateCustomSubConfig({
404 newConfig: {
405 instance: {
406 name: 'PeerTube updated',
407 shortDescription: 'my short description',
408 description: 'my super description',
409 terms: 'my super terms',
410 defaultNSFWPolicy: 'blur',
411 defaultClientRoute: '/videos/recently-added',
412 customizations: {
413 javascript: 'alert("coucou")',
414 css: 'body { background-color: red; }'
415 }
416 }
417 }
418 })
419
420 const config = await servers[0].config.getConfig()
421 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
422
423 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
424 })
425
426 it('Should have valid index html updated tags (title, description...)', async function () {
427 const config = await servers[0].config.getConfig()
428 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
429
430 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
431 })
432
433 it('Should use the original video URL for the canonical tag', async function () {
434 for (const basePath of watchVideoBasePaths) {
435 for (const id of videoIds) {
436 const res = await makeHTMLRequest(servers[1].url, basePath + id)
437 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/videos/watch/${servers[0].store.video.uuid}" />`)
438 }
439 }
440 })
441
442 it('Should use the original account URL for the canonical tag', async function () {
443 const accountURLtest = res => {
444 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/accounts/root" />`)
445 }
446
447 accountURLtest(await makeHTMLRequest(servers[1].url, '/accounts/root@' + servers[0].host))
448 accountURLtest(await makeHTMLRequest(servers[1].url, '/a/root@' + servers[0].host))
449 accountURLtest(await makeHTMLRequest(servers[1].url, '/@root@' + servers[0].host))
450 })
451
452 it('Should use the original channel URL for the canonical tag', async function () {
453 const channelURLtests = res => {
454 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-channels/root_channel" />`)
455 }
456
457 channelURLtests(await makeHTMLRequest(servers[1].url, '/video-channels/root_channel@' + servers[0].host))
458 channelURLtests(await makeHTMLRequest(servers[1].url, '/c/root_channel@' + servers[0].host))
459 channelURLtests(await makeHTMLRequest(servers[1].url, '/@root_channel@' + servers[0].host))
460 })
461
462 it('Should use the original playlist URL for the canonical tag', async function () {
463 for (const basePath of watchPlaylistBasePaths) {
464 for (const id of playlistIds) {
465 const res = await makeHTMLRequest(servers[1].url, basePath + id)
466 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-playlists/${playlist.uuid}" />`)
467 }
468 }
469 })
470
471 it('Should add noindex meta tag for remote accounts', async function () {
472 const handle = 'root@' + servers[0].host
473 const paths = [ '/accounts/', '/a/', '/@' ]
474
475 for (const path of paths) {
476 {
477 const { text } = await makeHTMLRequest(servers[1].url, path + handle)
478 expect(text).to.contain('<meta name="robots" content="noindex" />')
479 }
480
481 {
482 const { text } = await makeHTMLRequest(servers[0].url, path + handle)
483 expect(text).to.not.contain('<meta name="robots" content="noindex" />')
484 }
485 }
486 })
487
488 it('Should add noindex meta tag for remote channels', async function () {
489 const handle = 'root_channel@' + servers[0].host
490 const paths = [ '/video-channels/', '/c/', '/@' ]
491
492 for (const path of paths) {
493 {
494 const { text } = await makeHTMLRequest(servers[1].url, path + handle)
495 expect(text).to.contain('<meta name="robots" content="noindex" />')
496 }
497
498 {
499 const { text } = await makeHTMLRequest(servers[0].url, path + handle)
500 expect(text).to.not.contain('<meta name="robots" content="noindex" />')
501 }
502 }
503 })
504
505 it('Should not display internal/private video', async function () {
506 for (const basePath of watchVideoBasePaths) {
507 for (const id of [ privateVideoId, internalVideoId ]) {
508 const res = await makeGetRequest({
509 url: servers[0].url,
510 path: basePath + id,
511 accept: 'text/html',
512 expectedStatus: HttpStatusCode.NOT_FOUND_404
513 })
514
515 expect(res.text).to.not.contain('internal')
516 expect(res.text).to.not.contain('private')
517 }
518 }
519 })
520
521 it('Should add noindex meta tag for unlisted video', async function () {
522 for (const basePath of watchVideoBasePaths) {
523 const res = await makeGetRequest({
524 url: servers[0].url,
525 path: basePath + unlistedVideoId,
526 accept: 'text/html',
527 expectedStatus: HttpStatusCode.OK_200
528 })
529
530 expect(res.text).to.contain('unlisted')
531 expect(res.text).to.contain('<meta name="robots" content="noindex" />')
532 }
533 })
534 })
535
536 describe('Embed HTML', function () {
537
538 it('Should have the correct embed html tags', async function () {
539 const config = await servers[0].config.getConfig()
540 const res = await makeHTMLRequest(servers[0].url, servers[0].store.video.embedPath)
541
542 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
543 })
544 })
545
546 after(async function () {
547 await cleanupTests(servers)
548 })
549 })