Add fcbk open-graph and twitter-card metas for accounts, video-channels, playlists urls (#2996)
* Add open-graph and twitter-card metas to accounts and video-channels * Add open-graph and twitter-card to video-playlists watch view * Refactor meta-tags creation server-side * Add client.ts tests for account, channel and playlist tags * Correct lint forbidden spaces * Correct test regression on client.ts Co-authored-by: kimsible <kimsible@users.noreply.github.com>
This commit is contained in:
parent
7b3909644d
commit
8d987ec63e
|
@ -17,6 +17,7 @@ const testEmbedPath = join(distPath, 'standalone', 'videos', 'test-embed.html')
|
|||
|
||||
// Special route that add OpenGraph and oEmbed tags
|
||||
// Do not use a template engine for a so little thing
|
||||
clientsRouter.use('/videos/watch/playlist/:id', asyncMiddleware(generateWatchPlaylistHtmlPage))
|
||||
clientsRouter.use('/videos/watch/:id', asyncMiddleware(generateWatchHtmlPage))
|
||||
clientsRouter.use('/accounts/:nameWithHost', asyncMiddleware(generateAccountHtmlPage))
|
||||
clientsRouter.use('/video-channels/:nameWithHost', asyncMiddleware(generateVideoChannelHtmlPage))
|
||||
|
@ -134,6 +135,12 @@ async function generateWatchHtmlPage (req: express.Request, res: express.Respons
|
|||
return sendHTML(html, res)
|
||||
}
|
||||
|
||||
async function generateWatchPlaylistHtmlPage (req: express.Request, res: express.Response) {
|
||||
const html = await ClientHtml.getWatchPlaylistHTMLPage(req.params.id + '', req, res)
|
||||
|
||||
return sendHTML(html, res)
|
||||
}
|
||||
|
||||
async function generateAccountHtmlPage (req: express.Request, res: express.Response) {
|
||||
const html = await ClientHtml.getAccountHTMLPage(req.params.nameWithHost, req, res)
|
||||
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
import * as express from 'express'
|
||||
import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/models/i18n/i18n'
|
||||
import { CUSTOM_HTML_TAG_COMMENTS, EMBED_SIZE, PLUGIN_GLOBAL_CSS_PATH, WEBSERVER, FILES_CONTENT_HASH } from '../initializers/constants'
|
||||
import {
|
||||
AVATARS_SIZE,
|
||||
CUSTOM_HTML_TAG_COMMENTS,
|
||||
EMBED_SIZE,
|
||||
PLUGIN_GLOBAL_CSS_PATH,
|
||||
WEBSERVER,
|
||||
FILES_CONTENT_HASH
|
||||
} from '../initializers/constants'
|
||||
import { join } from 'path'
|
||||
import { escapeHTML, sha256 } from '../helpers/core-utils'
|
||||
import { VideoModel } from '../models/video/video'
|
||||
import { VideoPlaylistModel } from '../models/video/video-playlist'
|
||||
import validator from 'validator'
|
||||
import { VideoPrivacy } from '../../shared/models/videos'
|
||||
import { VideoPrivacy, VideoPlaylistPrivacy } from '../../shared/models/videos'
|
||||
import { readFile } from 'fs-extra'
|
||||
import { getActivityStreamDuration } from '../models/video/video-format-utils'
|
||||
import { AccountModel } from '../models/account/account'
|
||||
|
@ -13,7 +21,7 @@ import { VideoChannelModel } from '../models/video/video-channel'
|
|||
import * as Bluebird from 'bluebird'
|
||||
import { CONFIG } from '../initializers/config'
|
||||
import { logger } from '../helpers/logger'
|
||||
import { MAccountActor, MChannelActor, MVideo } from '../types/models'
|
||||
import { MAccountActor, MChannelActor } from '../types/models'
|
||||
|
||||
export class ClientHtml {
|
||||
|
||||
|
@ -56,7 +64,69 @@ export class ClientHtml {
|
|||
|
||||
let customHtml = ClientHtml.addTitleTag(html, escapeHTML(video.name))
|
||||
customHtml = ClientHtml.addDescriptionTag(customHtml, escapeHTML(video.description))
|
||||
customHtml = ClientHtml.addVideoOpenGraphAndOEmbedTags(customHtml, video)
|
||||
|
||||
const url = WEBSERVER.URL + video.getWatchStaticPath()
|
||||
const title = escapeHTML(video.name)
|
||||
const description = escapeHTML(video.description)
|
||||
|
||||
const image = {
|
||||
url: WEBSERVER.URL + video.getPreviewStaticPath()
|
||||
}
|
||||
|
||||
const embed = {
|
||||
url: WEBSERVER.URL + video.getEmbedStaticPath(),
|
||||
createdAt: video.createdAt.toISOString(),
|
||||
duration: getActivityStreamDuration(video.duration),
|
||||
views: video.views
|
||||
}
|
||||
|
||||
const ogType = 'video'
|
||||
const twitterCard = CONFIG.SERVICES.TWITTER.WHITELISTED ? 'player' : 'summary_large_image'
|
||||
const schemaType = 'VideoObject'
|
||||
|
||||
customHtml = ClientHtml.addTags(customHtml, { url, title, description, image, embed, ogType, twitterCard, schemaType })
|
||||
|
||||
return customHtml
|
||||
}
|
||||
|
||||
static async getWatchPlaylistHTMLPage (videoPlaylistId: string, req: express.Request, res: express.Response) {
|
||||
// Let Angular application handle errors
|
||||
if (!validator.isInt(videoPlaylistId) && !validator.isUUID(videoPlaylistId, 4)) {
|
||||
res.status(404)
|
||||
return ClientHtml.getIndexHTML(req, res)
|
||||
}
|
||||
|
||||
const [ html, videoPlaylist ] = await Promise.all([
|
||||
ClientHtml.getIndexHTML(req, res),
|
||||
VideoPlaylistModel.loadWithAccountAndChannel(videoPlaylistId, null)
|
||||
])
|
||||
|
||||
// Let Angular application handle errors
|
||||
if (!videoPlaylist || videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) {
|
||||
res.status(404)
|
||||
return html
|
||||
}
|
||||
|
||||
let customHtml = ClientHtml.addTitleTag(html, escapeHTML(videoPlaylist.name))
|
||||
customHtml = ClientHtml.addDescriptionTag(customHtml, escapeHTML(videoPlaylist.description))
|
||||
|
||||
const url = videoPlaylist.getWatchUrl()
|
||||
const title = escapeHTML(videoPlaylist.name)
|
||||
const description = escapeHTML(videoPlaylist.description)
|
||||
|
||||
const image = {
|
||||
url: videoPlaylist.getThumbnailUrl()
|
||||
}
|
||||
|
||||
const list = {
|
||||
numberOfItems: videoPlaylist.get('videosLength')
|
||||
}
|
||||
|
||||
const ogType = 'video'
|
||||
const twitterCard = 'summary'
|
||||
const schemaType = 'ItemList'
|
||||
|
||||
customHtml = ClientHtml.addTags(customHtml, { url, title, description, image, list, ogType, twitterCard, schemaType })
|
||||
|
||||
return customHtml
|
||||
}
|
||||
|
@ -87,7 +157,22 @@ export class ClientHtml {
|
|||
|
||||
let customHtml = ClientHtml.addTitleTag(html, escapeHTML(entity.getDisplayName()))
|
||||
customHtml = ClientHtml.addDescriptionTag(customHtml, escapeHTML(entity.description))
|
||||
customHtml = ClientHtml.addAccountOrChannelMetaTags(customHtml, entity)
|
||||
|
||||
const url = entity.Actor.url
|
||||
const title = escapeHTML(entity.getDisplayName())
|
||||
const description = escapeHTML(entity.description)
|
||||
|
||||
const image = {
|
||||
url: entity.Actor.getAvatarUrl(),
|
||||
width: AVATARS_SIZE.width,
|
||||
height: AVATARS_SIZE.height
|
||||
}
|
||||
|
||||
const ogType = 'website'
|
||||
const twitterCard = 'summary'
|
||||
const schemaType = 'ProfilePage'
|
||||
|
||||
customHtml = ClientHtml.addTags(customHtml, { url, title, description, image, ogType, twitterCard, schemaType })
|
||||
|
||||
return customHtml
|
||||
}
|
||||
|
@ -183,60 +268,100 @@ export class ClientHtml {
|
|||
return htmlStringPage.replace('</head>', linkTag + '</head>')
|
||||
}
|
||||
|
||||
private static addVideoOpenGraphAndOEmbedTags (htmlStringPage: string, video: MVideo) {
|
||||
const previewUrl = WEBSERVER.URL + video.getPreviewStaticPath()
|
||||
const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
|
||||
|
||||
const videoNameEscaped = escapeHTML(video.name)
|
||||
const videoDescriptionEscaped = escapeHTML(video.description)
|
||||
const embedUrl = WEBSERVER.URL + video.getEmbedStaticPath()
|
||||
|
||||
const openGraphMetaTags = {
|
||||
'og:type': 'video',
|
||||
'og:title': videoNameEscaped,
|
||||
'og:image': previewUrl,
|
||||
'og:url': videoUrl,
|
||||
'og:description': videoDescriptionEscaped,
|
||||
|
||||
'og:video:url': embedUrl,
|
||||
'og:video:secure_url': embedUrl,
|
||||
'og:video:type': 'text/html',
|
||||
'og:video:width': EMBED_SIZE.width,
|
||||
'og:video:height': EMBED_SIZE.height,
|
||||
|
||||
'name': videoNameEscaped,
|
||||
'description': videoDescriptionEscaped,
|
||||
'image': previewUrl,
|
||||
|
||||
'twitter:card': CONFIG.SERVICES.TWITTER.WHITELISTED ? 'player' : 'summary_large_image',
|
||||
'twitter:site': CONFIG.SERVICES.TWITTER.USERNAME,
|
||||
'twitter:title': videoNameEscaped,
|
||||
'twitter:description': videoDescriptionEscaped,
|
||||
'twitter:image': previewUrl,
|
||||
'twitter:player': embedUrl,
|
||||
'twitter:player:width': EMBED_SIZE.width,
|
||||
'twitter:player:height': EMBED_SIZE.height
|
||||
private static generateOpenGraphMetaTags (tags) {
|
||||
const metaTags = {
|
||||
'og:type': tags.ogType,
|
||||
'og:title': tags.title,
|
||||
'og:image': tags.image.url
|
||||
}
|
||||
|
||||
const oembedLinkTags = [
|
||||
{
|
||||
type: 'application/json+oembed',
|
||||
href: WEBSERVER.URL + '/services/oembed?url=' + encodeURIComponent(videoUrl),
|
||||
title: videoNameEscaped
|
||||
}
|
||||
]
|
||||
if (tags.image.width && tags.image.height) {
|
||||
metaTags['og:image:width'] = tags.image.width
|
||||
metaTags['og:image:height'] = tags.image.height
|
||||
}
|
||||
|
||||
const schemaTags = {
|
||||
metaTags['og:url'] = tags.url
|
||||
metaTags['og:description'] = tags.description
|
||||
|
||||
if (tags.embed) {
|
||||
metaTags['og:video:url'] = tags.embed.url
|
||||
metaTags['og:video:secure_url'] = tags.embed.url
|
||||
metaTags['og:video:type'] = 'text/html'
|
||||
metaTags['og:video:width'] = EMBED_SIZE.width
|
||||
metaTags['og:video:height'] = EMBED_SIZE.height
|
||||
}
|
||||
|
||||
return metaTags
|
||||
}
|
||||
|
||||
private static generateStandardMetaTags (tags) {
|
||||
return {
|
||||
name: tags.title,
|
||||
description: tags.description,
|
||||
image: tags.image.url
|
||||
}
|
||||
}
|
||||
|
||||
private static generateTwitterCardMetaTags (tags) {
|
||||
const metaTags = {
|
||||
'twitter:card': tags.twitterCard,
|
||||
'twitter:site': CONFIG.SERVICES.TWITTER.USERNAME,
|
||||
'twitter:title': tags.title,
|
||||
'twitter:description': tags.description,
|
||||
'twitter:image': tags.image.url
|
||||
}
|
||||
|
||||
if (tags.image.width && tags.image.height) {
|
||||
metaTags['twitter:image:width'] = tags.image.width
|
||||
metaTags['twitter:image:height'] = tags.image.height
|
||||
}
|
||||
|
||||
return metaTags
|
||||
}
|
||||
|
||||
private static generateSchemaTags (tags) {
|
||||
const schema = {
|
||||
'@context': 'http://schema.org',
|
||||
'@type': 'VideoObject',
|
||||
'name': videoNameEscaped,
|
||||
'description': videoDescriptionEscaped,
|
||||
'thumbnailUrl': previewUrl,
|
||||
'uploadDate': video.createdAt.toISOString(),
|
||||
'duration': getActivityStreamDuration(video.duration),
|
||||
'contentUrl': videoUrl,
|
||||
'embedUrl': embedUrl,
|
||||
'interactionCount': video.views
|
||||
'@type': tags.schemaType,
|
||||
'name': tags.title,
|
||||
'description': tags.description,
|
||||
'image': tags.image.url,
|
||||
'url': tags.url
|
||||
}
|
||||
|
||||
if (tags.list) {
|
||||
schema['numberOfItems'] = tags.list.numberOfItems
|
||||
schema['thumbnailUrl'] = tags.image.url
|
||||
}
|
||||
|
||||
if (tags.embed) {
|
||||
schema['embedUrl'] = tags.embed.url
|
||||
schema['uploadDate'] = tags.embed.createdAt
|
||||
schema['duration'] = tags.embed.duration
|
||||
schema['iterationCount'] = tags.embed.views
|
||||
schema['thumbnailUrl'] = tags.image.url
|
||||
schema['contentUrl'] = tags.url
|
||||
}
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
private static addTags (htmlStringPage: string, tagsValues: any) {
|
||||
const openGraphMetaTags = this.generateOpenGraphMetaTags(tagsValues)
|
||||
const standardMetaTags = this.generateStandardMetaTags(tagsValues)
|
||||
const twitterCardMetaTags = this.generateTwitterCardMetaTags(tagsValues)
|
||||
const schemaTags = this.generateSchemaTags(tagsValues)
|
||||
|
||||
const { url, title, embed } = tagsValues
|
||||
|
||||
const oembedLinkTags = []
|
||||
|
||||
if (embed) {
|
||||
oembedLinkTags.push({
|
||||
type: 'application/json+oembed',
|
||||
href: WEBSERVER.URL + '/services/oembed?url=' + encodeURIComponent(url),
|
||||
title
|
||||
})
|
||||
}
|
||||
|
||||
let tagsString = ''
|
||||
|
@ -248,28 +373,33 @@ export class ClientHtml {
|
|||
tagsString += `<meta property="${tagName}" content="${tagValue}" />`
|
||||
})
|
||||
|
||||
// Standard
|
||||
Object.keys(standardMetaTags).forEach(tagName => {
|
||||
const tagValue = standardMetaTags[tagName]
|
||||
|
||||
tagsString += `<meta property="${tagName}" content="${tagValue}" />`
|
||||
})
|
||||
|
||||
// Twitter card
|
||||
Object.keys(twitterCardMetaTags).forEach(tagName => {
|
||||
const tagValue = twitterCardMetaTags[tagName]
|
||||
|
||||
tagsString += `<meta property="${tagName}" content="${tagValue}" />`
|
||||
})
|
||||
|
||||
// OEmbed
|
||||
for (const oembedLinkTag of oembedLinkTags) {
|
||||
tagsString += `<link rel="alternate" type="${oembedLinkTag.type}" href="${oembedLinkTag.href}" title="${oembedLinkTag.title}" />`
|
||||
}
|
||||
|
||||
// Schema.org
|
||||
tagsString += `<script type="application/ld+json">${JSON.stringify(schemaTags)}</script>`
|
||||
if (schemaTags) {
|
||||
tagsString += `<script type="application/ld+json">${JSON.stringify(schemaTags)}</script>`
|
||||
}
|
||||
|
||||
// SEO, use origin video url so Google does not index remote videos
|
||||
tagsString += `<link rel="canonical" href="${video.url}" />`
|
||||
// SEO, use origin URL
|
||||
tagsString += `<link rel="canonical" href="${url}" />`
|
||||
|
||||
return this.addOpenGraphAndOEmbedTags(htmlStringPage, tagsString)
|
||||
}
|
||||
|
||||
private static addAccountOrChannelMetaTags (htmlStringPage: string, entity: MAccountActor | MChannelActor) {
|
||||
// SEO, use origin account or channel URL
|
||||
const metaTags = `<link rel="canonical" href="${entity.Actor.url}" />`
|
||||
|
||||
return this.addOpenGraphAndOEmbedTags(htmlStringPage, metaTags)
|
||||
}
|
||||
|
||||
private static addOpenGraphAndOEmbedTags (htmlStringPage: string, metaTags: string) {
|
||||
return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.META_TAGS, metaTags)
|
||||
return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.META_TAGS, tagsString)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -490,6 +490,10 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
|
|||
return join(STATIC_PATHS.THUMBNAILS, this.Thumbnail.filename)
|
||||
}
|
||||
|
||||
getWatchUrl () {
|
||||
return WEBSERVER.URL + '/videos/watch/playlist/' + this.uuid
|
||||
}
|
||||
|
||||
setAsRefreshed () {
|
||||
this.changed('updatedAt', true)
|
||||
|
||||
|
|
|
@ -13,8 +13,14 @@ import {
|
|||
serverLogin,
|
||||
updateCustomConfig,
|
||||
updateCustomSubConfig,
|
||||
uploadVideo
|
||||
uploadVideo,
|
||||
createVideoPlaylist,
|
||||
addVideoInPlaylist,
|
||||
getAccount,
|
||||
addVideoChannel
|
||||
} from '../../shared/extra-utils'
|
||||
import { VideoPlaylistPrivacy } from '@shared/models'
|
||||
import { MVideoPlaylist, MAccount, MChannel } from '@server/types/models'
|
||||
|
||||
const expect = chai.expect
|
||||
|
||||
|
@ -26,6 +32,11 @@ function checkIndexTags (html: string, title: string, description: string, css:
|
|||
|
||||
describe('Test a client controllers', function () {
|
||||
let server: ServerInfo
|
||||
let videoPlaylist: MVideoPlaylist
|
||||
let account: MAccount
|
||||
let videoChannel: MChannel
|
||||
const name = 'my super name for server 1'
|
||||
const description = 'my super description for server 1'
|
||||
|
||||
before(async function () {
|
||||
this.timeout(120000)
|
||||
|
@ -33,18 +44,56 @@ describe('Test a client controllers', function () {
|
|||
server = await flushAndRunServer(1)
|
||||
server.accessToken = await serverLogin(server)
|
||||
|
||||
const videoAttributes = {
|
||||
name: 'my super name for server 1',
|
||||
description: 'my super description for server 1'
|
||||
}
|
||||
// Video
|
||||
|
||||
const videoAttributes = { name, description }
|
||||
|
||||
await uploadVideo(server.url, server.accessToken, videoAttributes)
|
||||
|
||||
const res = await getVideosList(server.url)
|
||||
const videos = res.body.data
|
||||
const resVideosRequest = await getVideosList(server.url)
|
||||
|
||||
const videos = resVideosRequest.body.data
|
||||
|
||||
expect(videos.length).to.equal(1)
|
||||
|
||||
server.video = videos[0]
|
||||
|
||||
// Playlist
|
||||
|
||||
const playlistAttrs = {
|
||||
displayName: name,
|
||||
description,
|
||||
privacy: VideoPlaylistPrivacy.PUBLIC
|
||||
}
|
||||
|
||||
const resVideoPlaylistRequest = await createVideoPlaylist({ url: server.url, token: server.accessToken, playlistAttrs })
|
||||
|
||||
videoPlaylist = resVideoPlaylistRequest.body.videoPlaylist
|
||||
|
||||
await addVideoInPlaylist({
|
||||
url: server.url,
|
||||
token: server.accessToken,
|
||||
playlistId: videoPlaylist.id,
|
||||
elementAttrs: { videoId: server.video.id }
|
||||
})
|
||||
|
||||
// Account
|
||||
|
||||
const resAccountRequest = await getAccount(server.url, `${server.user.username}@${server.host}:${server.port}`)
|
||||
|
||||
account = resAccountRequest.body.account
|
||||
|
||||
// Channel
|
||||
|
||||
const videoChannelAttributesArg = {
|
||||
name: `${server.user.username}_channel`,
|
||||
displayName: name,
|
||||
description
|
||||
}
|
||||
|
||||
const resChannelRequest = await addVideoChannel(server.url, server.accessToken, videoChannelAttributesArg)
|
||||
|
||||
videoChannel = resChannelRequest.body.videoChannel
|
||||
})
|
||||
|
||||
it('Should have valid Open Graph tags on the watch page with video id', async function () {
|
||||
|
@ -53,8 +102,10 @@ describe('Test a client controllers', function () {
|
|||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
|
||||
expect(res.text).to.contain('<meta property="og:title" content="my super name for server 1" />')
|
||||
expect(res.text).to.contain('<meta property="og:description" content="my super description for server 1" />')
|
||||
expect(res.text).to.contain(`<meta property="og:title" content="${name}" />`)
|
||||
expect(res.text).to.contain(`<meta property="og:description" content="${description}" />`)
|
||||
expect(res.text).to.contain('<meta property="og:type" content="video" />')
|
||||
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/videos/watch/${server.video.uuid}" />`)
|
||||
})
|
||||
|
||||
it('Should have valid Open Graph tags on the watch page with video uuid', async function () {
|
||||
|
@ -63,8 +114,46 @@ describe('Test a client controllers', function () {
|
|||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
|
||||
expect(res.text).to.contain('<meta property="og:title" content="my super name for server 1" />')
|
||||
expect(res.text).to.contain('<meta property="og:description" content="my super description for server 1" />')
|
||||
expect(res.text).to.contain(`<meta property="og:title" content="${name}" />`)
|
||||
expect(res.text).to.contain(`<meta property="og:description" content="${description}" />`)
|
||||
expect(res.text).to.contain('<meta property="og:type" content="video" />')
|
||||
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/videos/watch/${server.video.uuid}" />`)
|
||||
})
|
||||
|
||||
it('Should have valid Open Graph tags on the watch playlist page', async function () {
|
||||
const res = await request(server.url)
|
||||
.get('/videos/watch/playlist/' + videoPlaylist.uuid)
|
||||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
|
||||
expect(res.text).to.contain(`<meta property="og:title" content="${videoPlaylist.name}" />`)
|
||||
expect(res.text).to.contain(`<meta property="og:description" content="${videoPlaylist.description}" />`)
|
||||
expect(res.text).to.contain('<meta property="og:type" content="video" />')
|
||||
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/videos/watch/playlist/${videoPlaylist.uuid}" />`)
|
||||
})
|
||||
|
||||
it('Should have valid Open Graph tags on the account page', async function () {
|
||||
const res = await request(server.url)
|
||||
.get('/accounts/' + server.user.username)
|
||||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
|
||||
expect(res.text).to.contain(`<meta property="og:title" content="${account.getDisplayName()}" />`)
|
||||
expect(res.text).to.contain(`<meta property="og:description" content="${account.description}" />`)
|
||||
expect(res.text).to.contain('<meta property="og:type" content="website" />')
|
||||
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/accounts/${server.user.username}" />`)
|
||||
})
|
||||
|
||||
it('Should have valid Open Graph tags on the channel page', async function () {
|
||||
const res = await request(server.url)
|
||||
.get('/video-channels/' + videoChannel.name)
|
||||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
|
||||
expect(res.text).to.contain(`<meta property="og:title" content="${videoChannel.getDisplayName()}" />`)
|
||||
expect(res.text).to.contain(`<meta property="og:description" content="${videoChannel.description}" />`)
|
||||
expect(res.text).to.contain('<meta property="og:type" content="website" />')
|
||||
expect(res.text).to.contain(`<meta property="og:url" content="${server.url}/video-channels/${videoChannel.name}" />`)
|
||||
})
|
||||
|
||||
it('Should have valid oEmbed discovery tags', async function () {
|
||||
|
@ -81,7 +170,7 @@ describe('Test a client controllers', function () {
|
|||
expect(res.text).to.contain(expectedLink)
|
||||
})
|
||||
|
||||
it('Should have valid twitter card', async function () {
|
||||
it('Should have valid twitter card on the whatch video page', async function () {
|
||||
const res = await request(server.url)
|
||||
.get('/videos/watch/' + server.video.uuid)
|
||||
.set('Accept', 'text/html')
|
||||
|
@ -89,6 +178,44 @@ describe('Test a client controllers', function () {
|
|||
|
||||
expect(res.text).to.contain('<meta property="twitter:card" content="summary_large_image" />')
|
||||
expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
|
||||
expect(res.text).to.contain(`<meta property="twitter:title" content="${name}" />`)
|
||||
expect(res.text).to.contain(`<meta property="twitter:description" content="${description}" />`)
|
||||
})
|
||||
|
||||
it('Should have valid twitter card on the watch playlist page', async function () {
|
||||
const res = await request(server.url)
|
||||
.get('/videos/watch/playlist/' + videoPlaylist.uuid)
|
||||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
|
||||
expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
|
||||
expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
|
||||
expect(res.text).to.contain(`<meta property="twitter:title" content="${videoPlaylist.name}" />`)
|
||||
expect(res.text).to.contain(`<meta property="twitter:description" content="${videoPlaylist.description}" />`)
|
||||
})
|
||||
|
||||
it('Should have valid twitter card on the account page', async function () {
|
||||
const res = await request(server.url)
|
||||
.get('/accounts/' + account.name)
|
||||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
|
||||
expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
|
||||
expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
|
||||
expect(res.text).to.contain(`<meta property="twitter:title" content="${account.name}" />`)
|
||||
expect(res.text).to.contain(`<meta property="twitter:description" content="${account.description}" />`)
|
||||
})
|
||||
|
||||
it('Should have valid twitter card on the channel page', async function () {
|
||||
const res = await request(server.url)
|
||||
.get('/video-channels/' + videoChannel.name)
|
||||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
|
||||
expect(res.text).to.contain('<meta property="twitter:card" content="summary" />')
|
||||
expect(res.text).to.contain('<meta property="twitter:site" content="@Chocobozzz" />')
|
||||
expect(res.text).to.contain(`<meta property="twitter:title" content="${videoChannel.name}" />`)
|
||||
expect(res.text).to.contain(`<meta property="twitter:description" content="${videoChannel.description}" />`)
|
||||
})
|
||||
|
||||
it('Should have valid twitter card if Twitter is whitelisted', async function () {
|
||||
|
@ -100,13 +227,37 @@ describe('Test a client controllers', function () {
|
|||
}
|
||||
await updateCustomConfig(server.url, server.accessToken, config)
|
||||
|
||||
const res = await request(server.url)
|
||||
const resVideoRequest = await request(server.url)
|
||||
.get('/videos/watch/' + server.video.uuid)
|
||||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
|
||||
expect(res.text).to.contain('<meta property="twitter:card" content="player" />')
|
||||
expect(res.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
|
||||
expect(resVideoRequest.text).to.contain('<meta property="twitter:card" content="player" />')
|
||||
expect(resVideoRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
|
||||
|
||||
const resVideoPlaylistRequest = await request(server.url)
|
||||
.get('/videos/watch/playlist/' + videoPlaylist.uuid)
|
||||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
|
||||
expect(resVideoPlaylistRequest.text).to.contain('<meta property="twitter:card" content="player" />')
|
||||
expect(resVideoPlaylistRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
|
||||
|
||||
const resAccountRequest = await request(server.url)
|
||||
.get('/accounts/' + account.name)
|
||||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
|
||||
expect(resAccountRequest.text).to.contain('<meta property="twitter:card" content="player" />')
|
||||
expect(resAccountRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
|
||||
|
||||
const resChannelRequest = await request(server.url)
|
||||
.get('/video-channels/' + videoChannel.name)
|
||||
.set('Accept', 'text/html')
|
||||
.expect(200)
|
||||
|
||||
expect(resChannelRequest.text).to.contain('<meta property="twitter:card" content="player" />')
|
||||
expect(resChannelRequest.text).to.contain('<meta property="twitter:site" content="@Kuja" />')
|
||||
})
|
||||
|
||||
it('Should have valid index html tags (title, description...)', async function () {
|
||||
|
|
Loading…
Reference in New Issue