]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/tests/client.ts
Introduce playlist command
[github/Chocobozzz/PeerTube.git] / server / tests / client.ts
CommitLineData
a1587156 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
0e1dc3e7
C
2
3import 'mocha'
4import * as chai from 'chai'
012580d9 5import { omit } from 'lodash'
012580d9 6import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
65e6e260 7import { Account, HTMLServerConfig, ServerConfig, VideoPlaylistCreateResult, VideoPlaylistPrivacy } from '@shared/models'
0e1dc3e7 8import {
7c3b7976 9 cleanupTests,
a59f210f
C
10 doubleFollow,
11 flushAndRunMultipleServers,
590fb506 12 getVideosList,
012580d9 13 makeGetRequest,
590fb506 14 makeHTMLRequest,
590fb506 15 ServerInfo,
a59f210f 16 setAccessTokensToServers,
39433fa5 17 setDefaultVideoChannel,
39433fa5 18 updateMyUser,
e5024f51 19 updateVideoChannel,
a59f210f
C
20 uploadVideo,
21 waitJobs
94565d52 22} from '../../shared/extra-utils'
590fb506
C
23
24const expect = chai.expect
5bcfd029 25
aea0b0e7 26function checkIndexTags (html: string, title: string, description: string, css: string, config: ServerConfig) {
5bcfd029
C
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>')
aea0b0e7
C
30
31 const htmlConfig: HTMLServerConfig = omit(config, 'signup')
32 expect(html).to.contain(`<script type="application/javascript">window.PeerTubeServerConfig = '${JSON.stringify(htmlConfig)}'</script>`)
5bcfd029 33}
0e1dc3e7
C
34
35describe('Test a client controllers', function () {
e5024f51 36 let servers: ServerInfo[] = []
39433fa5
C
37 let account: Account
38
39 const videoName = 'my super name for server 1'
a073c912
RK
40 const videoDescription = 'my<br> super __description__ for *server* 1<p></p>'
41 const videoDescriptionPlainText = 'my super description for server 1'
39433fa5
C
42
43 const playlistName = 'super playlist name'
44 const playlistDescription = 'super playlist description'
d4a8e7a6 45 let playlist: VideoPlaylistCreateResult
39433fa5
C
46
47 const channelDescription = 'my super channel description'
0e1dc3e7 48
a1eda903
C
49 const watchVideoBasePaths = [ '/videos/watch/', '/w/' ]
50 const watchPlaylistBasePaths = [ '/videos/watch/playlist/', '/w/p/' ]
51
d4a8e7a6
C
52 let videoIds: (string | number)[] = []
53 let playlistIds: (string | number)[] = []
54
0e1dc3e7
C
55 before(async function () {
56 this.timeout(120000)
57
e5024f51 58 servers = await flushAndRunMultipleServers(2)
e5024f51
TC
59
60 await setAccessTokensToServers(servers)
61
e5024f51
TC
62 await doubleFollow(servers[0], servers[1])
63
a59f210f 64 await setDefaultVideoChannel(servers)
0e1dc3e7 65
a59f210f 66 await updateVideoChannel(servers[0].url, servers[0].accessToken, servers[0].videoChannel.name, { description: channelDescription })
8d987ec6 67
39433fa5 68 // Video
8d987ec6 69
39433fa5 70 const videoAttributes = { name: videoName, description: videoDescription }
a59f210f 71 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
0e1dc3e7 72
a59f210f 73 const resVideosRequest = await getVideosList(servers[0].url)
8d987ec6 74 const videos = resVideosRequest.body.data
0e1dc3e7
C
75 expect(videos.length).to.equal(1)
76
d4a8e7a6
C
77 const video = videos[0]
78 servers[0].video = video
79 videoIds = [ video.id, video.uuid, video.shortUUID ]
8d987ec6
K
80
81 // Playlist
82
e6346d59 83 const attributes = {
39433fa5
C
84 displayName: playlistName,
85 description: playlistDescription,
86 privacy: VideoPlaylistPrivacy.PUBLIC,
a59f210f 87 videoChannelId: servers[0].videoChannel.id
8d987ec6
K
88 }
89
e6346d59 90 playlist = await servers[0].playlistsCommand.create({ attributes })
d4a8e7a6 91 playlistIds = [ playlist.id, playlist.shortUUID, playlist.uuid ]
8d987ec6 92
e6346d59 93 await servers[0].playlistsCommand.addElement({ playlistId: playlist.shortUUID, attributes: { videoId: video.id } })
8d987ec6
K
94
95 // Account
96
a59f210f 97 await updateMyUser({ url: servers[0].url, accessToken: servers[0].accessToken, description: 'my account description' })
8d987ec6 98
9fff08cf 99 account = await servers[0].accountsCommand.get({ accountName: `${servers[0].user.username}@${servers[0].host}` })
a59f210f
C
100
101 await waitJobs(servers)
0e1dc3e7
C
102 })
103
6fad8e51 104 describe('oEmbed', function () {
a1eda903 105
6fad8e51 106 it('Should have valid oEmbed discovery tags for videos', async function () {
a1eda903 107 for (const basePath of watchVideoBasePaths) {
d4a8e7a6
C
108 for (const id of videoIds) {
109 const res = await makeGetRequest({
110 url: servers[0].url,
111 path: basePath + id,
112 accept: 'text/html',
113 statusCodeExpected: HttpStatusCode.OK_200
114 })
0e1dc3e7 115
d4a8e7a6 116 const port = servers[0].port
0e1dc3e7 117
d4a8e7a6
C
118 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
119 `url=http%3A%2F%2Flocalhost%3A${port}%2Fw%2F${servers[0].video.uuid}" ` +
120 `title="${servers[0].video.name}" />`
8d987ec6 121
d4a8e7a6
C
122 expect(res.text).to.contain(expectedLink)
123 }
a1eda903 124 }
6fad8e51 125 })
8d987ec6 126
6fad8e51 127 it('Should have valid oEmbed discovery tags for a playlist', async function () {
a1eda903 128 for (const basePath of watchPlaylistBasePaths) {
d4a8e7a6
C
129 for (const id of playlistIds) {
130 const res = await makeGetRequest({
131 url: servers[0].url,
132 path: basePath + id,
133 accept: 'text/html',
134 statusCodeExpected: HttpStatusCode.OK_200
135 })
6fad8e51 136
d4a8e7a6 137 const port = servers[0].port
8d987ec6 138
d4a8e7a6
C
139 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
140 `url=http%3A%2F%2Flocalhost%3A${port}%2Fw%2Fp%2F${playlist.uuid}" ` +
141 `title="${playlistName}" />`
8d987ec6 142
d4a8e7a6
C
143 expect(res.text).to.contain(expectedLink)
144 }
a1eda903 145 }
6fad8e51 146 })
8d987ec6
K
147 })
148
6fad8e51 149 describe('Open Graph', function () {
8d987ec6 150
012580d9
C
151 async function accountPageTest (path: string) {
152 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
153 const text = res.text
9a911038 154
012580d9
C
155 expect(text).to.contain(`<meta property="og:title" content="${account.displayName}" />`)
156 expect(text).to.contain(`<meta property="og:description" content="${account.description}" />`)
157 expect(text).to.contain('<meta property="og:type" content="website" />')
158 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/accounts/${servers[0].user.username}" />`)
159 }
9a911038 160
012580d9
C
161 async function channelPageTest (path: string) {
162 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
163 const text = res.text
0e1dc3e7 164
012580d9
C
165 expect(text).to.contain(`<meta property="og:title" content="${servers[0].videoChannel.displayName}" />`)
166 expect(text).to.contain(`<meta property="og:description" content="${channelDescription}" />`)
167 expect(text).to.contain('<meta property="og:type" content="website" />')
168 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/video-channels/${servers[0].videoChannel.name}" />`)
169 }
170
a1eda903
C
171 async function watchVideoPageTest (path: string) {
172 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
173 const text = res.text
174
175 expect(text).to.contain(`<meta property="og:title" content="${videoName}" />`)
176 expect(text).to.contain(`<meta property="og:description" content="${videoDescriptionPlainText}" />`)
177 expect(text).to.contain('<meta property="og:type" content="video" />')
178 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/${servers[0].video.uuid}" />`)
179 }
180
181 async function watchPlaylistPageTest (path: string) {
182 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
183 const text = res.text
184
185 expect(text).to.contain(`<meta property="og:title" content="${playlistName}" />`)
186 expect(text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`)
187 expect(text).to.contain('<meta property="og:type" content="video" />')
d4a8e7a6 188 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/p/${playlist.uuid}" />`)
a1eda903
C
189 }
190
012580d9
C
191 it('Should have valid Open Graph tags on the account page', async function () {
192 await accountPageTest('/accounts/' + servers[0].user.username)
193 await accountPageTest('/a/' + servers[0].user.username)
194 await accountPageTest('/@' + servers[0].user.username)
6fad8e51 195 })
d8755eed 196
6fad8e51 197 it('Should have valid Open Graph tags on the channel page', async function () {
012580d9
C
198 await channelPageTest('/video-channels/' + servers[0].videoChannel.name)
199 await channelPageTest('/c/' + servers[0].videoChannel.name)
200 await channelPageTest('/@' + servers[0].videoChannel.name)
6fad8e51 201 })
d8755eed 202
a1eda903 203 it('Should have valid Open Graph tags on the watch page', async function () {
d4a8e7a6
C
204 for (const path of watchVideoBasePaths) {
205 for (const id of videoIds) {
206 await watchVideoPageTest(path + id)
207 }
208 }
6fad8e51 209 })
8d987ec6 210
6fad8e51 211 it('Should have valid Open Graph tags on the watch playlist page', async function () {
d4a8e7a6
C
212 for (const path of watchPlaylistBasePaths) {
213 for (const id of playlistIds) {
214 await watchPlaylistPageTest(path + id)
215 }
216 }
6fad8e51 217 })
8d987ec6
K
218 })
219
6fad8e51 220 describe('Twitter card', async function () {
8d987ec6 221
012580d9 222 describe('Not whitelisted', function () {
8d987ec6 223
012580d9
C
224 async function accountPageTest (path: string) {
225 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
226 const text = res.text
8d987ec6 227
012580d9
C
228 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
229 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
230 expect(text).to.contain(`<meta property="twitter:title" content="${account.name}" />`)
231 expect(text).to.contain(`<meta property="twitter:description" content="${account.description}" />`)
232 }
8be1afa1 233
012580d9
C
234 async function channelPageTest (path: string) {
235 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
236 const text = res.text
8be1afa1 237
012580d9
C
238 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
239 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
240 expect(text).to.contain(`<meta property="twitter:title" content="${servers[0].videoChannel.displayName}" />`)
241 expect(text).to.contain(`<meta property="twitter:description" content="${channelDescription}" />`)
9a911038
K
242 }
243
a1eda903
C
244 async function watchVideoPageTest (path: string) {
245 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
246 const text = res.text
8be1afa1 247
a1eda903
C
248 expect(text).to.contain('<meta property="twitter:card" content="summary_large_image" />')
249 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
250 expect(text).to.contain(`<meta property="twitter:title" content="${videoName}" />`)
251 expect(text).to.contain(`<meta property="twitter:description" content="${videoDescriptionPlainText}" />`)
252 }
253
254 async function watchPlaylistPageTest (path: string) {
255 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
256 const text = res.text
257
258 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
259 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
260 expect(text).to.contain(`<meta property="twitter:title" content="${playlistName}" />`)
261 expect(text).to.contain(`<meta property="twitter:description" content="${playlistDescription}" />`)
262 }
263
264 it('Should have valid twitter card on the watch video page', async function () {
d4a8e7a6
C
265 for (const path of watchVideoBasePaths) {
266 for (const id of videoIds) {
267 await watchVideoPageTest(path + id)
268 }
269 }
012580d9 270 })
9a911038 271
012580d9 272 it('Should have valid twitter card on the watch playlist page', async function () {
d4a8e7a6
C
273 for (const path of watchPlaylistBasePaths) {
274 for (const id of playlistIds) {
275 await watchPlaylistPageTest(path + id)
276 }
277 }
012580d9
C
278 })
279
280 it('Should have valid twitter card on the account page', async function () {
281 await accountPageTest('/accounts/' + account.name)
282 await accountPageTest('/a/' + account.name)
283 await accountPageTest('/@' + account.name)
284 })
285
286 it('Should have valid twitter card on the channel page', async function () {
287 await channelPageTest('/video-channels/' + servers[0].videoChannel.name)
288 await channelPageTest('/c/' + servers[0].videoChannel.name)
289 await channelPageTest('/@' + servers[0].videoChannel.name)
290 })
6fad8e51 291 })
8d987ec6 292
012580d9 293 describe('Whitelisted', function () {
8d987ec6 294
012580d9 295 before(async function () {
65e6e260 296 const config = await servers[0].configCommand.getCustomConfig()
012580d9
C
297 config.services.twitter = {
298 username: '@Kuja',
299 whitelisted: true
300 }
8d987ec6 301
65e6e260 302 await servers[0].configCommand.updateCustomConfig({ newCustomConfig: config })
012580d9 303 })
8d987ec6 304
012580d9
C
305 async function accountPageTest (path: string) {
306 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
307 const text = res.text
8be1afa1 308
012580d9
C
309 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
310 expect(text).to.contain('<meta property="twitter:site" content="@Kuja" />')
9a911038
K
311 }
312
012580d9
C
313 async function channelPageTest (path: string) {
314 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
315 const text = res.text
9a911038 316
012580d9
C
317 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
318 expect(text).to.contain('<meta property="twitter:site" content="@Kuja" />')
319 }
9a911038 320
a1eda903
C
321 async function watchVideoPageTest (path: string) {
322 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
323 const text = res.text
6fad8e51 324
a1eda903
C
325 expect(text).to.contain('<meta property="twitter:card" content="player" />')
326 expect(text).to.contain('<meta property="twitter:site" content="@Kuja" />')
327 }
328
329 async function watchPlaylistPageTest (path: string) {
330 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
331 const text = res.text
332
333 expect(text).to.contain('<meta property="twitter:card" content="player" />')
334 expect(text).to.contain('<meta property="twitter:site" content="@Kuja" />')
335 }
336
337 it('Should have valid twitter card on the watch video page', async function () {
d4a8e7a6
C
338 for (const path of watchVideoBasePaths) {
339 for (const id of videoIds) {
340 await watchVideoPageTest(path + id)
341 }
342 }
012580d9 343 })
9a911038 344
012580d9 345 it('Should have valid twitter card on the watch playlist page', async function () {
d4a8e7a6
C
346 for (const path of watchPlaylistBasePaths) {
347 for (const id of playlistIds) {
348 await watchPlaylistPageTest(path + id)
349 }
350 }
012580d9
C
351 })
352
353 it('Should have valid twitter card on the account page', async function () {
354 await accountPageTest('/accounts/' + account.name)
355 await accountPageTest('/a/' + account.name)
356 await accountPageTest('/@' + account.name)
357 })
358
359 it('Should have valid twitter card on the channel page', async function () {
360 await channelPageTest('/video-channels/' + servers[0].videoChannel.name)
361 await channelPageTest('/c/' + servers[0].videoChannel.name)
362 await channelPageTest('/@' + servers[0].videoChannel.name)
363 })
6fad8e51 364 })
5bcfd029
C
365 })
366
6fad8e51
C
367 describe('Index HTML', function () {
368
369 it('Should have valid index html tags (title, description...)', async function () {
65e6e260 370 const config = await servers[0].configCommand.getConfig()
a59f210f 371 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
6fad8e51
C
372
373 const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
65e6e260 374 checkIndexTags(res.text, 'PeerTube', description, '', config)
590fb506 375 })
5bcfd029 376
6fad8e51 377 it('Should update the customized configuration and have the correct index html tags', async function () {
65e6e260
C
378 await servers[0].configCommand.updateCustomSubConfig({
379 newConfig: {
380 instance: {
381 name: 'PeerTube updated',
382 shortDescription: 'my short description',
383 description: 'my super description',
384 terms: 'my super terms',
385 defaultNSFWPolicy: 'blur',
386 defaultClientRoute: '/videos/recently-added',
387 customizations: {
388 javascript: 'alert("coucou")',
389 css: 'body { background-color: red; }'
390 }
6fad8e51
C
391 }
392 }
393 })
5bcfd029 394
65e6e260 395 const config = await servers[0].configCommand.getConfig()
a59f210f 396 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
6fad8e51 397
65e6e260 398 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
6fad8e51 399 })
5bcfd029 400
6fad8e51 401 it('Should have valid index html updated tags (title, description...)', async function () {
65e6e260 402 const config = await servers[0].configCommand.getConfig()
a59f210f 403 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
5bcfd029 404
65e6e260 405 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
6fad8e51 406 })
e5024f51
TC
407
408 it('Should use the original video URL for the canonical tag', async function () {
a1eda903 409 for (const basePath of watchVideoBasePaths) {
d4a8e7a6
C
410 for (const id of videoIds) {
411 const res = await makeHTMLRequest(servers[1].url, basePath + id)
412 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
413 }
a1eda903 414 }
a59f210f
C
415 })
416
417 it('Should use the original account URL for the canonical tag', async function () {
65e6e260 418 const accountURLtest = res => {
9a911038
K
419 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/accounts/root" />`)
420 }
421
422 accountURLtest(await makeHTMLRequest(servers[1].url, '/accounts/root@' + servers[0].host))
423 accountURLtest(await makeHTMLRequest(servers[1].url, '/a/root@' + servers[0].host))
424 accountURLtest(await makeHTMLRequest(servers[1].url, '/@root@' + servers[0].host))
a59f210f
C
425 })
426
427 it('Should use the original channel URL for the canonical tag', async function () {
65e6e260 428 const channelURLtests = res => {
9a911038
K
429 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-channels/root_channel" />`)
430 }
431
432 channelURLtests(await makeHTMLRequest(servers[1].url, '/video-channels/root_channel@' + servers[0].host))
433 channelURLtests(await makeHTMLRequest(servers[1].url, '/c/root_channel@' + servers[0].host))
434 channelURLtests(await makeHTMLRequest(servers[1].url, '/@root_channel@' + servers[0].host))
a59f210f
C
435 })
436
437 it('Should use the original playlist URL for the canonical tag', async function () {
a1eda903 438 for (const basePath of watchPlaylistBasePaths) {
d4a8e7a6
C
439 for (const id of playlistIds) {
440 const res = await makeHTMLRequest(servers[1].url, basePath + id)
441 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-playlists/${playlist.uuid}" />`)
442 }
a1eda903 443 }
e5024f51 444 })
5bcfd029
C
445 })
446
aea0b0e7
C
447 describe('Embed HTML', function () {
448
449 it('Should have the correct embed html tags', async function () {
65e6e260 450 const config = await servers[0].configCommand.getConfig()
aea0b0e7
C
451 const res = await makeHTMLRequest(servers[0].url, servers[0].video.embedPath)
452
65e6e260 453 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
aea0b0e7
C
454 })
455 })
456
7c3b7976 457 after(async function () {
a59f210f 458 await cleanupTests(servers)
0e1dc3e7
C
459 })
460})