client.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
  2. import 'mocha'
  3. import * as chai from 'chai'
  4. import * as request from 'supertest'
  5. import { Account, VideoPlaylistPrivacy } from '@shared/models'
  6. import {
  7. addVideoInPlaylist,
  8. cleanupTests,
  9. createVideoPlaylist,
  10. doubleFollow,
  11. flushAndRunMultipleServers,
  12. getAccount,
  13. getCustomConfig,
  14. getVideosList,
  15. makeHTMLRequest,
  16. ServerInfo,
  17. setAccessTokensToServers,
  18. setDefaultVideoChannel,
  19. updateCustomConfig,
  20. updateCustomSubConfig,
  21. updateMyUser,
  22. updateVideoChannel,
  23. uploadVideo,
  24. waitJobs
  25. } from '../../shared/extra-utils'
  26. import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
  27. const expect = chai.expect
  28. function checkIndexTags (html: string, title: string, description: string, css: string) {
  29. expect(html).to.contain('<title>' + title + '</title>')
  30. expect(html).to.contain('<meta name="description" content="' + description + '" />')
  31. expect(html).to.contain('<style class="custom-css-style">' + css + '</style>')
  32. }
  33. describe('Test a client controllers', function () {
  34. let servers: ServerInfo[] = []
  35. let account: Account
  36. const videoName = 'my super name for server 1'
  37. const videoDescription = 'my super description for server 1'
  38. const playlistName = 'super playlist name'
  39. const playlistDescription = 'super playlist description'
  40. let playlistUUID: string
  41. const channelDescription = 'my super channel description'
  42. before(async function () {
  43. this.timeout(120000)
  44. servers = await flushAndRunMultipleServers(2)
  45. await setAccessTokensToServers(servers)
  46. await doubleFollow(servers[0], servers[1])
  47. await setDefaultVideoChannel(servers)
  48. await updateVideoChannel(servers[0].url, servers[0].accessToken, servers[0].videoChannel.name, { description: channelDescription })
  49. // Video
  50. const videoAttributes = { name: videoName, description: videoDescription }
  51. await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
  52. const resVideosRequest = await getVideosList(servers[0].url)
  53. const videos = resVideosRequest.body.data
  54. expect(videos.length).to.equal(1)
  55. servers[0].video = videos[0]
  56. // Playlist
  57. const playlistAttrs = {
  58. displayName: playlistName,
  59. description: playlistDescription,
  60. privacy: VideoPlaylistPrivacy.PUBLIC,
  61. videoChannelId: servers[0].videoChannel.id
  62. }
  63. const resVideoPlaylistRequest = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs })
  64. const playlist = resVideoPlaylistRequest.body.videoPlaylist
  65. const playlistId = playlist.id
  66. playlistUUID = playlist.uuid
  67. await addVideoInPlaylist({
  68. url: servers[0].url,
  69. token: servers[0].accessToken,
  70. playlistId,
  71. elementAttrs: { videoId: servers[0].video.id }
  72. })
  73. // Account
  74. await updateMyUser({ url: servers[0].url, accessToken: servers[0].accessToken, description: 'my account description' })
  75. const resAccountRequest = await getAccount(servers[0].url, `${servers[0].user.username}@${servers[0].host}`)
  76. account = resAccountRequest.body
  77. await waitJobs(servers)
  78. })
  79. describe('oEmbed', function () {
  80. it('Should have valid oEmbed discovery tags for videos', async function () {
  81. const path = '/videos/watch/' + servers[0].video.uuid
  82. const res = await request(servers[0].url)
  83. .get(path)
  84. .set('Accept', 'text/html')
  85. .expect(HttpStatusCode.OK_200)
  86. const port = servers[0].port
  87. const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
  88. `url=http%3A%2F%2Flocalhost%3A${port}%2Fvideos%2Fwatch%2F${servers[0].video.uuid}" ` +
  89. `title="${servers[0].video.name}" />`
  90. expect(res.text).to.contain(expectedLink)
  91. })
  92. it('Should have valid oEmbed discovery tags for a playlist', async function () {
  93. const res = await request(servers[0].url)
  94. .get('/videos/watch/playlist/' + playlistUUID)
  95. .set('Accept', 'text/html')
  96. .expect(HttpStatusCode.OK_200)
  97. const port = servers[0].port
  98. const expectedLink = '<link rel="alternate" type="application/json+oembed" href="http://localhost:' + port + '/services/oembed?' +
  99. `url=http%3A%2F%2Flocalhost%3A${port}%2Fvideos%2Fwatch%2Fplaylist%2F${playlistUUID}" ` +
  100. `title="${playlistName}" />`
  101. expect(res.text).to.contain(expectedLink)
  102. })
  103. })
  104. describe('Open Graph', function () {
  105. it('Should have valid Open Graph tags on the account page', async function () {
  106. const res = await request(servers[0].url)
  107. .get('/accounts/' + servers[0].user.username)
  108. .set('Accept', 'text/html')
  109. .expect(HttpStatusCode.OK_200)
  110. expect(res.text).to.contain(`<meta property="og:title" content="${account.displayName}" />`)
  111. expect(res.text).to.contain(`<meta property="og:description" content="${account.description}" />`)
  112. expect(res.text).to.contain('<meta property="og:type" content="website" />')
  113. expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/accounts/${servers[0].user.username}" />`)
  114. })
  115. it('Should have valid Open Graph tags on the channel page', async function () {
  116. const res = await request(servers[0].url)
  117. .get('/video-channels/' + servers[0].videoChannel.name)
  118. .set('Accept', 'text/html')
  119. .expect(HttpStatusCode.OK_200)
  120. expect(res.text).to.contain(`<meta property="og:title" content="${servers[0].videoChannel.displayName}" />`)
  121. expect(res.text).to.contain(`<meta property="og:description" content="${channelDescription}" />`)
  122. expect(res.text).to.contain('<meta property="og:type" content="website" />')
  123. expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/video-channels/${servers[0].videoChannel.name}" />`)
  124. })
  125. it('Should have valid Open Graph tags on the watch page with video id', async function () {
  126. const res = await request(servers[0].url)
  127. .get('/videos/watch/' + servers[0].video.id)
  128. .set('Accept', 'text/html')
  129. .expect(HttpStatusCode.OK_200)
  130. expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`)
  131. expect(res.text).to.contain(`<meta property="og:description" content="${videoDescription}" />`)
  132. expect(res.text).to.contain('<meta property="og:type" content="video" />')
  133. expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
  134. })
  135. it('Should have valid Open Graph tags on the watch page with video uuid', async function () {
  136. const res = await request(servers[0].url)
  137. .get('/videos/watch/' + servers[0].video.uuid)
  138. .set('Accept', 'text/html')
  139. .expect(HttpStatusCode.OK_200)
  140. expect(res.text).to.contain(`<meta property="og:title" content="${videoName}" />`)
  141. expect(res.text).to.contain(`<meta property="og:description" content="${videoDescription}" />`)
  142. expect(res.text).to.contain('<meta property="og:type" content="video" />')
  143. expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
  144. })
  145. it('Should have valid Open Graph tags on the watch playlist page', async function () {
  146. const res = await request(servers[0].url)
  147. .get('/videos/watch/playlist/' + playlistUUID)
  148. .set('Accept', 'text/html')
  149. .expect(HttpStatusCode.OK_200)
  150. expect(res.text).to.contain(`<meta property="og:title" content="${playlistName}" />`)
  151. expect(res.text).to.contain(`<meta property="og:description" content="${playlistDescription}" />`)
  152. expect(res.text).to.contain('<meta property="og:type" content="video" />')
  153. expect(res.text).to.contain(`<meta property="og:url" content="${servers[0].url}/videos/watch/playlist/${playlistUUID}" />`)
  154. })
  155. })
  156. describe('Twitter card', async function () {
  157. it('Should have valid twitter card on the watch video page', async function () {
  158. const res = await request(servers[0].url)
  159. .get('/videos/watch/' + servers[0].video.uuid)
  160. .set('Accept', 'text/html')
  161. .expect(HttpStatusCode.OK_200)
  162. expect(res.text).to.contain('<meta property="twitter:card" content="summary_large_image" />')
  163. expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
  164. expect(res.text).to.contain(`<meta property="twitter:title" content="${videoName}" />`)
  165. expect(res.text).to.contain(`<meta property="twitter:description" content="${videoDescription}" />`)
  166. })
  167. it('Should have valid twitter card on the watch playlist page', async function () {
  168. const res = await request(servers[0].url)
  169. .get('/videos/watch/playlist/' + playlistUUID)
  170. .set('Accept', 'text/html')
  171. .expect(HttpStatusCode.OK_200)
  172. expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
  173. expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
  174. expect(res.text).to.contain(`<meta property="twitter:title" content="${playlistName}" />`)
  175. expect(res.text).to.contain(`<meta property="twitter:description" content="${playlistDescription}" />`)
  176. })
  177. it('Should have valid twitter card on the account page', async function () {
  178. const res = await request(servers[0].url)
  179. .get('/accounts/' + account.name)
  180. .set('Accept', 'text/html')
  181. .expect(HttpStatusCode.OK_200)
  182. expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
  183. expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
  184. expect(res.text).to.contain(`<meta property="twitter:title" content="${account.name}" />`)
  185. expect(res.text).to.contain(`<meta property="twitter:description" content="${account.description}" />`)
  186. })
  187. it('Should have valid twitter card on the channel page', async function () {
  188. const res = await request(servers[0].url)
  189. .get('/video-channels/' + servers[0].videoChannel.name)
  190. .set('Accept', 'text/html')
  191. .expect(HttpStatusCode.OK_200)
  192. expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
  193. expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
  194. expect(res.text).to.contain(`<meta property="twitter:title" content="${servers[0].videoChannel.displayName}" />`)
  195. expect(res.text).to.contain(`<meta property="twitter:description" content="${channelDescription}" />`)
  196. })
  197. it('Should have valid twitter card if Twitter is whitelisted', async function () {
  198. const res1 = await getCustomConfig(servers[0].url, servers[0].accessToken)
  199. const config = res1.body
  200. config.services.twitter = {
  201. username: '@Kuja',
  202. whitelisted: true
  203. }
  204. await updateCustomConfig(servers[0].url, servers[0].accessToken, config)
  205. const resVideoRequest = await request(servers[0].url)
  206. .get('/videos/watch/' + servers[0].video.uuid)
  207. .set('Accept', 'text/html')
  208. .expect(HttpStatusCode.OK_200)
  209. expect(resVideoRequest.text).to.contain('<meta property="twitter:card" content="player" />')
  210. expect(resVideoRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
  211. const resVideoPlaylistRequest = await request(servers[0].url)
  212. .get('/videos/watch/playlist/' + playlistUUID)
  213. .set('Accept', 'text/html')
  214. .expect(HttpStatusCode.OK_200)
  215. expect(resVideoPlaylistRequest.text).to.contain('<meta property="twitter:card" content="player" />')
  216. expect(resVideoPlaylistRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
  217. const resAccountRequest = await request(servers[0].url)
  218. .get('/accounts/' + account.name)
  219. .set('Accept', 'text/html')
  220. .expect(HttpStatusCode.OK_200)
  221. expect(resAccountRequest.text).to.contain('<meta property="twitter:card" content="summary" />')
  222. expect(resAccountRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
  223. const resChannelRequest = await request(servers[0].url)
  224. .get('/video-channels/' + servers[0].videoChannel.name)
  225. .set('Accept', 'text/html')
  226. .expect(HttpStatusCode.OK_200)
  227. expect(resChannelRequest.text).to.contain('<meta property="twitter:card" content="summary" />')
  228. expect(resChannelRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
  229. })
  230. })
  231. describe('Index HTML', function () {
  232. it('Should have valid index html tags (title, description...)', async function () {
  233. const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
  234. const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.'
  235. checkIndexTags(res.text, 'PeerTube', description, '')
  236. })
  237. it('Should update the customized configuration and have the correct index html tags', async function () {
  238. await updateCustomSubConfig(servers[0].url, servers[0].accessToken, {
  239. instance: {
  240. name: 'PeerTube updated',
  241. shortDescription: 'my short description',
  242. description: 'my super description',
  243. terms: 'my super terms',
  244. defaultClientRoute: '/videos/recently-added',
  245. defaultNSFWPolicy: 'blur',
  246. customizations: {
  247. javascript: 'alert("coucou")',
  248. css: 'body { background-color: red; }'
  249. }
  250. }
  251. })
  252. const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
  253. checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }')
  254. })
  255. it('Should have valid index html updated tags (title, description...)', async function () {
  256. const res = await makeHTMLRequest(servers[0].url, '/videos/trending')
  257. checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }')
  258. })
  259. it('Should use the original video URL for the canonical tag', async function () {
  260. const res = await makeHTMLRequest(servers[1].url, '/videos/watch/' + servers[0].video.uuid)
  261. expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/videos/watch/${servers[0].video.uuid}" />`)
  262. })
  263. it('Should use the original account URL for the canonical tag', async function () {
  264. const res = await makeHTMLRequest(servers[1].url, '/accounts/root@' + servers[0].host)
  265. expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/accounts/root" />`)
  266. })
  267. it('Should use the original channel URL for the canonical tag', async function () {
  268. const res = await makeHTMLRequest(servers[1].url, '/video-channels/root_channel@' + servers[0].host)
  269. expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-channels/root_channel" />`)
  270. })
  271. it('Should use the original playlist URL for the canonical tag', async function () {
  272. const res = await makeHTMLRequest(servers[1].url, '/videos/watch/playlist/' + playlistUUID)
  273. expect(res.text).to.contain(`<link rel="canonical" href="${servers[0].url}/video-playlists/${playlistUUID}" />`)
  274. })
  275. })
  276. after(async function () {
  277. await cleanupTests(servers)
  278. })
  279. })