1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
4 import * as chai from 'chai'
5 import { omit } from 'lodash'
6 import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
7 import { Account, HTMLServerConfig, ServerConfig, VideoPlaylistCreateResult, VideoPlaylistPrivacy } from '@shared/models'
11 flushAndRunMultipleServers,
15 setAccessTokensToServers,
16 setDefaultVideoChannel,
18 } from '../../shared/extra-utils'
20 const expect = chai.expect
22 function checkIndexTags (html: string, title: string, description: string, css: string, config: ServerConfig) {
23 expect(html).to.contain('<title>' + title + '</title>')
24 expect(html).to.contain('<meta name="description" content="' + description + '" />')
25 expect(html).to.contain('<style class="custom-css-style">' + css + '</style>')
27 const htmlConfig: HTMLServerConfig = omit(config, 'signup')
28 expect(html).to.contain(`<script type="application/javascript">window.PeerTubeServerConfig = '${JSON.stringify(htmlConfig)}'</script>`)
31 describe('Test a client controllers', function () {
32 let servers: ServerInfo[] = []
35 const videoName = 'my super name for server 1'
36 const videoDescription = 'my<br> super __description__ for *server* 1<p></p>'
37 const videoDescriptionPlainText = 'my super description for server 1'
39 const playlistName = 'super playlist name'
40 const playlistDescription = 'super playlist description'
41 let playlist: VideoPlaylistCreateResult
43 const channelDescription = 'my super channel description'
45 const watchVideoBasePaths = [ '/videos/watch/', '/w/' ]
46 const watchPlaylistBasePaths = [ '/videos/watch/playlist/', '/w/p/' ]
48 let videoIds: (string | number)[] = []
49 let playlistIds: (string | number)[] = []
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 servers[0].channelsCommand.update({
63 channelName: servers[0].videoChannel.name,
64 attributes: { description: channelDescription }
70 const attributes = { name: videoName, description: videoDescription }
71 await servers[0].videosCommand.upload({ attributes })
73 const { data } = await servers[0].videosCommand.list()
74 expect(data.length).to.equal(1)
77 servers[0].video = video
78 videoIds = [ video.id, video.uuid, video.shortUUID ]
85 displayName: playlistName,
86 description: playlistDescription,
87 privacy: VideoPlaylistPrivacy.PUBLIC,
88 videoChannelId: servers[0].videoChannel.id
91 playlist = await servers[0].playlistsCommand.create({ attributes })
92 playlistIds = [ playlist.id, playlist.shortUUID, playlist.uuid ]
94 await servers[0].playlistsCommand.addElement({ playlistId: playlist.shortUUID, attributes: { videoId: servers[0].video.id } })
100 await servers[0].usersCommand.updateMe({ description: 'my account description' })
102 account = await servers[0].accountsCommand.get({ accountName: `${servers[0].user.username}@${servers[0].host}` })
105 await waitJobs(servers)
108 describe('oEmbed', function () {
110 it('Should have valid oEmbed discovery tags for videos', async function () {
111 for (const basePath of watchVideoBasePaths) {
112 for (const id of videoIds) {
113 const res = await makeGetRequest({
117 statusCodeExpected: HttpStatusCode.OK_200
120 const port = servers[0].port
122 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
123 `url=http%3A%2F%2Flocalhost%3A${port}%2Fw%2F${servers[0].video.uuid}" ` +
124 `title="${servers[0].video.name}" />`
126 expect(res.text).to.contain(expectedLink)
131 it('Should have valid oEmbed discovery tags for a playlist', async function () {
132 for (const basePath of watchPlaylistBasePaths) {
133 for (const id of playlistIds) {
134 const res = await makeGetRequest({
138 statusCodeExpected: HttpStatusCode.OK_200
141 const port = servers[0].port
143 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
144 `url=http%3A%2F%2Flocalhost%3A${port}%2Fw%2Fp%2F${playlist.uuid}" ` +
145 `title="${playlistName}" />`
147 expect(res.text).to.contain(expectedLink)
153 describe('Open Graph', function () {
155 async function accountPageTest (path: string) {
156 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
157 const text = res.text
159 expect(text).to.contain(`<meta property="og:title" content="${account.displayName}" />`)
160 expect(text).to.contain(`<meta property="og:description" content="${account.description}" />`)
161 expect(text).to.contain('<meta property="og:type" content="website" />')
162 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/accounts/${servers[0].user.username}" />`)
165 async function channelPageTest (path: string) {
166 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
167 const text = res.text
169 expect(text).to.contain(`<meta property="og:title" content="${servers[0].videoChannel.displayName}" />`)
170 expect(text).to.contain(`<meta property="og:description" content="${channelDescription}" />`)
171 expect(text).to.contain('<meta property="og:type" content="website" />')
172 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/video-channels/${servers[0].videoChannel.name}" />`)
175 async function watchVideoPageTest (path: string) {
176 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
177 const text = res.text
179 expect(text).to.contain(`<meta property="og:title" content="${videoName}" />`)
180 expect(text).to.contain(`<meta property="og:description" content="${videoDescriptionPlainText}" />`)
181 expect(text).to.contain('<meta property="og:type" content="video" />')
182 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/${servers[0].video.uuid}" />`)
185 async function watchPlaylistPageTest (path: string) {
186 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
187 const text = res.text
189 expect(text).to.contain(`<meta property="og:title" content="${playlistName}" />`)
190 expect(text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`)
191 expect(text).to.contain('<meta property="og:type" content="video" />')
192 expect(text).to.contain(`<meta property="og:url" content="${servers[0].url}/w/p/${playlist.uuid}" />`)
195 it('Should have valid Open Graph tags on the account page', async function () {
196 await accountPageTest('/accounts/' + servers[0].user.username)
197 await accountPageTest('/a/' + servers[0].user.username)
198 await accountPageTest('/@' + servers[0].user.username)
201 it('Should have valid Open Graph tags on the channel page', async function () {
202 await channelPageTest('/video-channels/' + servers[0].videoChannel.name)
203 await channelPageTest('/c/' + servers[0].videoChannel.name)
204 await channelPageTest('/@' + servers[0].videoChannel.name)
207 it('Should have valid Open Graph tags on the watch page', async function () {
208 for (const path of watchVideoBasePaths) {
209 for (const id of videoIds) {
210 await watchVideoPageTest(path + id)
215 it('Should have valid Open Graph tags on the watch playlist page', async function () {
216 for (const path of watchPlaylistBasePaths) {
217 for (const id of playlistIds) {
218 await watchPlaylistPageTest(path + id)
224 describe('Twitter card', async function () {
226 describe('Not whitelisted', function () {
228 async function accountPageTest (path: string) {
229 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
230 const text = res.text
232 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
233 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
234 expect(text).to.contain(`<meta property="twitter:title" content="${account.name}" />`)
235 expect(text).to.contain(`<meta property="twitter:description" content="${account.description}" />`)
238 async function channelPageTest (path: string) {
239 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
240 const text = res.text
242 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
243 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
244 expect(text).to.contain(`<meta property="twitter:title" content="${servers[0].videoChannel.displayName}" />`)
245 expect(text).to.contain(`<meta property="twitter:description" content="${channelDescription}" />`)
248 async function watchVideoPageTest (path: string) {
249 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
250 const text = res.text
252 expect(text).to.contain('<meta property="twitter:card" content="summary_large_image" />')
253 expect(text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
254 expect(text).to.contain(`<meta property="twitter:title" content="${videoName}" />`)
255 expect(text).to.contain(`<meta property="twitter:description" content="${videoDescriptionPlainText}" />`)
258 async function watchPlaylistPageTest (path: string) {
259 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
260 const text = res.text
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="${playlistName}" />`)
265 expect(text).to.contain(`<meta property="twitter:description" content="${playlistDescription}" />`)
268 it('Should have valid twitter card on the watch video page', async function () {
269 for (const path of watchVideoBasePaths) {
270 for (const id of videoIds) {
271 await watchVideoPageTest(path + id)
276 it('Should have valid twitter card on the watch playlist page', async function () {
277 for (const path of watchPlaylistBasePaths) {
278 for (const id of playlistIds) {
279 await watchPlaylistPageTest(path + id)
284 it('Should have valid twitter card on the account page', async function () {
285 await accountPageTest('/accounts/' + account.name)
286 await accountPageTest('/a/' + account.name)
287 await accountPageTest('/@' + account.name)
290 it('Should have valid twitter card on the channel page', async function () {
291 await channelPageTest('/video-channels/' + servers[0].videoChannel.name)
292 await channelPageTest('/c/' + servers[0].videoChannel.name)
293 await channelPageTest('/@' + servers[0].videoChannel.name)
297 describe('Whitelisted', function () {
299 before(async function () {
300 const config = await servers[0].configCommand.getCustomConfig()
301 config.services.twitter = {
306 await servers[0].configCommand.updateCustomConfig({ newCustomConfig: config })
309 async function accountPageTest (path: string) {
310 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
311 const text = res.text
313 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
314 expect(text).to.contain('<meta property="twitter:site" content="@Kuja" />')
317 async function channelPageTest (path: string) {
318 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
319 const text = res.text
321 expect(text).to.contain('<meta property="twitter:card" content="summary" />')
322 expect(text).to.contain('<meta property="twitter:site" content="@Kuja" />')
325 async function watchVideoPageTest (path: string) {
326 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
327 const text = res.text
329 expect(text).to.contain('<meta property="twitter:card" content="player" />')
330 expect(text).to.contain('<meta property="twitter:site" content="@Kuja" />')
333 async function watchPlaylistPageTest (path: string) {
334 const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 })
335 const text = res.text
337 expect(text).to.contain('<meta property="twitter:card" content="player" />')
338 expect(text).to.contain('<meta property="twitter:site" content="@Kuja" />')
341 it('Should have valid twitter card on the watch video page', async function () {
342 for (const path of watchVideoBasePaths) {
343 for (const id of videoIds) {
344 await watchVideoPageTest(path + id)
349 it('Should have valid twitter card on the watch playlist page', async function () {
350 for (const path of watchPlaylistBasePaths) {
351 for (const id of playlistIds) {
352 await watchPlaylistPageTest(path + id)
357 it('Should have valid twitter card on the account page', async function () {
358 await accountPageTest('/accounts/' + account.name)
359 await accountPageTest('/a/' + account.name)
360 await accountPageTest('/@' + account.name)
363 it('Should have valid twitter card on the channel page', async function () {
364 await channelPageTest('/video-channels/' + servers[0].videoChannel.name)
365 await channelPageTest('/c/' + servers[0].videoChannel.name)
366 await channelPageTest('/@' + servers[0].videoChannel.name)
371 describe('Index HTML', function () {
373 it('Should have valid index html tags (title, description...)', async function () {
374 const config = await servers[0].configCommand.getConfig()
375 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
377 const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
378 checkIndexTags(res.text, 'PeerTube', description, '', config)
381 it('Should update the customized configuration and have the correct index html tags', async function () {
382 await servers[0].configCommand.updateCustomSubConfig({
385 name: 'PeerTube updated',
386 shortDescription: 'my short description',
387 description: 'my super description',
388 terms: 'my super terms',
389 defaultNSFWPolicy: 'blur',
390 defaultClientRoute: '/videos/recently-added',
392 javascript: 'alert("coucou")',
393 css: 'body { background-color: red; }'
399 const config = await servers[0].configCommand.getConfig()
400 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
402 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
405 it('Should have valid index html updated tags (title, description...)', async function () {
406 const config = await servers[0].configCommand.getConfig()
407 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
409 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
412 it('Should use the original video URL for the canonical tag', async function () {
413 for (const basePath of watchVideoBasePaths) {
414 for (const id of videoIds) {
415 const res = await makeHTMLRequest(servers[1].url, basePath + id)
416 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
421 it('Should use the original account URL for the canonical tag', async function () {
422 const accountURLtest = res => {
423 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/accounts/root" />`)
426 accountURLtest(await makeHTMLRequest(servers[1].url, '/accounts/root@' + servers[0].host))
427 accountURLtest(await makeHTMLRequest(servers[1].url, '/a/root@' + servers[0].host))
428 accountURLtest(await makeHTMLRequest(servers[1].url, '/@root@' + servers[0].host))
431 it('Should use the original channel URL for the canonical tag', async function () {
432 const channelURLtests = res => {
433 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-channels/root_channel" />`)
436 channelURLtests(await makeHTMLRequest(servers[1].url, '/video-channels/root_channel@' + servers[0].host))
437 channelURLtests(await makeHTMLRequest(servers[1].url, '/c/root_channel@' + servers[0].host))
438 channelURLtests(await makeHTMLRequest(servers[1].url, '/@root_channel@' + servers[0].host))
441 it('Should use the original playlist URL for the canonical tag', async function () {
442 for (const basePath of watchPlaylistBasePaths) {
443 for (const id of playlistIds) {
444 const res = await makeHTMLRequest(servers[1].url, basePath + id)
445 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-playlists/${playlist.uuid}" />`)
451 describe('Embed HTML', function () {
453 it('Should have the correct embed html tags', async function () {
454 const config = await servers[0].configCommand.getConfig()
455 const res = await makeHTMLRequest(servers[0].url, servers[0].video.embedPath)
457 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', config)
461 after(async function () {
462 await cleanupTests(servers)