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