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