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, HTMLServerConfig, ServerConfig, VideoPlaylistPrivacy } from '@shared/models'
12 flushAndRunMultipleServers,
19 setAccessTokensToServers,
20 setDefaultVideoChannel,
22 updateCustomSubConfig,
27 } from '../../shared/extra-utils'
28 import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
29 import { omit } from 'lodash'
31 const expect = chai.expect
33 function checkIndexTags (html: string, title: string, description: string, css: string, config: ServerConfig) {
34 expect(html).to.contain('<title>' + title + '</title>')
35 expect(html).to.contain('<meta name="description" content="' + description + '" />')
36 expect(html).to.contain('<style class="custom-css-style">' + css + '</style>')
38 const htmlConfig: HTMLServerConfig = omit(config, 'signup')
39 expect(html).to.contain(`<script type="application/javascript">window.PeerTubeServerConfig = '${JSON.stringify(htmlConfig)}'</script>`)
42 describe('Test a client controllers', function () {
43 let servers: ServerInfo[] = []
46 const videoName = 'my super name for server 1'
47 const videoDescription = 'my<br> super __description__ for *server* 1<p></p>'
48 const videoDescriptionPlainText = 'my super description for server 1'
50 const playlistName = 'super playlist name'
51 const playlistDescription = 'super playlist description'
52 let playlistUUID: string
54 const channelDescription = 'my super channel description'
56 before(async function () {
59 servers = await flushAndRunMultipleServers(2)
61 await setAccessTokensToServers(servers)
63 await doubleFollow(servers[0], servers[1])
65 await setDefaultVideoChannel(servers)
67 await updateVideoChannel(servers[0].url, servers[0].accessToken, servers[0].videoChannel.name, { description: channelDescription })
71 const videoAttributes = { name: videoName, description: videoDescription }
72 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
74 const resVideosRequest = await getVideosList(servers[0].url)
75 const videos = resVideosRequest.body.data
76 expect(videos.length).to.equal(1)
78 servers[0].video = videos[0]
82 const playlistAttrs = {
83 displayName: playlistName,
84 description: playlistDescription,
85 privacy: VideoPlaylistPrivacy.PUBLIC,
86 videoChannelId: servers[0].videoChannel.id
89 const resVideoPlaylistRequest = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs })
91 const playlist = resVideoPlaylistRequest.body.videoPlaylist
92 const playlistId = playlist.id
93 playlistUUID = playlist.uuid
95 await addVideoInPlaylist({
97 token: servers[0].accessToken,
99 elementAttrs: { videoId: servers[0].video.id }
104 await updateMyUser({ url: servers[0].url, accessToken: servers[0].accessToken, description: 'my account description' })
106 const resAccountRequest = await getAccount(servers[0].url, `${servers[0].user.username}@${servers[0].host}`)
107 account = resAccountRequest.body
109 await waitJobs(servers)
112 describe('oEmbed', function () {
113 it('Should have valid oEmbed discovery tags for videos', async function () {
114 const path = '/videos/watch/' + servers[0].video.uuid
115 const res = await request(servers[0].url)
117 .set('Accept', 'text/html')
118 .expect(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}%2Fvideos%2Fwatch%2F${servers[0].video.uuid}" ` +
124 `title="${servers[0].video.name}" />`
126 expect(res.text).to.contain(expectedLink)
129 it('Should have valid oEmbed discovery tags for a playlist', async function () {
130 const res = await request(servers[0].url)
131 .get('/videos/watch/playlist/' + playlistUUID)
132 .set('Accept', 'text/html')
133 .expect(HttpStatusCode.OK_200)
135 const port = servers[0].port
137 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
138 `url=http%3A%2F%2Flocalhost%3A${port}%2Fvideos%2Fwatch%2Fplaylist%2F${playlistUUID}" ` +
139 `title="${playlistName}" />`
141 expect(res.text).to.contain(expectedLink)
145 describe('Open Graph', function () {
147 it('Should have valid Open Graph tags on the account page', async function () {
148 const res = await request(servers[0].url)
149 .get('/accounts/' + servers[0].user.username)
150 .set('Accept', 'text/html')
151 .expect(HttpStatusCode.OK_200)
153 expect(res.text).to.contain(`<meta property="og:title" content="${account.displayName}" />`)
154 expect(res.text).to.contain(`<meta property="og:description" content="${account.description}" />`)
155 expect(res.text).to.contain('<meta property="og:type" content="website" />')
156 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/accounts/${servers[0].user.username}" />`)
159 it('Should have valid Open Graph tags on the channel page', async function () {
160 const res = await request(servers[0].url)
161 .get('/video-channels/' + servers[0].videoChannel.name)
162 .set('Accept', 'text/html')
163 .expect(HttpStatusCode.OK_200)
165 expect(res.text).to.contain(`<meta property="og:title" content="${servers[0].videoChannel.displayName}" />`)
166 expect(res.text).to.contain(`<meta property="og:description" content="${channelDescription}" />`)
167 expect(res.text).to.contain('<meta property="og:type" content="website" />')
168 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/video-channels/${servers[0].videoChannel.name}" />`)
171 it('Should have valid Open Graph tags on the watch page with video id', async function () {
172 const res = await request(servers[0].url)
173 .get('/videos/watch/' + servers[0].video.id)
174 .set('Accept', 'text/html')
175 .expect(HttpStatusCode.OK_200)
177 expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`)
178 expect(res.text).to.contain(`<meta property="og:description" content="${videoDescriptionPlainText}" />`)
179 expect(res.text).to.contain('<meta property="og:type" content="video" />')
180 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
183 it('Should have valid Open Graph tags on the watch page with video uuid', async function () {
184 const res = await request(servers[0].url)
185 .get('/videos/watch/' + servers[0].video.uuid)
186 .set('Accept', 'text/html')
187 .expect(HttpStatusCode.OK_200)
189 expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`)
190 expect(res.text).to.contain(`<meta property="og:description" content="${videoDescriptionPlainText}" />`)
191 expect(res.text).to.contain('<meta property="og:type" content="video" />')
192 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
195 it('Should have valid Open Graph tags on the watch playlist page', async function () {
196 const res = await request(servers[0].url)
197 .get('/videos/watch/playlist/' + playlistUUID)
198 .set('Accept', 'text/html')
199 .expect(HttpStatusCode.OK_200)
201 expect(res.text).to.contain(`<meta property="og:title" content="${playlistName}" />`)
202 expect(res.text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`)
203 expect(res.text).to.contain('<meta property="og:type" content="video" />')
204 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/playlist/${playlistUUID}" />`)
208 describe('Twitter card', async function () {
210 it('Should have valid twitter card on the watch video page', async function () {
211 const res = await request(servers[0].url)
212 .get('/videos/watch/' + servers[0].video.uuid)
213 .set('Accept', 'text/html')
214 .expect(HttpStatusCode.OK_200)
216 expect(res.text).to.contain('<meta property="twitter:card" content="summary_large_image" />')
217 expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
218 expect(res.text).to.contain(`<meta property="twitter:title" content="${videoName}" />`)
219 expect(res.text).to.contain(`<meta property="twitter:description" content="${videoDescriptionPlainText}" />`)
222 it('Should have valid twitter card on the watch playlist page', async function () {
223 const res = await request(servers[0].url)
224 .get('/videos/watch/playlist/' + playlistUUID)
225 .set('Accept', 'text/html')
226 .expect(HttpStatusCode.OK_200)
228 expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
229 expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
230 expect(res.text).to.contain(`<meta property="twitter:title" content="${playlistName}" />`)
231 expect(res.text).to.contain(`<meta property="twitter:description" content="${playlistDescription}" />`)
234 it('Should have valid twitter card on the account page', async function () {
235 const res = await request(servers[0].url)
236 .get('/accounts/' + account.name)
237 .set('Accept', 'text/html')
238 .expect(HttpStatusCode.OK_200)
240 expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
241 expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
242 expect(res.text).to.contain(`<meta property="twitter:title" content="${account.name}" />`)
243 expect(res.text).to.contain(`<meta property="twitter:description" content="${account.description}" />`)
246 it('Should have valid twitter card on the channel page', async function () {
247 const res = await request(servers[0].url)
248 .get('/video-channels/' + servers[0].videoChannel.name)
249 .set('Accept', 'text/html')
250 .expect(HttpStatusCode.OK_200)
252 expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
253 expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
254 expect(res.text).to.contain(`<meta property="twitter:title" content="${servers[0].videoChannel.displayName}" />`)
255 expect(res.text).to.contain(`<meta property="twitter:description" content="${channelDescription}" />`)
258 it('Should have valid twitter card if Twitter is whitelisted', async function () {
259 const res1 = await getCustomConfig(servers[0].url, servers[0].accessToken)
260 const config = res1.body
261 config.services.twitter = {
265 await updateCustomConfig(servers[0].url, servers[0].accessToken, config)
267 const resVideoRequest = await request(servers[0].url)
268 .get('/videos/watch/' + servers[0].video.uuid)
269 .set('Accept', 'text/html')
270 .expect(HttpStatusCode.OK_200)
272 expect(resVideoRequest.text).to.contain('<meta property="twitter:card" content="player" />')
273 expect(resVideoRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
275 const resVideoPlaylistRequest = await request(servers[0].url)
276 .get('/videos/watch/playlist/' + playlistUUID)
277 .set('Accept', 'text/html')
278 .expect(HttpStatusCode.OK_200)
280 expect(resVideoPlaylistRequest.text).to.contain('<meta property="twitter:card" content="player" />')
281 expect(resVideoPlaylistRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
283 const resAccountRequest = await request(servers[0].url)
284 .get('/accounts/' + account.name)
285 .set('Accept', 'text/html')
286 .expect(HttpStatusCode.OK_200)
288 expect(resAccountRequest.text).to.contain('<meta property="twitter:card" content="summary" />')
289 expect(resAccountRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
291 const resChannelRequest = await request(servers[0].url)
292 .get('/video-channels/' + servers[0].videoChannel.name)
293 .set('Accept', 'text/html')
294 .expect(HttpStatusCode.OK_200)
296 expect(resChannelRequest.text).to.contain('<meta property="twitter:card" content="summary" />')
297 expect(resChannelRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
301 describe('Index HTML', function () {
303 it('Should have valid index html tags (title, description...)', async function () {
304 const resConfig = await getConfig(servers[0].url)
305 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
307 const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
308 checkIndexTags(res.text, 'PeerTube', description, '', resConfig.body)
311 it('Should update the customized configuration and have the correct index html tags', async function () {
312 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, {
314 name: 'PeerTube updated',
315 shortDescription: 'my short description',
316 description: 'my super description',
317 terms: 'my super terms',
318 defaultNSFWPolicy: 'blur',
319 defaultClientRoute: '/videos/recently-added',
321 javascript: 'alert("coucou")',
322 css: 'body { background-color: red; }'
327 const resConfig = await getConfig(servers[0].url)
328 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
330 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body)
333 it('Should have valid index html updated tags (title, description...)', async function () {
334 const resConfig = await getConfig(servers[0].url)
335 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
337 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body)
340 it('Should use the original video URL for the canonical tag', async function () {
341 const res = await makeHTMLRequest(servers[1].url, '/videos/watch/' + servers[0].video.uuid)
342 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
345 it('Should use the original account URL for the canonical tag', async function () {
346 const res = await makeHTMLRequest(servers[1].url, '/accounts/root@' + servers[0].host)
347 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/accounts/root" />`)
350 it('Should use the original channel URL for the canonical tag', async function () {
351 const res = await makeHTMLRequest(servers[1].url, '/video-channels/root_channel@' + servers[0].host)
352 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-channels/root_channel" />`)
355 it('Should use the original playlist URL for the canonical tag', async function () {
356 const res = await makeHTMLRequest(servers[1].url, '/videos/watch/playlist/' + playlistUUID)
357 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-playlists/${playlistUUID}" />`)
361 describe('Embed HTML', function () {
363 it('Should have the correct embed html tags', async function () {
364 const resConfig = await getConfig(servers[0].url)
365 const res = await makeHTMLRequest(servers[0].url, servers[0].video.embedPath)
367 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body)
371 after(async function () {
372 await cleanupTests(servers)