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 super description for server 1'
44 const playlistName = 'super playlist name'
45 const playlistDescription = 'super playlist description'
46 let playlistUUID: string
48 const channelDescription = 'my super channel description'
50 before(async function () {
53 servers = await flushAndRunMultipleServers(2)
55 await setAccessTokensToServers(servers)
57 await doubleFollow(servers[0], servers[1])
59 await setDefaultVideoChannel(servers)
61 await updateVideoChannel(servers[0].url, servers[0].accessToken, servers[0].videoChannel.name, { description: channelDescription })
65 const videoAttributes = { name: videoName, description: videoDescription }
66 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
68 const resVideosRequest = await getVideosList(servers[0].url)
69 const videos = resVideosRequest.body.data
70 expect(videos.length).to.equal(1)
72 servers[0].video = videos[0]
76 const playlistAttrs = {
77 displayName: playlistName,
78 description: playlistDescription,
79 privacy: VideoPlaylistPrivacy.PUBLIC,
80 videoChannelId: servers[0].videoChannel.id
83 const resVideoPlaylistRequest = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs })
85 const playlist = resVideoPlaylistRequest.body.videoPlaylist
86 const playlistId = playlist.id
87 playlistUUID = playlist.uuid
89 await addVideoInPlaylist({
91 token: servers[0].accessToken,
93 elementAttrs: { videoId: servers[0].video.id }
98 await updateMyUser({ url: servers[0].url, accessToken: servers[0].accessToken, description: 'my account description' })
100 const resAccountRequest = await getAccount(servers[0].url, `${servers[0].user.username}@${servers[0].host}`)
101 account = resAccountRequest.body
103 await waitJobs(servers)
106 describe('oEmbed', function () {
107 it('Should have valid oEmbed discovery tags for videos', async function () {
108 const path = '/videos/watch/' + servers[0].video.uuid
109 const res = await request(servers[0].url)
111 .set('Accept', 'text/html')
112 .expect(HttpStatusCode.OK_200)
114 const port = servers[0].port
116 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
117 `url=http%3A%2F%2Flocalhost%3A${port}%2Fvideos%2Fwatch%2F${servers[0].video.uuid}" ` +
118 `title="${servers[0].video.name}" />`
120 expect(res.text).to.contain(expectedLink)
123 it('Should have valid oEmbed discovery tags for a playlist', async function () {
124 const res = await request(servers[0].url)
125 .get('/videos/watch/playlist/' + playlistUUID)
126 .set('Accept', 'text/html')
127 .expect(HttpStatusCode.OK_200)
129 const port = servers[0].port
131 const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
132 `url=http%3A%2F%2Flocalhost%3A${port}%2Fvideos%2Fwatch%2Fplaylist%2F${playlistUUID}" ` +
133 `title="${playlistName}" />`
135 expect(res.text).to.contain(expectedLink)
139 describe('Open Graph', function () {
141 it('Should have valid Open Graph tags on the account page', async function () {
142 const res = await request(servers[0].url)
143 .get('/accounts/' + servers[0].user.username)
144 .set('Accept', 'text/html')
145 .expect(HttpStatusCode.OK_200)
147 expect(res.text).to.contain(`<meta property="og:title" content="${account.displayName}" />`)
148 expect(res.text).to.contain(`<meta property="og:description" content="${account.description}" />`)
149 expect(res.text).to.contain('<meta property="og:type" content="website" />')
150 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/accounts/${servers[0].user.username}" />`)
153 it('Should have valid Open Graph tags on the channel page', async function () {
154 const res = await request(servers[0].url)
155 .get('/video-channels/' + servers[0].videoChannel.name)
156 .set('Accept', 'text/html')
157 .expect(HttpStatusCode.OK_200)
159 expect(res.text).to.contain(`<meta property="og:title" content="${servers[0].videoChannel.displayName}" />`)
160 expect(res.text).to.contain(`<meta property="og:description" content="${channelDescription}" />`)
161 expect(res.text).to.contain('<meta property="og:type" content="website" />')
162 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/video-channels/${servers[0].videoChannel.name}" />`)
165 it('Should have valid Open Graph tags on the watch page with video id', async function () {
166 const res = await request(servers[0].url)
167 .get('/videos/watch/' + servers[0].video.id)
168 .set('Accept', 'text/html')
169 .expect(HttpStatusCode.OK_200)
171 expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`)
172 expect(res.text).to.contain(`<meta property="og:description" content="${videoDescription}" />`)
173 expect(res.text).to.contain('<meta property="og:type" content="video" />')
174 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
177 it('Should have valid Open Graph tags on the watch page with video uuid', async function () {
178 const res = await request(servers[0].url)
179 .get('/videos/watch/' + servers[0].video.uuid)
180 .set('Accept', 'text/html')
181 .expect(HttpStatusCode.OK_200)
183 expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`)
184 expect(res.text).to.contain(`<meta property="og:description" content="${videoDescription}" />`)
185 expect(res.text).to.contain('<meta property="og:type" content="video" />')
186 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
189 it('Should have valid Open Graph tags on the watch playlist page', async function () {
190 const res = await request(servers[0].url)
191 .get('/videos/watch/playlist/' + playlistUUID)
192 .set('Accept', 'text/html')
193 .expect(HttpStatusCode.OK_200)
195 expect(res.text).to.contain(`<meta property="og:title" content="${playlistName}" />`)
196 expect(res.text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`)
197 expect(res.text).to.contain('<meta property="og:type" content="video" />')
198 expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/playlist/${playlistUUID}" />`)
202 describe('Twitter card', async function () {
204 it('Should have valid twitter card on the watch video page', async function () {
205 const res = await request(servers[0].url)
206 .get('/videos/watch/' + servers[0].video.uuid)
207 .set('Accept', 'text/html')
208 .expect(HttpStatusCode.OK_200)
210 expect(res.text).to.contain('<meta property="twitter:card" content="summary_large_image" />')
211 expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
212 expect(res.text).to.contain(`<meta property="twitter:title" content="${videoName}" />`)
213 expect(res.text).to.contain(`<meta property="twitter:description" content="${videoDescription}" />`)
216 it('Should have valid twitter card on the watch playlist page', async function () {
217 const res = await request(servers[0].url)
218 .get('/videos/watch/playlist/' + playlistUUID)
219 .set('Accept', 'text/html')
220 .expect(HttpStatusCode.OK_200)
222 expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
223 expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
224 expect(res.text).to.contain(`<meta property="twitter:title" content="${playlistName}" />`)
225 expect(res.text).to.contain(`<meta property="twitter:description" content="${playlistDescription}" />`)
228 it('Should have valid twitter card on the account page', async function () {
229 const res = await request(servers[0].url)
230 .get('/accounts/' + account.name)
231 .set('Accept', 'text/html')
232 .expect(HttpStatusCode.OK_200)
234 expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
235 expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
236 expect(res.text).to.contain(`<meta property="twitter:title" content="${account.name}" />`)
237 expect(res.text).to.contain(`<meta property="twitter:description" content="${account.description}" />`)
240 it('Should have valid twitter card on the channel page', async function () {
241 const res = await request(servers[0].url)
242 .get('/video-channels/' + servers[0].videoChannel.name)
243 .set('Accept', 'text/html')
244 .expect(HttpStatusCode.OK_200)
246 expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
247 expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
248 expect(res.text).to.contain(`<meta property="twitter:title" content="${servers[0].videoChannel.displayName}" />`)
249 expect(res.text).to.contain(`<meta property="twitter:description" content="${channelDescription}" />`)
252 it('Should have valid twitter card if Twitter is whitelisted', async function () {
253 const res1 = await getCustomConfig(servers[0].url, servers[0].accessToken)
254 const config = res1.body
255 config.services.twitter = {
259 await updateCustomConfig(servers[0].url, servers[0].accessToken, config)
261 const resVideoRequest = await request(servers[0].url)
262 .get('/videos/watch/' + servers[0].video.uuid)
263 .set('Accept', 'text/html')
264 .expect(HttpStatusCode.OK_200)
266 expect(resVideoRequest.text).to.contain('<meta property="twitter:card" content="player" />')
267 expect(resVideoRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
269 const resVideoPlaylistRequest = await request(servers[0].url)
270 .get('/videos/watch/playlist/' + playlistUUID)
271 .set('Accept', 'text/html')
272 .expect(HttpStatusCode.OK_200)
274 expect(resVideoPlaylistRequest.text).to.contain('<meta property="twitter:card" content="player" />')
275 expect(resVideoPlaylistRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
277 const resAccountRequest = await request(servers[0].url)
278 .get('/accounts/' + account.name)
279 .set('Accept', 'text/html')
280 .expect(HttpStatusCode.OK_200)
282 expect(resAccountRequest.text).to.contain('<meta property="twitter:card" content="summary" />')
283 expect(resAccountRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
285 const resChannelRequest = await request(servers[0].url)
286 .get('/video-channels/' + servers[0].videoChannel.name)
287 .set('Accept', 'text/html')
288 .expect(HttpStatusCode.OK_200)
290 expect(resChannelRequest.text).to.contain('<meta property="twitter:card" content="summary" />')
291 expect(resChannelRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
295 describe('Index HTML', function () {
297 it('Should have valid index html tags (title, description...)', async function () {
298 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
300 const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
301 checkIndexTags(res.text, 'PeerTube', description, '')
304 it('Should update the customized configuration and have the correct index html tags', async function () {
305 await updateCustomSubConfig(servers[0].url, servers[0].accessToken, {
307 name: 'PeerTube updated',
308 shortDescription: 'my short description',
309 description: 'my super description',
310 terms: 'my super terms',
311 defaultNSFWPolicy: 'blur',
312 defaultClientRoute: '/videos/recently-added',
314 javascript: 'alert("coucou")',
315 css: 'body { background-color: red; }'
320 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
322 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }')
325 it('Should have valid index html updated tags (title, description...)', async function () {
326 const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
328 checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }')
331 it('Should use the original video URL for the canonical tag', async function () {
332 const res = await makeHTMLRequest(servers[1].url, '/videos/watch/' + servers[0].video.uuid)
333 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
336 it('Should use the original account URL for the canonical tag', async function () {
337 const res = await makeHTMLRequest(servers[1].url, '/accounts/root@' + servers[0].host)
338 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/accounts/root" />`)
341 it('Should use the original channel URL for the canonical tag', async function () {
342 const res = await makeHTMLRequest(servers[1].url, '/video-channels/root_channel@' + servers[0].host)
343 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-channels/root_channel" />`)
346 it('Should use the original playlist URL for the canonical tag', async function () {
347 const res = await makeHTMLRequest(servers[1].url, '/videos/watch/playlist/' + playlistUUID)
348 expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-playlists/${playlistUUID}" />`)
352 after(async function () {
353 await cleanupTests(servers)