Support relative URLs in OpenGraph properties (#52)
This commit is contained in:
parent
8b190a6c23
commit
cba1bbfa28
@ -388,6 +388,12 @@ type WebLog = {
|
||||
member this.AbsoluteUrl(permalink: Permalink) =
|
||||
$"{this.UrlBase}/{permalink}"
|
||||
|
||||
/// <summary>Convert a string URL to an absolute URL for this web log if required</summary>
|
||||
/// <param name="url">The URL which may be translated to an absolute one</param>
|
||||
/// <returns>The given URL if it was already absolute, or a corresponding absolute URL if not</returns>
|
||||
member this.UrlToAbsolute(url: string) =
|
||||
if url.StartsWith "http" then url else this.AbsoluteUrl(Permalink url)
|
||||
|
||||
/// <summary>Generate a relative URL for the given link</summary>
|
||||
/// <param name="permalink">The permalink for which a relative URL should be generated</param>
|
||||
/// <returns>A relative URL for the given link</returns>
|
||||
|
@ -418,9 +418,12 @@ type OpenGraphAudio = {
|
||||
|> dict
|
||||
|
||||
/// <summary>The <c>meta</c> properties for this image</summary>
|
||||
member this.Properties = seq {
|
||||
yield "og:audio", this.Url
|
||||
if this.Url.StartsWith "https:" then yield "og:audio:secure_url", this.Url
|
||||
/// <param name="urlTransform">A function to convert relative URLs to absolute URLs</param>
|
||||
/// <returns>A sequence of key/value pairs for this OpenGraph audio file</returns>
|
||||
member this.ToProperties(urlTransform: string -> string) = seq {
|
||||
let url = urlTransform this.Url
|
||||
yield "og:audio", url
|
||||
if url.StartsWith "https:" then yield "og:audio:secure_url", url
|
||||
match this.Type with
|
||||
| Some typ -> yield "og:audio:type", typ
|
||||
| None ->
|
||||
@ -472,9 +475,12 @@ type OpenGraphImage = {
|
||||
|> dict
|
||||
|
||||
/// <summary>The <c>meta</c> properties for this image</summary>
|
||||
member this.Properties = seq {
|
||||
yield "og:image", this.Url
|
||||
if this.Url.StartsWith "https:" then yield "og:image:secure_url", this.Url
|
||||
/// <param name="urlTransform">A function to convert relative URLs to absolute URLs</param>
|
||||
/// <returns>A sequence of key/value pairs for this OpenGraph image file</returns>
|
||||
member this.ToProperties(urlTransform: string -> string) = seq {
|
||||
let url = urlTransform this.Url
|
||||
yield "og:image", url
|
||||
if url.StartsWith "https:" then yield "og:image:secure_url", url
|
||||
match this.Type with
|
||||
| Some typ -> yield "og:image:type", typ
|
||||
| None ->
|
||||
@ -520,9 +526,12 @@ type OpenGraphVideo = {
|
||||
|> dict
|
||||
|
||||
/// <summary>The <c>meta</c> properties for this video</summary>
|
||||
member this.Properties = seq {
|
||||
yield "og:video", this.Url
|
||||
if this.Url.StartsWith "https:" then yield "og:video:secure_url", this.Url
|
||||
/// <param name="urlTransform">A function to convert relative URLs to absolute URLs</param>
|
||||
/// <returns>A sequence of key/value pairs for this OpenGraph video file</returns>
|
||||
member this.ToProperties(urlTransform: string -> string) = seq {
|
||||
let url = urlTransform this.Url
|
||||
yield "og:video", url
|
||||
if url.StartsWith "https:" then yield "og:video:secure_url", url
|
||||
match this.Type with
|
||||
| Some typ -> yield "og:video:type", typ
|
||||
| None ->
|
||||
@ -633,17 +642,19 @@ type OpenGraphProperties = {
|
||||
Other = None }
|
||||
|
||||
/// <summary>The <c>meta</c> properties for this page or post</summary>
|
||||
member this.Properties = seq {
|
||||
/// <param name="urlTransform">A function to convert relative URLs to absolute URLs</param>
|
||||
/// <returns>A sequence of key/value pairs for this set of OpenGraph properties</returns>
|
||||
member this.ToProperties urlTransform = seq {
|
||||
yield "og:type", string this.Type
|
||||
yield! this.Image.Properties
|
||||
yield! this.Image.ToProperties urlTransform
|
||||
match this.Description with Some desc -> yield "og:description", desc | None -> ()
|
||||
match this.Determiner with Some det -> yield "og:determiner", det | None -> ()
|
||||
match this.Locale with Some loc -> yield "og:locale", loc | None -> ()
|
||||
match this.LocaleAlternate with
|
||||
| Some alt -> yield! alt |> List.map (fun it -> "og:locale:alternate", it)
|
||||
| None -> ()
|
||||
match this.Audio with Some audio -> yield! audio.Properties | None -> ()
|
||||
match this.Video with Some video -> yield! video.Properties | None -> ()
|
||||
match this.Audio with Some audio -> yield! audio.ToProperties urlTransform | None -> ()
|
||||
match this.Video with Some video -> yield! video.ToProperties urlTransform | None -> ()
|
||||
match this.Other with Some oth -> yield! oth |> List.map (fun it -> it.Name, it.Value) | None -> ()
|
||||
}
|
||||
|
||||
|
@ -504,21 +504,18 @@ type EditCommonModel() =
|
||||
post.OpenGraph |> Option.iter this.PopulateOpenGraph
|
||||
|
||||
/// <summary>Convert the properties of the model into a set of OpenGraph properties</summary>
|
||||
/// <param name="webLog">The current web log</param>
|
||||
member this.ToOpenGraph(webLog: WebLog) =
|
||||
member this.ToOpenGraph() =
|
||||
if this.AssignOpenGraph then
|
||||
let toAbsolute (url: string) =
|
||||
if url.StartsWith "http" then url else webLog.AbsoluteUrl (Permalink url)
|
||||
let audio =
|
||||
match this.OpenGraphAudioUrl.Trim() with
|
||||
| "" -> None
|
||||
| url -> Some { OpenGraphAudio.Url = toAbsolute url; Type = noneIfBlank this.OpenGraphAudioType }
|
||||
| url -> Some { OpenGraphAudio.Url = url; Type = noneIfBlank this.OpenGraphAudioType }
|
||||
let video =
|
||||
match this.OpenGraphVideoUrl.Trim() with
|
||||
| "" -> None
|
||||
| url ->
|
||||
Some {
|
||||
OpenGraphVideo.Url = toAbsolute url
|
||||
OpenGraphVideo.Url = url
|
||||
Type = noneIfBlank this.OpenGraphVideoType
|
||||
Width = noneIfBlank this.OpenGraphVideoWidth |> Option.map int
|
||||
Height = noneIfBlank this.OpenGraphVideoHeight |> Option.map int
|
||||
@ -526,7 +523,7 @@ type EditCommonModel() =
|
||||
Some {
|
||||
Type = if this.OpenGraphType = "" then Article else OpenGraphType.Parse this.OpenGraphType
|
||||
Image = {
|
||||
Url = toAbsolute this.OpenGraphImageUrl
|
||||
Url = this.OpenGraphImageUrl
|
||||
Type = noneIfBlank this.OpenGraphImageType
|
||||
Width = noneIfBlank this.OpenGraphImageWidth |> Option.map int
|
||||
Height = noneIfBlank this.OpenGraphImageHeight |> Option.map int
|
||||
@ -749,10 +746,9 @@ type EditPageModel() =
|
||||
|
||||
/// <summary>Update a page with values from this model</summary>
|
||||
/// <param name="page">The page to be updated</param>
|
||||
/// <param name="webLog">The web log to which this page belongs</param>
|
||||
/// <param name="now">The <c>Instant</c> to use for this particular update</param>
|
||||
/// <returns>The page, updated with the values from this model</returns>
|
||||
member this.UpdatePage (page: Page) webLog now =
|
||||
member this.UpdatePage (page: Page) now =
|
||||
let revision = { AsOf = now; Text = MarkupText.Parse $"{this.Source}: {this.Text}" }
|
||||
// Detect a permalink change, and add the prior one to the prior list
|
||||
match string page.Permalink with
|
||||
@ -768,7 +764,7 @@ type EditPageModel() =
|
||||
IsInPageList = this.IsShownInPageList
|
||||
Template = match this.Template with "" -> None | tmpl -> Some tmpl
|
||||
Text = revision.Text.AsHtml()
|
||||
OpenGraph = this.ToOpenGraph webLog
|
||||
OpenGraph = this.ToOpenGraph()
|
||||
Metadata = Seq.zip this.MetaNames this.MetaValues
|
||||
|> Seq.filter (fun it -> fst it > "")
|
||||
|> Seq.map (fun it -> { Name = fst it; Value = snd it })
|
||||
@ -908,10 +904,9 @@ type EditPostModel() =
|
||||
|
||||
/// <summary>Update a post with values from the submitted form</summary>
|
||||
/// <param name="post">The post which should be updated</param>
|
||||
/// <param name="webLog">The web log to which this post belongs</param>
|
||||
/// <param name="now">The <c>Instant</c> to use for this particular update</param>
|
||||
/// <returns>The post, updated with the values from this model</returns>
|
||||
member this.UpdatePost (post: Post) (webLog: WebLog) now =
|
||||
member this.UpdatePost (post: Post) now =
|
||||
let revision = { AsOf = now; Text = MarkupText.Parse $"{this.Source}: {this.Text}" }
|
||||
// Detect a permalink change, and add the prior one to the prior list
|
||||
match string post.Permalink with
|
||||
@ -935,7 +930,7 @@ type EditPostModel() =
|
||||
Template = match this.Template.Trim() with "" -> None | tmpl -> Some tmpl
|
||||
CategoryIds = this.CategoryIds |> Array.map CategoryId |> List.ofArray
|
||||
Status = if this.DoPublish then Published else post.Status
|
||||
OpenGraph = this.ToOpenGraph webLog
|
||||
OpenGraph = this.ToOpenGraph()
|
||||
Metadata = Seq.zip this.MetaNames this.MetaValues
|
||||
|> Seq.filter (fun it -> fst it > "")
|
||||
|> Seq.map (fun it -> { Name = fst it; Value = snd it })
|
||||
|
@ -34,6 +34,18 @@ let webLogTests = testList "WebLog" [
|
||||
"https://my.site/blog/page.html"
|
||||
"Absolute URL is incorrect"
|
||||
}
|
||||
testList "UrlToAbsolute" [
|
||||
test "succeeds for relative URL" {
|
||||
Expect.equal
|
||||
({ WebLog.Empty with UrlBase = "https://my.site" }.UrlToAbsolute "blog/page.html")
|
||||
"https://my.site/blog/page.html"
|
||||
"Absolute URL is incorrect"
|
||||
}
|
||||
test "succeeds for absolute URL" {
|
||||
Expect.equal
|
||||
(WebLog.Empty.UrlToAbsolute "https://test.units") "https://test.units" "Absolute URL is incorrect"
|
||||
}
|
||||
]
|
||||
testList "RelativeUrl" [
|
||||
test "succeeds for domain root URL" {
|
||||
Expect.equal
|
||||
|
@ -259,67 +259,79 @@ let markupTextTests = testList "MarkupText" [
|
||||
|
||||
/// Unit tests for the OpenGraphAudio type
|
||||
let openGraphAudioTests = testList "OpenGraphAudio" [
|
||||
testList "Properties" [
|
||||
let webLog = { WebLog.Empty with UrlBase = "https://unit.test/taco" }
|
||||
let transform = webLog.UrlToAbsolute
|
||||
testList "ToProperties" [
|
||||
test "succeeds with minimum required" {
|
||||
let props = Array.ofSeq { OpenGraphAudio.Empty with Url = "http://test.this" }.Properties
|
||||
let props = Array.ofSeq ({ OpenGraphAudio.Empty with Url = "http://test.this" }.ToProperties transform)
|
||||
Expect.hasLength props 1 "There should be one property"
|
||||
Expect.equal props[0] ("og:audio", "http://test.this") "The URL was not written correctly"
|
||||
}
|
||||
test "succeeds with secure URL" {
|
||||
let props = Array.ofSeq { OpenGraphAudio.Empty with Url = "https://test.this" }.Properties
|
||||
let props = Array.ofSeq ({ OpenGraphAudio.Empty with Url = "https://test.this" }.ToProperties transform)
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[0] ("og:audio", "https://test.this") "The URL was not written correctly"
|
||||
Expect.equal
|
||||
props[1] ("og:audio:secure_url", "https://test.this") "The Secure URL was not written correctly"
|
||||
}
|
||||
test "succeeds with all properties filled" {
|
||||
let props = Array.ofSeq { Url = "http://test.this"; Type = Some "audio/mpeg" }.Properties
|
||||
let props = Array.ofSeq ({ Url = "http://test.this"; Type = Some "audio/mpeg" }.ToProperties transform)
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[0] ("og:audio", "http://test.this") "The URL was not written correctly"
|
||||
Expect.equal props[1] ("og:audio:type", "audio/mpeg") "The MIME type was not written correctly"
|
||||
}
|
||||
test "succeeds when deriving AAC" {
|
||||
let props = Array.ofSeq { OpenGraphAudio.Empty with Url = "/this/cool.file.aac" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:audio:type", "audio/aac") "The MIME type for AAC was not derived correctly"
|
||||
test "succeeds when deriving AAC and transforming URL" {
|
||||
let props = Array.ofSeq ({ OpenGraphAudio.Empty with Url = "this/cool.file.aac" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal
|
||||
props[0]
|
||||
("og:audio", "https://unit.test/taco/this/cool.file.aac")
|
||||
"The URL was not transformed correctly"
|
||||
Expect.equal
|
||||
props[1]
|
||||
("og:audio:secure_url", "https://unit.test/taco/this/cool.file.aac")
|
||||
"The URL was not transformed correctly"
|
||||
Expect.equal props[2] ("og:audio:type", "audio/aac") "The MIME type for AAC was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving MP3" {
|
||||
let props = Array.ofSeq { OpenGraphAudio.Empty with Url = "/an.other/song.mp3" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:audio:type", "audio/mpeg") "The MIME type for MP3 was not derived correctly"
|
||||
let props = Array.ofSeq ({ OpenGraphAudio.Empty with Url = "an.other/song.mp3" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:audio:type", "audio/mpeg") "The MIME type for MP3 was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving OGA" {
|
||||
let props = Array.ofSeq { OpenGraphAudio.Empty with Url = "/talks/speex.oga" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:audio:type", "audio/ogg") "The MIME type for OGA was not derived correctly"
|
||||
let props = Array.ofSeq ({ OpenGraphAudio.Empty with Url = "talks/speex.oga" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:audio:type", "audio/ogg") "The MIME type for OGA was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving WAV" {
|
||||
let props = Array.ofSeq { OpenGraphAudio.Empty with Url = "/some/old.school.wav" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:audio:type", "audio/wav") "The MIME type for WAV was not derived correctly"
|
||||
let props = Array.ofSeq ({ OpenGraphAudio.Empty with Url = "some/old.school.wav" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:audio:type", "audio/wav") "The MIME type for WAV was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving WEBA" {
|
||||
let props = Array.ofSeq { OpenGraphAudio.Empty with Url = "/new/format/file.weba" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:audio:type", "audio/webm") "The MIME type for WEBA was not derived correctly"
|
||||
let props = Array.ofSeq ({ OpenGraphAudio.Empty with Url = "new/format/file.weba" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:audio:type", "audio/webm") "The MIME type for WEBA was not derived correctly"
|
||||
}
|
||||
test "succeeds when type cannot be derived" {
|
||||
let props = Array.ofSeq { OpenGraphAudio.Empty with Url = "/profile.jpg" }.Properties
|
||||
Expect.hasLength props 1 "There should be one property (only URL; no type derived)"
|
||||
let props = Array.ofSeq ({ OpenGraphAudio.Empty with Url = "profile.jpg" }.ToProperties transform)
|
||||
Expect.hasLength props 2 "There should be two properties (only URLs; no type derived)"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
/// Tests for the OpenGraphImage type
|
||||
let openGraphImageTests = testList "OpenGraphImage" [
|
||||
testList "Properties" [
|
||||
let webLog = { WebLog.Empty with UrlBase = "https://unit.test/taco" }
|
||||
let transform = webLog.UrlToAbsolute
|
||||
testList "ToProperties" [
|
||||
test "succeeds with minimum required" {
|
||||
let props = Array.ofSeq { OpenGraphImage.Empty with Url = "http://test.url" }.Properties
|
||||
let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "http://test.url" }.ToProperties transform)
|
||||
Expect.hasLength props 1 "There should be one property"
|
||||
Expect.equal props[0] ("og:image", "http://test.url") "The URL was not written correctly"
|
||||
}
|
||||
test "succeeds with secure URL" {
|
||||
let props = Array.ofSeq { OpenGraphImage.Empty with Url = "https://secure.url" }.Properties
|
||||
let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "https://secure.url" }.ToProperties transform)
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[0] ("og:image", "https://secure.url") "The URL was not written correctly"
|
||||
Expect.equal
|
||||
@ -331,7 +343,7 @@ let openGraphImageTests = testList "OpenGraphImage" [
|
||||
Type = Some "image/jpeg"
|
||||
Width = Some 400
|
||||
Height = Some 600
|
||||
Alt = Some "This ought to be good" }.Properties
|
||||
Alt = Some "This ought to be good" }.ToProperties transform
|
||||
|> Array.ofSeq
|
||||
Expect.hasLength props 5 "There should be five properties"
|
||||
Expect.equal props[0] ("og:image", "http://test.this") "The URL was not written correctly"
|
||||
@ -340,69 +352,77 @@ let openGraphImageTests = testList "OpenGraphImage" [
|
||||
Expect.equal props[3] ("og:image:height", "600") "The height was not written correctly"
|
||||
Expect.equal props[4] ("og:image:alt", "This ought to be good") "The alt text was not written correctly"
|
||||
}
|
||||
test "succeeds when deriving BMP" {
|
||||
let props = Array.ofSeq { OpenGraphImage.Empty with Url = "/old/windows.bmp" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:image:type", "image/bmp") "The MIME type for BMP was not derived correctly"
|
||||
test "succeeds when deriving BMP and transforming URL" {
|
||||
let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "old/windows.bmp" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal
|
||||
props[0] ("og:image", "https://unit.test/taco/old/windows.bmp") "The URL was not transformed correctly"
|
||||
Expect.equal
|
||||
props[1]
|
||||
("og:image:secure_url", "https://unit.test/taco/old/windows.bmp")
|
||||
"The URL was not transformed correctly"
|
||||
Expect.equal props[2] ("og:image:type", "image/bmp") "The MIME type for BMP was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving GIF" {
|
||||
let props = Array.ofSeq { OpenGraphImage.Empty with Url = "/its.a.soft.g.gif" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:image:type", "image/gif") "The MIME type for GIF was not derived correctly"
|
||||
let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "its.a.soft.g.gif" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:image:type", "image/gif") "The MIME type for GIF was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving ICO" {
|
||||
let props = Array.ofSeq { OpenGraphImage.Empty with Url = "/favicon.ico" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "favicon.ico" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal
|
||||
props[1] ("og:image:type", "image/vnd.microsoft.icon") "The MIME type for ICO was not derived correctly"
|
||||
props[2] ("og:image:type", "image/vnd.microsoft.icon") "The MIME type for ICO was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving JPEG" {
|
||||
let props = Array.ofSeq { OpenGraphImage.Empty with Url = "/big/name/photo.jpeg" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:image:type", "image/jpeg") "The MIME type for JPEG was not derived correctly"
|
||||
let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "big/name/photo.jpeg" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:image:type", "image/jpeg") "The MIME type for JPEG was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving PNG" {
|
||||
let props = Array.ofSeq { OpenGraphImage.Empty with Url = "/some/nice/graphic.png" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:image:type", "image/png") "The MIME type for PNG was not derived correctly"
|
||||
let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "some/nice/graphic.png" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:image:type", "image/png") "The MIME type for PNG was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving SVG" {
|
||||
let props = Array.ofSeq { OpenGraphImage.Empty with Url = "/fancy-new-vector.svg" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:image:type", "image/svg+xml") "The MIME type for SVG was not derived correctly"
|
||||
let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "fancy-new-vector.svg" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:image:type", "image/svg+xml") "The MIME type for SVG was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving TIF" {
|
||||
let props = Array.ofSeq { OpenGraphImage.Empty with Url = "/tagged/file.tif" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:image:type", "image/tiff") "The MIME type for TIF was not derived correctly"
|
||||
let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "tagged/file.tif" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:image:type", "image/tiff") "The MIME type for TIF was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving TIFF" {
|
||||
let props = Array.ofSeq { OpenGraphImage.Empty with Url = "/tagged/file.two.tiff" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:image:type", "image/tiff") "The MIME type for TIFF was not derived correctly"
|
||||
let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "tagged/file.two.tiff" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:image:type", "image/tiff") "The MIME type for TIFF was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving WEBP" {
|
||||
let props = Array.ofSeq { OpenGraphImage.Empty with Url = "/modern/photo.webp" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:image:type", "image/webp") "The MIME type for WEBP was not derived correctly"
|
||||
let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "modern/photo.webp" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:image:type", "image/webp") "The MIME type for WEBP was not derived correctly"
|
||||
}
|
||||
test "succeeds when type cannot be derived" {
|
||||
let props = Array.ofSeq { OpenGraphImage.Empty with Url = "/intro.mp3" }.Properties
|
||||
Expect.hasLength props 1 "There should be one property (only URL; no type derived)"
|
||||
let props = Array.ofSeq ({ OpenGraphImage.Empty with Url = "intro.mp3" }.ToProperties transform)
|
||||
Expect.hasLength props 2 "There should be two properties (only URLs; no type derived)"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
/// Unit tests for the OpenGraphVideo type
|
||||
let openGraphVideoTests = testList "OpenGraphVideo" [
|
||||
testList "Properties" [
|
||||
let webLog = { WebLog.Empty with UrlBase = "https://unit.test/taco" }
|
||||
let transform = webLog.UrlToAbsolute
|
||||
testList "ToProperties" [
|
||||
test "succeeds with minimum required" {
|
||||
let props = Array.ofSeq { OpenGraphVideo.Empty with Url = "http://url.test" }.Properties
|
||||
let props = Array.ofSeq ({ OpenGraphVideo.Empty with Url = "http://url.test" }.ToProperties transform)
|
||||
Expect.hasLength props 1 "There should be one property"
|
||||
Expect.equal props[0] ("og:video", "http://url.test") "The URL was not written correctly"
|
||||
}
|
||||
test "succeeds with secure URL" {
|
||||
let props = Array.ofSeq { OpenGraphVideo.Empty with Url = "https://url.secure" }.Properties
|
||||
let props = Array.ofSeq ({ OpenGraphVideo.Empty with Url = "https://url.secure" }.ToProperties transform)
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[0] ("og:video", "https://url.secure") "The URL was not written correctly"
|
||||
Expect.equal
|
||||
@ -413,7 +433,7 @@ let openGraphVideoTests = testList "OpenGraphVideo" [
|
||||
{ Url = "http://test.this"
|
||||
Type = Some "video/mpeg"
|
||||
Width = Some 1200
|
||||
Height = Some 900 }.Properties
|
||||
Height = Some 900 }.ToProperties transform
|
||||
|> Array.ofSeq
|
||||
Expect.hasLength props 4 "There should be five properties"
|
||||
Expect.equal props[0] ("og:video", "http://test.this") "The URL was not written correctly"
|
||||
@ -421,34 +441,41 @@ let openGraphVideoTests = testList "OpenGraphVideo" [
|
||||
Expect.equal props[2] ("og:video:width", "1200") "The width was not written correctly"
|
||||
Expect.equal props[3] ("og:video:height", "900") "The height was not written correctly"
|
||||
}
|
||||
test "succeeds when deriving AVI" {
|
||||
let props = Array.ofSeq { OpenGraphVideo.Empty with Url = "/my.video.avi" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:video:type", "video/x-msvideo") "The MIME type for AVI was not derived correctly"
|
||||
test "succeeds when deriving AVI and transforming URL" {
|
||||
let props = Array.ofSeq ({ OpenGraphVideo.Empty with Url = "my.video.avi" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal
|
||||
props[0] ("og:video", "https://unit.test/taco/my.video.avi") "The URL not transformed correctly"
|
||||
Expect.equal
|
||||
props[1]
|
||||
("og:video:secure_url", "https://unit.test/taco/my.video.avi")
|
||||
"The URL not transformed correctly"
|
||||
Expect.equal props[2] ("og:video:type", "video/x-msvideo") "The MIME type for AVI was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving MP4" {
|
||||
let props = Array.ofSeq { OpenGraphVideo.Empty with Url = "/chapters/1/01.mp4" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:video:type", "video/mp4") "The MIME type for MP4 was not derived correctly"
|
||||
let props = Array.ofSeq ({ OpenGraphVideo.Empty with Url = "chapters/1/01.mp4" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:video:type", "video/mp4") "The MIME type for MP4 was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving MPEG" {
|
||||
let props = Array.ofSeq { OpenGraphVideo.Empty with Url = "/viral/video.mpeg" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:video:type", "video/mpeg") "The MIME type for MPEG was not derived correctly"
|
||||
let props = Array.ofSeq ({ OpenGraphVideo.Empty with Url = "viral/video.mpeg" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:video:type", "video/mpeg") "The MIME type for MPEG was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving OGV" {
|
||||
let props = Array.ofSeq { OpenGraphVideo.Empty with Url = "/open/video/example.ogv" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:video:type", "video/ogg") "The MIME type for OGV was not derived correctly"
|
||||
let props =
|
||||
Array.ofSeq ({ OpenGraphVideo.Empty with Url = "open/video/example.ogv" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:video:type", "video/ogg") "The MIME type for OGV was not derived correctly"
|
||||
}
|
||||
test "succeeds when deriving WEBM" {
|
||||
let props = Array.ofSeq { OpenGraphVideo.Empty with Url = "/images/hero.webm" }.Properties
|
||||
Expect.hasLength props 2 "There should be two properties"
|
||||
Expect.equal props[1] ("og:video:type", "video/webm") "The MIME type for WEBM was not derived correctly"
|
||||
let props = Array.ofSeq ({ OpenGraphVideo.Empty with Url = "images/hero.webm" }.ToProperties transform)
|
||||
Expect.hasLength props 3 "There should be three properties"
|
||||
Expect.equal props[2] ("og:video:type", "video/webm") "The MIME type for WEBM was not derived correctly"
|
||||
}
|
||||
test "succeeds when type cannot be derived" {
|
||||
let props = Array.ofSeq { OpenGraphVideo.Empty with Url = "/favicon.ico" }.Properties
|
||||
Expect.hasLength props 1 "There should be one property (only URL; no type derived)"
|
||||
let props = Array.ofSeq ({ OpenGraphVideo.Empty with Url = "favicon.ico" }.ToProperties transform)
|
||||
Expect.hasLength props 2 "There should be two properties (only URLs; no type derived)"
|
||||
}
|
||||
]
|
||||
]
|
||||
@ -552,7 +579,8 @@ let openGraphPropertiesTests = testList "OpenGraphProperties" [
|
||||
test "succeeds with minimal values" {
|
||||
let props =
|
||||
{ OpenGraphProperties.Empty with
|
||||
Image = { OpenGraphImage.Empty with Url = "http://this.aint.nothing" } }.Properties
|
||||
Image = { OpenGraphImage.Empty with Url = "http://this.aint.nothing" } }
|
||||
.ToProperties WebLog.Empty.UrlToAbsolute
|
||||
|> Array.ofSeq
|
||||
Expect.hasLength props 2 "There should have been two properties"
|
||||
Expect.equal props[0] ("og:type", "article") "Type not written correctly"
|
||||
@ -568,7 +596,8 @@ let openGraphPropertiesTests = testList "OpenGraphProperties" [
|
||||
Locale = Some "en_US"
|
||||
LocaleAlternate = Some [ "en_UK"; "es_MX" ]
|
||||
Video = Some { OpenGraphVideo.Empty with Url = "http://this.video.file" }
|
||||
Other = Some [ { Name = "book.publisher"; Value = "Yep" } ] }.Properties
|
||||
Other = Some [ { Name = "book.publisher"; Value = "Yep" } ] }
|
||||
.ToProperties WebLog.Empty.UrlToAbsolute
|
||||
|> Array.ofSeq
|
||||
Expect.hasLength props 10 "There should have been ten properties"
|
||||
Expect.equal props[0] ("og:type", "book") "Type not written correctly"
|
||||
|
@ -438,15 +438,14 @@ let editCommonModelTests = testList "EditCommonModel" [
|
||||
]
|
||||
testList "ToOpenGraph" [
|
||||
test "succeeds when OpenGraph properties are not assigned" {
|
||||
Expect.isNone
|
||||
(EditCommonModel().ToOpenGraph WebLog.Empty) "No OpenGraph properties should have returned None"
|
||||
Expect.isNone (EditCommonModel().ToOpenGraph()) "No OpenGraph properties should have returned None"
|
||||
}
|
||||
test "succeeds when minimal OpenGraph properties are assigned" {
|
||||
let model = EditCommonModel()
|
||||
model.AssignOpenGraph <- true
|
||||
model.OpenGraphType <- string Article
|
||||
model.OpenGraphImageUrl <- "https://unit.test/img.tiff"
|
||||
let tryOg = model.ToOpenGraph WebLog.Empty
|
||||
let tryOg = model.ToOpenGraph()
|
||||
Expect.isSome tryOg "There should have been a set of OpenGraph properties returned"
|
||||
let og = tryOg.Value
|
||||
Expect.equal og.Type Article "OpenGraph type not filled correctly"
|
||||
@ -484,7 +483,7 @@ let editCommonModelTests = testList "EditCommonModel" [
|
||||
model.OpenGraphVideoHeight <- "768"
|
||||
model.OpenGraphExtraNames <- [| "og:duration"; "og:rating" |]
|
||||
model.OpenGraphExtraValues <- [| "1:30:27"; "G" |]
|
||||
let tryOg = model.ToOpenGraph WebLog.Empty
|
||||
let tryOg = model.ToOpenGraph()
|
||||
Expect.isSome tryOg "There should have been a set of OpenGraph properties returned"
|
||||
let og = tryOg.Value
|
||||
Expect.equal og.Type VideoMovie "OpenGraph type not filled correctly"
|
||||
@ -514,24 +513,6 @@ let editCommonModelTests = testList "EditCommonModel" [
|
||||
[ { Name = "og:duration"; Value = "1:30:27" }; { Name = "og:rating"; Value = "G" } ]
|
||||
"OpenGraph extra properties not filled properly"
|
||||
}
|
||||
test "succeeds when relative URLs are assigned" {
|
||||
let model = EditCommonModel()
|
||||
model.AssignOpenGraph <- true
|
||||
model.OpenGraphType <- string Article
|
||||
model.OpenGraphImageUrl <- "image.jpg"
|
||||
model.OpenGraphAudioUrl <- "tunes/sound.ogg"
|
||||
model.OpenGraphVideoUrl <- "teaser.mp4"
|
||||
let tryOg = model.ToOpenGraph { WebLog.Empty with UrlBase = "https://test.units/verify" }
|
||||
Expect.isSome tryOg "There should have been a set of OpenGraph properties returned"
|
||||
let og = tryOg.Value
|
||||
Expect.equal og.Image.Url "https://test.units/verify/image.jpg" "OpenGraph image URL not filled properly"
|
||||
Expect.isSome og.Audio "OpenGraph audio should have been filled"
|
||||
Expect.equal
|
||||
og.Audio.Value.Url "https://test.units/verify/tunes/sound.ogg" "OpenGraph audio URL not filled properly"
|
||||
Expect.isSome og.Video "OpenGraph video should have been filled"
|
||||
Expect.equal
|
||||
og.Video.Value.Url "https://test.units/verify/teaser.mp4" "OpenGraph video URL not filled properly"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@ -743,7 +724,7 @@ let editPageModelTests = testList "EditPageModel" [
|
||||
let model = EditPageModel.FromPage testFullPage
|
||||
model.Title <- "Updated Page"
|
||||
model.IsShownInPageList <- false
|
||||
let page = model.UpdatePage testFullPage WebLog.Empty (Noda.epoch + Duration.FromHours 4)
|
||||
let page = model.UpdatePage testFullPage (Noda.epoch + Duration.FromHours 4)
|
||||
Expect.equal page.Title "Updated Page" "Title not filled properly"
|
||||
Expect.equal page.Permalink (Permalink "blog/page.html") "Permalink not filled properly"
|
||||
Expect.isEmpty page.PriorPermalinks "PriorPermalinks should be empty"
|
||||
@ -778,7 +759,7 @@ let editPageModelTests = testList "EditPageModel" [
|
||||
model.MetaNames <- [| "banana"; "apple"; "grape" |]
|
||||
model.MetaValues <- [| "monkey"; "zebra"; "ape" |]
|
||||
let now = Noda.epoch + Duration.FromDays 7
|
||||
let page = model.UpdatePage testFullPage WebLog.Empty now
|
||||
let page = model.UpdatePage testFullPage now
|
||||
Expect.equal page.Title "My Updated Page" "Title not filled properly"
|
||||
Expect.equal page.Permalink (Permalink "blog/updated.html") "Permalink not filled properly"
|
||||
Expect.equal page.PriorPermalinks [ Permalink "blog/page.html" ] "PriorPermalinks not filled properly"
|
||||
@ -919,7 +900,7 @@ let editPostModelTests = testList "EditPostModel" [
|
||||
model
|
||||
testList "UpdatePost" [
|
||||
test "succeeds for a full podcast episode" {
|
||||
let post = (updatedModel ()).UpdatePost testFullPost WebLog.Empty (Noda.epoch + Duration.FromDays 400)
|
||||
let post = (updatedModel ()).UpdatePost testFullPost (Noda.epoch + Duration.FromDays 400)
|
||||
Expect.equal post.Title "An Updated Post" "Title not filled properly"
|
||||
Expect.equal post.Permalink (Permalink "1970/01/updated-post.html") "Permalink not filled properly"
|
||||
Expect.equal post.PriorPermalinks [ Permalink "1970/01/a-post.html" ] "PriorPermalinks not filled properly"
|
||||
@ -980,7 +961,7 @@ let editPostModelTests = testList "EditPostModel" [
|
||||
minModel.SeasonDescription <- ""
|
||||
minModel.EpisodeNumber <- ""
|
||||
minModel.EpisodeDescription <- ""
|
||||
let post = minModel.UpdatePost testFullPost WebLog.Empty (Noda.epoch + Duration.FromDays 500)
|
||||
let post = minModel.UpdatePost testFullPost (Noda.epoch + Duration.FromDays 500)
|
||||
Expect.isSome post.Episode "There should have been a podcast episode"
|
||||
let ep = post.Episode.Value
|
||||
Expect.equal ep.Media "an-updated-ep.mp3" "Media not filled properly"
|
||||
@ -1007,7 +988,7 @@ let editPostModelTests = testList "EditPostModel" [
|
||||
minModel.ChapterSource <- "internal"
|
||||
minModel.ChapterFile <- ""
|
||||
minModel.ChapterType <- ""
|
||||
let post = minModel.UpdatePost testFullPost WebLog.Empty (Noda.epoch + Duration.FromDays 500)
|
||||
let post = minModel.UpdatePost testFullPost (Noda.epoch + Duration.FromDays 500)
|
||||
Expect.isSome post.Episode "There should have been a podcast episode"
|
||||
let ep = post.Episode.Value
|
||||
Expect.equal ep.Chapters (Some []) "Chapters not filled properly"
|
||||
@ -1020,7 +1001,6 @@ let editPostModelTests = testList "EditPostModel" [
|
||||
let post =
|
||||
minModel.UpdatePost
|
||||
{ testFullPost with Episode = Some { testFullPost.Episode.Value with Chapters = Some [] } }
|
||||
WebLog.Empty
|
||||
(Noda.epoch + Duration.FromDays 500)
|
||||
Expect.isSome post.Episode "There should have been a podcast episode"
|
||||
let ep = post.Episode.Value
|
||||
@ -1033,15 +1013,14 @@ let editPostModelTests = testList "EditPostModel" [
|
||||
let model = updatedModel ()
|
||||
model.IsEpisode <- false
|
||||
model.Template <- ""
|
||||
let post = model.UpdatePost testFullPost WebLog.Empty Noda.epoch
|
||||
let post = model.UpdatePost testFullPost Noda.epoch
|
||||
Expect.isNone post.Template "Template not filled properly"
|
||||
Expect.isNone post.Episode "Episode not filled properly"
|
||||
}
|
||||
test "succeeds when publishing a draft" {
|
||||
let model = updatedModel ()
|
||||
model.DoPublish <- true
|
||||
let post =
|
||||
model.UpdatePost { testFullPost with Status = Draft } WebLog.Empty (Noda.epoch + Duration.FromDays 375)
|
||||
let post = model.UpdatePost { testFullPost with Status = Draft } (Noda.epoch + Duration.FromDays 375)
|
||||
Expect.equal post.Status Published "Status not set properly"
|
||||
Expect.equal post.PublishedOn (Some (Noda.epoch + Duration.FromDays 375)) "PublishedOn not set properly"
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ let save : HttpHandler = requireAccess Author >=> fun next ctx -> task {
|
||||
match! tryPage with
|
||||
| Some page when canEdit page.AuthorId ctx ->
|
||||
let updateList = page.IsInPageList <> model.IsShownInPageList
|
||||
let updatedPage = model.UpdatePage page ctx.WebLog now
|
||||
let updatedPage = model.UpdatePage page now
|
||||
do! (if model.IsNew then data.Page.Add else data.Page.Update) updatedPage
|
||||
if updateList then do! PageListCache.update ctx
|
||||
do! addMessage ctx { UserMessage.Success with Message = "Page saved successfully" }
|
||||
|
@ -495,7 +495,7 @@ let save : HttpHandler = requireAccess Author >=> fun next ctx -> task {
|
||||
| Some post when canEdit post.AuthorId ctx ->
|
||||
let priorCats = post.CategoryIds
|
||||
let updatedPost =
|
||||
model.UpdatePost post ctx.WebLog (Noda.now ())
|
||||
model.UpdatePost post (Noda.now ())
|
||||
|> function
|
||||
| post ->
|
||||
if model.SetPublished then
|
||||
|
@ -207,7 +207,7 @@ let parser =
|
||||
|> app.WebLog.AbsoluteUrl
|
||||
|> function url -> writeOgProp ("og:url", url)
|
||||
match if app.IsPage then app.Page.OpenGraph else app.Posts.Posts[0].OpenGraph with
|
||||
| Some props -> props.Properties |> Seq.iter writeOgProp
|
||||
| Some props -> props.ToProperties app.WebLog.UrlToAbsolute |> Seq.iter writeOgProp
|
||||
| None -> ()
|
||||
|
||||
writer.WriteLine $"""{s}<meta name=generator content="{app.Generator}">"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user