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