module Giraffe.Htmx.Tests open System open Giraffe.Htmx open Microsoft.AspNetCore.Http open NSubstitute open Xunit /// Tests for the IHeaderDictionary extension properties module IHeaderDictionaryExtensions = [] let ``HxBoosted succeeds when the header is not present`` () = let ctx = Substitute.For () ctx.Request.Headers.ReturnsForAnyArgs (HeaderDictionary ()) |> ignore Option.isNone ctx.Request.Headers.HxBoosted |> Assert.True [] let ``HxBoosted succeeds when the header is present and true`` () = let ctx = Substitute.For () let dic = HeaderDictionary () dic.Add ("HX-Boosted", "true") ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore Option.isSome ctx.Request.Headers.HxBoosted |> Assert.True Option.get ctx.Request.Headers.HxBoosted |> Assert.True [] let ``HxBoosted succeeds when the header is present and false`` () = let ctx = Substitute.For () let dic = HeaderDictionary () dic.Add ("HX-Boosted", "false") ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore Option.isSome ctx.Request.Headers.HxBoosted |> Assert.True Option.get ctx.Request.Headers.HxBoosted |> Assert.False [] let ``HxCurrentUrl succeeds when the header is not present`` () = let ctx = Substitute.For () ctx.Request.Headers.ReturnsForAnyArgs (HeaderDictionary ()) |> ignore Option.isNone ctx.Request.Headers.HxCurrentUrl |> Assert.True [] let ``HxCurrentUrl succeeds when the header is present`` () = let ctx = Substitute.For () let dic = HeaderDictionary () dic.Add ("HX-Current-URL", "http://localhost/test.htm") ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore Option.isSome ctx.Request.Headers.HxCurrentUrl |> Assert.True Assert.Equal (Uri "http://localhost/test.htm", Option.get ctx.Request.Headers.HxCurrentUrl) [] let ``HxHistoryRestoreRequest succeeds when the header is not present`` () = let ctx = Substitute.For () ctx.Request.Headers.ReturnsForAnyArgs (HeaderDictionary ()) |> ignore Option.isNone ctx.Request.Headers.HxHistoryRestoreRequest |> Assert.True [] let ``HxHistoryRestoreRequest succeeds when the header is present and true`` () = let ctx = Substitute.For () let dic = HeaderDictionary () dic.Add ("HX-History-Restore-Request", "true") ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore Option.isSome ctx.Request.Headers.HxHistoryRestoreRequest |> Assert.True Option.get ctx.Request.Headers.HxHistoryRestoreRequest |> Assert.True [] let ``HxHistoryRestoreRequest succeeds when the header is present and false`` () = let ctx = Substitute.For () let dic = HeaderDictionary () dic.Add ("HX-History-Restore-Request", "false") ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore Option.isSome ctx.Request.Headers.HxHistoryRestoreRequest |> Assert.True Option.get ctx.Request.Headers.HxHistoryRestoreRequest |> Assert.False [] let ``HxPrompt succeeds when the header is not present`` () = let ctx = Substitute.For () ctx.Request.Headers.ReturnsForAnyArgs (HeaderDictionary ()) |> ignore Option.isNone ctx.Request.Headers.HxPrompt |> Assert.True [] let ``HxPrompt succeeds when the header is present`` () = let ctx = Substitute.For () let dic = HeaderDictionary () dic.Add ("HX-Prompt", "of course") ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore Option.isSome ctx.Request.Headers.HxPrompt |> Assert.True Assert.Equal("of course", Option.get ctx.Request.Headers.HxPrompt) [] let ``HxRequest succeeds when the header is not present`` () = let ctx = Substitute.For () ctx.Request.Headers.ReturnsForAnyArgs (HeaderDictionary ()) |> ignore Option.isNone ctx.Request.Headers.HxRequest |> Assert.True [] let ``HxRequest succeeds when the header is present and true`` () = let ctx = Substitute.For () let dic = HeaderDictionary () dic.Add ("HX-Request", "true") ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore Option.isSome ctx.Request.Headers.HxRequest |> Assert.True Option.get ctx.Request.Headers.HxRequest |> Assert.True [] let ``HxRequest succeeds when the header is present and false`` () = let ctx = Substitute.For () let dic = HeaderDictionary () dic.Add ("HX-Request", "false") ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore Option.isSome ctx.Request.Headers.HxRequest |> Assert.True Option.get ctx.Request.Headers.HxRequest |> Assert.False [] let ``HxTarget succeeds when the header is not present`` () = let ctx = Substitute.For () ctx.Request.Headers.ReturnsForAnyArgs (HeaderDictionary ()) |> ignore Option.isNone ctx.Request.Headers.HxTarget |> Assert.True [] let ``HxTarget succeeds when the header is present`` () = let ctx = Substitute.For () let dic = HeaderDictionary () dic.Add ("HX-Target", "#leItem") ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore Option.isSome ctx.Request.Headers.HxTarget |> Assert.True Assert.Equal("#leItem", Option.get ctx.Request.Headers.HxTarget) [] let ``HxTrigger succeeds when the header is not present`` () = let ctx = Substitute.For () ctx.Request.Headers.ReturnsForAnyArgs (HeaderDictionary ()) |> ignore Option.isNone ctx.Request.Headers.HxTrigger |> Assert.True [] let ``HxTrigger succeeds when the header is present`` () = let ctx = Substitute.For () let dic = HeaderDictionary () dic.Add ("HX-Trigger", "#trig") ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore Option.isSome ctx.Request.Headers.HxTrigger |> Assert.True Assert.Equal("#trig", Option.get ctx.Request.Headers.HxTrigger) [] let ``HxTriggerName succeeds when the header is not present`` () = let ctx = Substitute.For () ctx.Request.Headers.ReturnsForAnyArgs (HeaderDictionary ()) |> ignore Option.isNone ctx.Request.Headers.HxTriggerName |> Assert.True [] let ``HxTriggerName succeeds when the header is present`` () = let ctx = Substitute.For () let dic = HeaderDictionary () dic.Add ("HX-Trigger-Name", "click") ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore Option.isSome ctx.Request.Headers.HxTriggerName |> Assert.True Assert.Equal("click", Option.get ctx.Request.Headers.HxTriggerName) /// Tests for the HttpRequest extension properties module HttpRequestExtensions = [] let ``IsHtmx succeeds when request is not from htmx`` () = let ctx = Substitute.For () ctx.Request.Headers.ReturnsForAnyArgs (HeaderDictionary ()) |> ignore Assert.False ctx.Request.IsHtmx [] let ``IsHtmx succeeds when request is from htmx`` () = let ctx = Substitute.For () let dic = HeaderDictionary () dic.Add ("HX-Request", "true") ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore Assert.True ctx.Request.IsHtmx [] let ``IsHtmxRefresh succeeds when request is not from htmx`` () = let ctx = Substitute.For () ctx.Request.Headers.ReturnsForAnyArgs (HeaderDictionary ()) |> ignore Assert.False ctx.Request.IsHtmxRefresh [] let ``IsHtmxRefresh succeeds when request is from htmx, but not a refresh`` () = let ctx = Substitute.For () let dic = HeaderDictionary () dic.Add ("HX-Request", "true") ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore Assert.False ctx.Request.IsHtmxRefresh [] let ``IsHtmxRefresh succeeds when request is from htmx and is a refresh`` () = let ctx = Substitute.For () let dic = HeaderDictionary () dic.Add ("HX-Request", "true") dic.Add ("HX-History-Restore-Request", "true") ctx.Request.Headers.ReturnsForAnyArgs dic |> ignore Assert.True ctx.Request.IsHtmxRefresh /// Tests for the HttpHandler functions provided in the Handlers module module HandlerTests = open System.Threading.Tasks /// Dummy "next" parameter to get the pipeline to execute/terminate let next (ctx : HttpContext) = Task.FromResult (Some ctx) [] let ``withHxPushUrl succeeds`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxPushUrl "/a-new-url" next ctx Assert.True (dic.ContainsKey "HX-Push-Url") Assert.Equal ("/a-new-url", dic["HX-Push-Url"][0]) } [] let ``withHxNoPushUrl succeeds`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxNoPushUrl next ctx Assert.True (dic.ContainsKey "HX-Push-Url") Assert.Equal ("false", dic["HX-Push-Url"][0]) } [] let ``withHxRedirect succeeds`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxRedirect "/somewhere-else" next ctx Assert.True (dic.ContainsKey "HX-Redirect") Assert.Equal ("/somewhere-else", dic["HX-Redirect"][0]) } [] let ``withHxRefresh succeeds when set to true`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxRefresh true next ctx Assert.True (dic.ContainsKey "HX-Refresh") Assert.Equal ("true", dic["HX-Refresh"][0]) } [] let ``withHxRefresh succeeds when set to false`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxRefresh false next ctx Assert.True (dic.ContainsKey "HX-Refresh") Assert.Equal ("false", dic["HX-Refresh"][0]) } [] let ``withHxReplaceUrl succeeds`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxReplaceUrl "/a-substitute-url" next ctx Assert.True (dic.ContainsKey "HX-Replace-Url") Assert.Equal ("/a-substitute-url", dic["HX-Replace-Url"][0]) } [] let ``withHxNoReplaceUrl succeeds`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxNoReplaceUrl next ctx Assert.True (dic.ContainsKey "HX-Replace-Url") Assert.Equal ("false", dic["HX-Replace-Url"][0]) } [] let ``withHxReswap succeeds`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxReswap HxSwap.BeforeEnd next ctx Assert.True (dic.ContainsKey "HX-Reswap") Assert.Equal (HxSwap.BeforeEnd, dic["HX-Reswap"][0]) } [] let ``withHxRetarget succeeds`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxRetarget "#somewhereElse" next ctx Assert.True (dic.ContainsKey "HX-Retarget") Assert.Equal ("#somewhereElse", dic["HX-Retarget"][0]) } [] let ``withHxTrigger succeeds`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxTrigger "doSomething" next ctx Assert.True (dic.ContainsKey "HX-Trigger") Assert.Equal ("doSomething", dic["HX-Trigger"][0]) } [] let ``withHxTriggerMany succeeds`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxTriggerMany [ "blah", "foo"; "bleh", "bar" ] next ctx Assert.True (dic.ContainsKey "HX-Trigger") Assert.Equal ("""{ "blah": "foo", "bleh": "bar" }""", dic["HX-Trigger"][0]) } [] let ``withHxTriggerAfterSettle succeeds`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxTriggerAfterSettle "byTheWay" next ctx Assert.True (dic.ContainsKey "HX-Trigger-After-Settle") Assert.Equal ("byTheWay", dic["HX-Trigger-After-Settle"][0]) } [] let ``withHxTriggerManyAfterSettle succeeds`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxTriggerManyAfterSettle [ "oof", "ouch"; "hmm", "uh" ] next ctx Assert.True (dic.ContainsKey "HX-Trigger-After-Settle") Assert.Equal ("""{ "oof": "ouch", "hmm": "uh" }""", dic["HX-Trigger-After-Settle"][0]) } [] let ``withHxTriggerAfterSwap succeeds`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxTriggerAfterSwap "justASec" next ctx Assert.True (dic.ContainsKey "HX-Trigger-After-Swap") Assert.Equal ("justASec", dic["HX-Trigger-After-Swap"][0]) } [] let ``withHxTriggerManyAfterSwap succeeds`` () = let ctx = Substitute.For () let dic = HeaderDictionary () ctx.Response.Headers.ReturnsForAnyArgs dic |> ignore task { let! _ = withHxTriggerManyAfterSwap [ "this", "1"; "that", "2" ] next ctx Assert.True (dic.ContainsKey "HX-Trigger-After-Swap") Assert.Equal ("""{ "this": "1", "that": "2" }""", dic["HX-Trigger-After-Swap"][0]) }