1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
4 import * as chai from 'chai'
5 import * as request from 'supertest'
6 import { Account, VideoPlaylistPrivacy } from '@shared/models'
12 flushAndRunMultipleServers,
18 setAccessTokensToServers,
19 setDefaultVideoChannel,
21 updateCustomSubConfig,
26 } from '../../shared/extra-utils'
27 import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
29 const expect = chai.expect
31 function checkIndexTags (html: string, title: string, description: string, css: string) {
32 expect(html).to.contain('<title>' + title + '</title>')
33 expect(html).to.contain('<meta name="description" content="' + description + '" />')
34 expect(html).to.contain('<style class="custom-css-style">' + css + '</style>')
37 describe('Test a client controllers', function () {
38 let servers: ServerInfo[] = []
41 const videoName = 'my super name for server 1'
42 const videoDescription = 'my<br> super __description__ for *server* 1<p></p>'
43 const videoDescriptionPlainText = 'my super description for server 1'
45 const playlistName = 'super playlist name'
46 const playlistDescription = 'super playlist description'
47 let playlistUUID: string
49 const channelDescription = 'my super channel description'
51 before(async function () {
54 servers = await flushAndRunMultipleServers(2)
56 await setAccessTokensToServers(servers)
58 await doubleFollow(servers[0], servers[1])
60 await setDefaultVideoChannel(servers)
62 await updateVideoChannel(servers[0].url, servers[0].accessToken, servers[0].videoChannel.name, { description: channelDescription })
66 const videoAttributes = { name: videoName, description: videoDescription }
67 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
69 const resVideosRequest = await getVideosList(servers[0].url)
70 const videos = resVideosRequest.body.data
71 expect(videos.length).to.equal(1)
73 servers[0].video = videos[0]
77 const playlistAttrs = {
78 displayName: playlistName,
79 description: playlistDescription,
80 privacy: VideoPlaylistPrivacy.PUBLIC,
81 videoChannelId: servers[0].videoChannel.id
84 const resVideoPlaylistRequest = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs })
86 const playlist = resVideoPlaylistRequest.body.videoPlaylist
87 const playlistId = playlist.id
88 playlistUUID = playlist.uuid
90 await addVideoInPlaylist({
92 token: servers[0].accessToken,
94 elementAttrs: { videoId: servers[0].video.id }
99 await updateMyUser({ url: servers[0].url, accessToken: servers[0].accessToken, description: 'my account description' })
101 const resAccountRequest = await getAccount(servers[0].url, `${servers[0].user.username}@${servers[0].host}`)
102 account = resAccountRequest.body
104 await waitJobs(servers)
107 describe('oEmbed', function () {
108 it('Should have valid oEmbed discovery tags for videos', async function () {
109 const path = '/videos/watch/' + servers[0].video.uuid
110 const res = await request(servers[0].url)
112 .set('Accept', 'text/html')
113 .expect(HttpStatusCode.OK_200)
115 const port = servers[0].port
117 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
118 `url=http%3A%2F%2Flocalhost%3A${port}%2Fvideos%2Fwatch%2F${servers[0].video.uuid}" ` +
119 `title="${servers[0].video.name}" />`
121 expect(res.text).to.contain(expectedLink)
124 it('Should have valid oEmbed discovery tags for a playlist', async function () {
125 const res = await request(servers[0].url)
126 .get('/videos/watch/playlist/' + playlistUUID)
127 .set('Accept', 'text/html')
128 .expect(HttpStatusCode.OK_200)
130 const port = servers[0].port
132 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
133 `url=http%3A%2F%2Flocalhost%3A${port}%2Fvideos%2Fwatch%2Fplaylist%2F${playlistUUID}" ` +
134 `title="${playlistName}" />`
136 expect(res.text).to.contain(expectedLink)
140 describe('Open Graph', function () {
142 it('Should have valid Open Graph tags on the account page', async function () {
143 const accountPageTests = (res) => {
144 expect(res.text).to.contain(`<meta property="og:title" content="${account.displayName}" />`)
145 expect(res.text).to.contain(`<meta property="og:description" content="${account.description}" />`)
146 expect(res.text).to.contain('<meta property="og:type" content="website" />')
147 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/accounts/${servers[0].user.username}" />`)
150 accountPageTests(await request(servers[0].url)
151 .get('/accounts/' + servers[0].user.username)
152 .set('Accept', 'text/html')
153 .expect(HttpStatusCode.OK_200))
155 accountPageTests(await request(servers[0].url)
156 .get('/a/' + servers[0].user.username)
157 .set('Accept', 'text/html')
158 .expect(HttpStatusCode.OK_200))
160 accountPageTests(await request(servers[0].url)
161 .get('/@' + servers[0].user.username)
162 .set('Accept', 'text/html')
163 .expect(HttpStatusCode.OK_200))
166 it('Should have valid Open Graph tags on the channel page', async function () {
167 const channelPageOGtests = (res) => {
168 expect(res.text).to.contain(`<meta property="og:title" content="${servers[0].videoChannel.displayName}" />`)
169 expect(res.text).to.contain(`<meta property="og:description" content="${channelDescription}" />`)
170 expect(res.text).to.contain('<meta property="og:type" content="website" />')
171 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/video-channels/${servers[0].videoChannel.name}" />`)
174 channelPageOGtests(await request(servers[0].url)
175 .get('/video-channels/' + servers[0].videoChannel.name)
176 .set('Accept', 'text/html')
177 .expect(HttpStatusCode.OK_200))
179 channelPageOGtests(await request(servers[0].url)
180 .get('/c/' + servers[0].videoChannel.name)
181 .set('Accept', 'text/html')
182 .expect(HttpStatusCode.OK_200))
184 channelPageOGtests(await request(servers[0].url)
185 .get('/@' + servers[0].videoChannel.name)
186 .set('Accept', 'text/html')
187 .expect(HttpStatusCode.OK_200))
190 it('Should have valid Open Graph tags on the watch page with video id', async function () {
191 const res = await request(servers[0].url)
192 .get('/videos/watch/' + servers[0].video.id)
193 .set('Accept', 'text/html')
194 .expect(HttpStatusCode.OK_200)
196 expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`)
197 expect(res.text).to.contain(`<meta property="og:description" content="${videoDescriptionPlainText}" />`)
198 expect(res.text).to.contain('<meta property="og:type" content="video" />')
199 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
202 it('Should have valid Open Graph tags on the watch page with video uuid', async function () {
203 const res = await request(servers[0].url)
204 .get('/videos/watch/' + servers[0].video.uuid)
205 .set('Accept', 'text/html')
206 .expect(HttpStatusCode.OK_200)
208 expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`)
209 expect(res.text).to.contain(`<meta property="og:description" content="${videoDescriptionPlainText}" />`)
210 expect(res.text).to.contain('<meta property="og:type" content="video" />')
211 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
214 it('Should have valid Open Graph tags on the watch playlist page', async function () {
215 const res = await request(servers[0].url)
216 .get('/videos/watch/playlist/' + playlistUUID)
217 .set('Accept', 'text/html')
218 .expect(HttpStatusCode.OK_200)
220 expect(res.text).to.contain(`<meta property="og:title" content="${playlistName}" />`)
221 expect(res.text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`)
222 expect(res.text).to.contain('<meta property="og:type" content="video" />')
223 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/playlist/${playlistUUID}" />`)
227 describe('Twitter card', async function () {
229 it('Should have valid twitter card on the watch video page', async function () {
230 const res = await request(servers[0].url)
231 .get('/videos/watch/' + servers[0].video.uuid)
232 .set('Accept', 'text/html')
233 .expect(HttpStatusCode.OK_200)
235 expect(res.text).to.contain('<meta property="twitter:card" content="summary_large_image" />')
236 expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
237 expect(res.text).to.contain(`<meta property="twitter:title" content="${videoName}" />`)
238 expect(res.text).to.contain(`<meta property="twitter:description" content="${videoDescriptionPlainText}" />`)
241 it('Should have valid twitter card on the watch playlist page', async function () {
242 const res = await request(servers[0].url)
243 .get('/videos/watch/playlist/' + playlistUUID)
244 .set('Accept', 'text/html')
245 .expect(HttpStatusCode.OK_200)
247 expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
248 expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
249 expect(res.text).to.contain(`<meta property="twitter:title" content="${playlistName}" />`)
250 expect(res.text).to.contain(`<meta property="twitter:description" content="${playlistDescription}" />`)
253 it('Should have valid twitter card on the account page', async function () {
254 const accountPageTests = (res) => {
255 expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
256 expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
257 expect(res.text).to.contain(`<meta property="twitter:title" content="${account.name}" />`)
258 expect(res.text).to.contain(`<meta property="twitter:description" content="${account.description}" />`)
261 accountPageTests(await request(servers[0].url)
262 .get('/accounts/' + account.name)
263 .set('Accept', 'text/html')
264 .expect(HttpStatusCode.OK_200))
266 accountPageTests(await request(servers[0].url)
267 .get('/a/' + account.name)
268 .set('Accept', 'text/html')
269 .expect(HttpStatusCode.OK_200))
271 accountPageTests(await request(servers[0].url)
272 .get('/@' + account.name)
273 .set('Accept', 'text/html')
274 .expect(HttpStatusCode.OK_200))
277 it('Should have valid twitter card on the channel page', async function () {
278 const channelPageTests = (res) => {
279 expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
280 expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
281 expect(res.text).to.contain(`<meta property="twitter:title" content="${servers[0].videoChannel.displayName}" />`)
282 expect(res.text).to.contain(`<meta property="twitter:description" content="${channelDescription}" />`)
285 channelPageTests(await request(servers[0].url)
286 .get('/video-channels/' + servers[0].videoChannel.name)
287 .set('Accept', 'text/html')
288 .expect(HttpStatusCode.OK_200))
290 channelPageTests(await request(servers[0].url)
291 .get('/c/' + servers[0].videoChannel.name)
292 .set('Accept', 'text/html')
293 .expect(HttpStatusCode.OK_200))
295 channelPageTests(await request(servers[0].url)
296 .get('/@' + servers[0].videoChannel.name)
297 .set('Accept', 'text/html')
298 .expect(HttpStatusCode.OK_200))
301 it('Should have valid twitter card if Twitter is whitelisted', async function () {
302 const res1 = await getCustomConfig(servers[0].url, servers[0].accessToken)
303 const config = res1.body
304 config.services.twitter = {
308 await updateCustomConfig(servers[0].url, servers[0].accessToken, config)
310 const resVideoRequest = await request(servers[0].url)
311 .get('/videos/watch/' + servers[0].video.uuid)
312 .set('Accept', 'text/html')
313 .expect(HttpStatusCode.OK_200)
315 expect(resVideoRequest.text).to.contain('<meta property="twitter:card" content="player" />')
316 expect(resVideoRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
318 const resVideoPlaylistRequest = await request(servers[0].url)
319 .get('/videos/watch/playlist/' + playlistUUID)
320 .set('Accept', 'text/html')
321 .expect(HttpStatusCode.OK_200)
323 expect(resVideoPlaylistRequest.text).to.contain('<meta property="twitter:card" content="player" />')
324 expect(resVideoPlaylistRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
326 const accountTests = (res) => {
327 expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
328 expect(res.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
331 accountTests(await request(servers[0].url)
332 .get('/accounts/' + account.name)
333 .set('Accept', 'text/html')
334 .expect(HttpStatusCode.OK_200))
336 accountTests(await request(servers[0].url)
337 .get('/a/' + account.name)
338 .set('Accept', 'text/html')
339 .expect(HttpStatusCode.OK_200))
341 accountTests(await request(servers[0].url)
342 .get('/@' + account.name)
343 .set('Accept', 'text/html')
344 .expect(HttpStatusCode.OK_200))
346 const channelTests = (res) => {
347 expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
348 expect(res.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
351 channelTests(await request(servers[0].url)
352 .get('/video-channels/' + servers[0].videoChannel.name)
353 .set('Accept', 'text/html')
354 .expect(HttpStatusCode.OK_200))
356 channelTests(await request(servers[0].url)
357 .get('/c/' + servers[0].videoChannel.name)
358 .set('Accept', 'text/html')
359 .expect(HttpStatusCode.OK_200))
361 channelTests(await request(servers[0].url)
362 .get('/@' + servers[0].videoChannel.name)
363 .set('Accept', 'text/html')
364 .expect(HttpStatusCode.OK_200))
368 describe('Index HTML', function () {
370 it('Should have valid index html tags (title, description...)', async function () {
371 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
373 const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
374 checkIndexTags(res.text, 'PeerTube', description, '')
377 it('Should update the customized configuration and have the correct index html tags', async function () {
378 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, {
380 name: 'PeerTube updated',
381 shortDescription: 'my short description',
382 description: 'my super description',
383 terms: 'my super terms',
384 defaultNSFWPolicy: 'blur',
385 defaultClientRoute: '/videos/recently-added',
387 javascript: 'alert("coucou")',
388 css: 'body { background-color: red; }'
393 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
395 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }')
398 it('Should have valid index html updated tags (title, description...)', async function () {
399 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
401 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }')
404 it('Should use the original video URL for the canonical tag', async function () {
405 const res = await makeHTMLRequest(servers[1].url, '/videos/watch/' + servers[0].video.uuid)
406 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
409 it('Should use the original account URL for the canonical tag', async function () {
410 const accountURLtest = (res) => {
411 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/accounts/root" />`)
414 accountURLtest(await makeHTMLRequest(servers[1].url, '/accounts/root@' + servers[0].host))
415 accountURLtest(await makeHTMLRequest(servers[1].url, '/a/root@' + servers[0].host))
416 accountURLtest(await makeHTMLRequest(servers[1].url, '/@root@' + servers[0].host))
419 it('Should use the original channel URL for the canonical tag', async function () {
420 const channelURLtests = (res) => {
421 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-channels/root_channel" />`)
424 channelURLtests(await makeHTMLRequest(servers[1].url, '/video-channels/root_channel@' + servers[0].host))
425 channelURLtests(await makeHTMLRequest(servers[1].url, '/c/root_channel@' + servers[0].host))
426 channelURLtests(await makeHTMLRequest(servers[1].url, '/@root_channel@' + servers[0].host))
429 it('Should use the original playlist URL for the canonical tag', async function () {
430 const res = await makeHTMLRequest(servers[1].url, '/videos/watch/playlist/' + playlistUUID)
431 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-playlists/${playlistUUID}" />`)
435 after(async function () {
436 await cleanupTests(servers)