elm-pages-v3-beta/tests/StaticHttpRequestsTests.elm
2022-02-07 21:07:54 -08:00

1316 lines
55 KiB
Elm

module StaticHttpRequestsTests exposing (all)
import ApiRoute
import Bytes.Decode
import Bytes.Encode
import Codec
import DataSource exposing (DataSource)
import DataSource.Http
import Dict
import Expect
import Html
import Http
import Json.Decode as JD exposing (Decoder)
import Json.Encode as Encode
import Pages.Internal.NotFoundReason
import Pages.Internal.Platform.Cli exposing (..)
import Pages.Internal.Platform.Effect as Effect exposing (Effect)
import Pages.Internal.Platform.ToJsPayload as ToJsPayload
import Pages.Internal.StaticHttpBody as StaticHttpBody
import Pages.Manifest as Manifest
import Pages.ProgramConfig exposing (ProgramConfig)
import Pages.SiteConfig exposing (SiteConfig)
import Pages.StaticHttp.Request as Request
import Path
import ProgramTest exposing (ProgramTest)
import Regex
import RenderRequest
import Server.Response as Response
import SimulatedEffect.Cmd
import SimulatedEffect.Ports
import SimulatedEffect.Task
import Task
import Test exposing (Test, describe, test)
all : Test
all =
describe "Static Http Requests"
[ test "initial requests are sent out" <|
\() ->
startSimple []
(DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages" starDecoder)
|> simulateHttp
(get "https://api.github.com/repos/dillonkearns/elm-pages")
"""{ "stargazer_count": 86 }"""
|> expectSuccess
[ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
, """{"stargazer_count":86}"""
)
]
, test "StaticHttp request for initial are resolved" <|
\() ->
startSimple
[ "post-1" ]
(DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages" starDecoder)
|> simulateHttp
(get "https://api.github.com/repos/dillonkearns/elm-pages")
"""{ "stargazer_count": 86 }"""
|> expectSuccess
[ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
, """{"stargazer_count":86}"""
)
]
, describe "single page renders"
[ test "single pages that are pre-rendered" <|
\() ->
startSimple [ "post-1" ]
(DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages" starDecoder)
|> simulateHttp
(get "https://api.github.com/repos/dillonkearns/elm-pages")
"""{ "stargazer_count": 86 }"""
|> ProgramTest.expectOutgoingPortValues
"toJsPort"
(Codec.decoder (ToJsPayload.successCodecNew2 "" ""))
(\actualPorts ->
case actualPorts of
[ ToJsPayload.PageProgress portData ] ->
portData.is404
|> Expect.false "Expected page to be found and rendered"
_ ->
Expect.fail <| "Expected exactly 1 port of type PageProgress. Instead, got \n" ++ Debug.toString actualPorts
)
, test "data sources are not resolved 404 pages with matching route but not pre-rendered" <|
\() ->
startWithRoutes [ "post-2" ]
[ [ "post-1" ]
]
[]
[ ( [ "post-2" ]
, DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages" starDecoder
)
]
|> ProgramTest.expectOutgoingPortValues
"toJsPort"
(Codec.decoder (ToJsPayload.successCodecNew2 "" ""))
(\actualPorts ->
case actualPorts of
[ ToJsPayload.PageProgress portData ] ->
portData.is404
|> Expect.true "Expected 404 not found page"
_ ->
Expect.fail <| "Expected exactly 1 port of type PageProgress. Instead, got \n" ++ Debug.toString actualPorts
)
]
, test "the stripped JSON from the same request with different decoders is merged so the decoders succeed" <|
\() ->
startSimple
[ "post-1" ]
(DataSource.map2 Tuple.pair
(DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages"
(JD.field "stargazer_count" JD.int)
)
(DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages"
(JD.field "language" JD.string)
)
)
|> simulateHttp
(get "https://api.github.com/repos/dillonkearns/elm-pages")
"""{ "stargazer_count": 86, "language": "Elm" }"""
|> expectSuccess
[ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
, """{"stargazer_count":86,"language":"Elm"}"""
)
]
, test "andThen" <|
\() ->
startSimple
[ "elm-pages" ]
(DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages" (JD.succeed ())
|> DataSource.andThen
(\_ ->
DataSource.Http.get "NEXT-REQUEST" (JD.succeed ())
)
)
|> simulateHttp
(get "https://api.github.com/repos/dillonkearns/elm-pages")
"""null"""
|> simulateHttp
(get "NEXT-REQUEST")
"""null"""
|> expectSuccess
[ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
, """null"""
)
, ( get "NEXT-REQUEST"
, """null"""
)
]
, test "andThen chain avoids repeat requests" <|
\() ->
let
getReq : String -> Decoder a -> DataSource a
getReq url decoder =
DataSource.Http.request
(get url)
(DataSource.Http.expectJson decoder)
pokemonDetailRequest : DataSource ()
pokemonDetailRequest =
getReq
"https://pokeapi.co/api/v2/pokemon/"
(JD.list
(JD.field "url" JD.string
|> JD.map
(\url ->
getReq url
(JD.field "image" JD.string)
)
)
)
|> DataSource.resolve
|> DataSource.map (\_ -> ())
in
startSimple
[ "elm-pages" ]
pokemonDetailRequest
|> simulateMultipleHttp
[ ( get "https://pokeapi.co/api/v2/pokemon/"
, """[
{"url": "url1"},
{"url": "url2"},
{"url": "url3"},
{"url": "url4"},
{"url": "url5"},
{"url": "url6"},
{"url": "url7"},
{"url": "url8"},
{"url": "url9"},
{"url": "url10"}
]"""
)
, ( get "url1"
, """{"image": "image1.jpg"}"""
)
, ( get "url2"
, """{"image": "image2.jpg"}"""
)
, ( get "url3"
, """{"image": "image3.jpg"}"""
)
, ( get "url4"
, """{"image": "image4.jpg"}"""
)
, ( get "url5"
, """{"image": "image5.jpg"}"""
)
, ( get "url6"
, """{"image": "image6.jpg"}"""
)
, ( get "url7"
, """{"image": "image7.jpg"}"""
)
, ( get "url8"
, """{"image": "image8.jpg"}"""
)
, ( get "url9"
, """{"image": "image9.jpg"}"""
)
, ( get "url10"
, """{"image": "image10.jpg"}"""
)
]
|> expectSuccess
[ ( get "https://pokeapi.co/api/v2/pokemon/"
, """[{"url":"url1"},{"url":"url2"},{"url":"url3"},{"url":"url4"},{"url":"url5"},{"url":"url6"},{"url":"url7"},{"url":"url8"},{"url":"url9"},{"url":"url10"}]"""
)
, ( get "url1"
, """{"image":"image1.jpg"}"""
)
, ( get "url2"
, """{"image":"image2.jpg"}"""
)
, ( get "url3"
, """{"image":"image3.jpg"}"""
)
, ( get "url4"
, """{"image":"image4.jpg"}"""
)
, ( get "url5"
, """{"image":"image5.jpg"}"""
)
, ( get "url6"
, """{"image":"image6.jpg"}"""
)
, ( get "url7"
, """{"image":"image7.jpg"}"""
)
, ( get "url8"
, """{"image":"image8.jpg"}"""
)
, ( get "url9"
, """{"image":"image9.jpg"}"""
)
, ( get "url10"
, """{"image":"image10.jpg"}"""
)
]
--, test "port is sent out once all requests are finished" <|
-- \() ->
-- start
-- [ ( [ "elm-pages" ]
-- , DataSource.Http.get (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages") starDecoder
-- )
-- , ( [ "elm-pages-starter" ]
-- , DataSource.Http.get (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages-starter") starDecoder
-- )
-- ]
-- |> ProgramTest.simulateHttpOk
-- "GET"
-- "https://api.github.com/repos/dillonkearns/elm-pages"
-- """{ "stargazer_count": 86 }"""
-- |> ProgramTest.simulateHttpOk
-- "GET"
-- "https://api.github.com/repos/dillonkearns/elm-pages-starter"
-- """{ "stargazer_count": 22 }"""
-- |> expectSuccess
-- [ ( "elm-pages"
-- , [ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
-- , """{"stargazer_count":86}"""
-- )
-- ]
-- )
-- , ( "elm-pages-starter"
-- , [ ( get "https://api.github.com/repos/dillonkearns/elm-pages-starter"
-- , """{"stargazer_count":22}"""
-- )
-- ]
-- )
-- ]
, test "reduced JSON is sent out" <|
\() ->
startSimple []
(DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages" (JD.field "stargazer_count" JD.int))
|> simulateHttp
(get "https://api.github.com/repos/dillonkearns/elm-pages")
"""{ "stargazer_count": 86, "unused_field": 123 }"""
|> expectSuccess
[ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
, """{"stargazer_count":86}"""
)
]
, test "you can use elm/json decoders with StaticHttp.unoptimizedRequest" <|
\() ->
startSimple []
(DataSource.Http.request
{ url = "https://api.github.com/repos/dillonkearns/elm-pages"
, method = "GET"
, headers = []
, body = DataSource.Http.emptyBody
}
(DataSource.Http.expectJson
(JD.field "stargazer_count" JD.int)
)
)
|> simulateHttp
(get "https://api.github.com/repos/dillonkearns/elm-pages")
"""{ "stargazer_count": 86, "unused_field": 123 }"""
|> expectSuccess
[ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
, """{ "stargazer_count": 86, "unused_field": 123 }"""
)
]
, test "plain string" <|
\() ->
startSimple []
(DataSource.Http.request
{ url = "https://example.com/file.txt"
, method = "GET"
, headers = []
, body = DataSource.Http.emptyBody
}
(DataSource.Http.expectString Ok)
)
|> simulateHttp
(get "https://example.com/file.txt")
"This is a raw text file."
|> expectSuccess
[ ( get "https://example.com/file.txt"
, "This is a raw text file."
)
]
, test "Err in String to Result function turns into decode error" <|
\() ->
startSimple []
(DataSource.Http.request
{ url = "https://example.com/file.txt"
, method = "GET"
, headers = []
, body = DataSource.Http.emptyBody
}
(DataSource.Http.expectString
(\string ->
if String.toUpper string == string then
Ok string
else
Err "String was not uppercased"
)
)
)
|> simulateHttp
(get "https://example.com/file.txt")
"This is a raw text file."
|> ProgramTest.expectOutgoingPortValues
"toJsPort"
(Codec.decoder (ToJsPayload.successCodecNew2 "" ""))
(expectErrorsPort
"""-- STATIC HTTP DECODING ERROR ----------------------------------------------------- elm-pages
String was not uppercased"""
)
, test "POST method works" <|
\() ->
startSimple []
(DataSource.Http.request
{ method = "POST"
, url = "https://api.github.com/repos/dillonkearns/elm-pages"
, headers = []
, body = DataSource.Http.emptyBody
}
(DataSource.Http.expectJson
(JD.field "stargazer_count" JD.int)
)
)
|> simulateHttp
(post "https://api.github.com/repos/dillonkearns/elm-pages")
"""{ "stargazer_count": 86, "unused_field": 123 }"""
|> expectSuccess
[ ( { method = "POST"
, url = "https://api.github.com/repos/dillonkearns/elm-pages"
, headers = []
, body = DataSource.Http.emptyBody
}
, """{"stargazer_count":86}"""
)
]
, test "json is reduced from andThen chains" <|
\() ->
startSimple []
(DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages" (JD.field "stargazer_count" JD.int)
|> DataSource.andThen
(\_ ->
DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages-starter" (JD.field "stargazer_count" JD.int)
)
)
|> simulateHttp
(get "https://api.github.com/repos/dillonkearns/elm-pages")
"""{ "stargazer_count": 100, "unused_field": 123 }"""
|> simulateHttp
(get "https://api.github.com/repos/dillonkearns/elm-pages-starter")
"""{ "stargazer_count": 50, "unused_field": 456 }"""
|> expectSuccess
[ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
, """{"stargazer_count":100}"""
)
, ( get "https://api.github.com/repos/dillonkearns/elm-pages-starter"
, """{"stargazer_count":50}"""
)
]
, test "reduced json is preserved by StaticHttp.map2" <|
\() ->
startSimple []
(DataSource.map2 (\_ _ -> ())
(DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages" (JD.field "stargazer_count" JD.int))
(DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages-starter" (JD.field "stargazer_count" JD.int))
)
|> simulateMultipleHttp
[ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
, """{ "stargazer_count": 100, "unused_field": 123 }"""
)
, ( get "https://api.github.com/repos/dillonkearns/elm-pages-starter"
, """{ "stargazer_count": 50, "unused_field": 456 }"""
)
]
|> expectSuccess
[ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
, """{"stargazer_count":100}"""
)
, ( get "https://api.github.com/repos/dillonkearns/elm-pages-starter"
, """{"stargazer_count":50}"""
)
]
, test "the port sends out even if there are no http requests" <|
\() ->
start
[ ( []
, DataSource.succeed ()
)
]
|> expectSuccess []
, test "the port sends out when there are duplicate http requests for the same page" <|
\() ->
startSimple []
(DataSource.map2 (\_ _ -> ())
(DataSource.Http.get "http://example.com" (JD.succeed ()))
(DataSource.Http.get "http://example.com" (JD.succeed ()))
)
|> simulateHttp
(get "http://example.com")
"""null"""
|> expectSuccess
[ ( get "http://example.com"
, """null"""
)
]
, test "an error is sent out for decoder failures" <|
\() ->
startSimple [ "elm-pages" ]
(DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages" (JD.fail "The user should get this message from the CLI."))
|> simulateHttp
(get "https://api.github.com/repos/dillonkearns/elm-pages")
"""{ "stargazer_count": 86 }"""
|> ProgramTest.expectOutgoingPortValues
"toJsPort"
(Codec.decoder (ToJsPayload.successCodecNew2 "" ""))
(expectErrorsPort
"""-- STATIC HTTP DECODING ERROR ----------------------------------------------------- elm-pages
I encountered some errors while decoding this JSON:
The user should get this message from the CLI.
{
"stargazer_count": 86
}"""
)
, describe "staticHttpCache"
[ test "it doesn't perform http requests that are provided in the http cache flag" <|
\() ->
startWithHttpCache
[ ( { url = "https://api.github.com/repos/dillonkearns/elm-pages"
, method = "GET"
, headers = []
, body = StaticHttpBody.EmptyBody
}
, """{"stargazer_count":86}"""
)
]
[ ( []
, DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages" starDecoder
)
]
|> expectSuccess
[ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
, """{"stargazer_count":86}"""
)
]
, test "it ignores unused cache" <|
\() ->
startSimpleWithCache []
(DataSource.Http.get "https://api.github.com/repos/dillonkearns/elm-pages" starDecoder)
[ ( { url = "https://this-is-never-used.example.com/"
, method = "GET"
, headers = []
, body = StaticHttpBody.EmptyBody
}
, """{"stargazer_count":86}"""
)
]
|> simulateHttp
(get "https://api.github.com/repos/dillonkearns/elm-pages")
"""{ "stargazer_count": 86 }"""
|> expectSuccess
[ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
, """{"stargazer_count":86}"""
)
]
]
--, describe "generateFiles"
-- [ test "initial requests are sent out" <|
-- \() ->
-- startLowLevel
-- [ ApiRoute.succeed
-- (DataSource.Http.get (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages")
-- (starDecoder
-- |> Decode.map
-- (\starCount ->
-- { body = "Star count: " ++ String.fromInt starCount
-- }
-- )
-- )
-- )
-- |> ApiRoute.literal "test.txt"
-- |> ApiRoute.single
-- ]
-- []
-- []
-- |> ProgramTest.simulateHttpOk
-- "GET"
-- "https://api.github.com/repos/dillonkearns/elm-pages"
-- """{ "stargazer_count": 86 }"""
-- |> expectSuccessNew
-- []
-- [ \success ->
-- success.filesToGenerate
-- |> Expect.equal
-- [ { path = [ "test.txt" ]
-- , content = "Star count: 86"
-- }
-- ]
-- ]
-- , test "it sends success port when no HTTP requests are needed because they're all cached" <|
-- \() ->
-- startLowLevel
-- [ ApiRoute.succeed
-- (DataSource.Http.get (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages-starter")
-- (starDecoder
-- |> Decode.map
-- (\starCount ->
-- { body = "Star count: " ++ String.fromInt starCount
-- }
-- )
-- )
-- )
-- |> ApiRoute.literal "test.txt"
-- |> ApiRoute.single
-- ]
-- [ ( { url = "https://api.github.com/repos/dillonkearns/elm-pages"
-- , method = "GET"
-- , headers = []
-- , body = StaticHttpBody.EmptyBody
-- }
-- , """{"stargazer_count":86}"""
-- )
-- , ( { url = "https://api.github.com/repos/dillonkearns/elm-pages-starter"
-- , method = "GET"
-- , headers = []
-- , body = StaticHttpBody.EmptyBody
-- }
-- , """{"stargazer_count":23}"""
-- )
-- ]
-- [ ( []
-- , DataSource.Http.get (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages") starDecoder
-- )
-- ]
-- |> expectSuccessNew
-- [ ( ""
-- , [ ( get "https://api.github.com/repos/dillonkearns/elm-pages"
-- , """{"stargazer_count":86}"""
-- )
-- ]
-- )
-- ]
-- [ \success ->
-- success.filesToGenerate
-- |> Expect.equal
-- [ { path = [ "test.txt" ]
-- , content = "Star count: 23"
-- }
-- ]
-- ]
-- ]
]
type Route
= Route String
start : List ( List String, DataSource a ) -> ProgramTest (Model Route) Msg Effect
start pages =
startWithHttpCache [] pages
startWithHttpCache :
List ( Request.Request, String )
-> List ( List String, DataSource a )
-> ProgramTest (Model Route) Msg Effect
startWithHttpCache =
startLowLevel []
startLowLevel :
List (ApiRoute.ApiRoute ApiRoute.Response)
-> List ( Request.Request, String )
-> List ( List String, DataSource a )
-> ProgramTest (Model Route) Msg Effect
startLowLevel apiRoutes staticHttpCache pages =
let
pageToLoad : List String
pageToLoad =
case pages |> List.head |> Maybe.map Tuple.first of
Just justPageToLoad ->
justPageToLoad
Nothing ->
Debug.todo "Error - no pages"
config : ProgramConfig Msg () Route () () ()
config =
{ toJsPort = toJsPort
, fromJsPort = fromJsPort
, init = \_ _ _ _ _ -> ( (), Cmd.none )
, getStaticRoutes =
--StaticHttp.get (Secrets.succeed "https://my-cms.com/posts")
-- (Decode.field "posts" (Decode.list (Decode.string |> Decode.map Route)))
pages
|> List.map Tuple.first
|> List.map (String.join "/")
|> List.map Route
|> DataSource.succeed
, handleRoute = \_ -> DataSource.succeed Nothing
, urlToRoute = .path >> Route
, update = \_ _ _ _ _ -> ( (), Cmd.none )
, basePath = []
, data =
\(Route pageRoute) ->
let
thing : Maybe (DataSource a)
thing =
pages
|> Dict.fromList
|> Dict.get
(pageRoute
|> String.split "/"
|> List.filter (\pathPart -> pathPart /= "")
)
in
case thing of
Just request ->
request |> DataSource.map (\_ -> Response.render ())
Nothing ->
Debug.todo <| "Couldn't find page: " ++ pageRoute ++ "\npages: " ++ Debug.toString pages
, site = Just site
, view =
\page _ ->
let
thing : Maybe (DataSource a)
thing =
pages
|> Dict.fromList
|> Dict.get
(page.path |> Path.toSegments)
in
case thing of
Just _ ->
\_ _ -> { view = \_ -> { title = "Title", body = Html.text "" }, head = [] }
Nothing ->
Debug.todo <| "Couldn't find page: " ++ Debug.toString page ++ "\npages: " ++ Debug.toString pages
, subscriptions = \_ _ _ -> Sub.none
, routeToPath = \(Route route) -> route |> String.split "/"
, sharedData = DataSource.succeed ()
, onPageChange = \_ -> Continue
, apiRoutes = \_ -> apiRoutes
, pathPatterns = []
, byteDecodePageData = \_ -> Bytes.Decode.fail
, sendPageData = \_ -> Cmd.none
, encodeResponse = \_ -> Bytes.Encode.signedInt8 0
, hotReloadData = Sub.none
, decodeResponse = Bytes.Decode.fail
, byteEncodePageData = \_ -> Bytes.Encode.signedInt8 0
, fetchPageData = \_ -> Task.fail Http.NetworkError
}
encodedFlags : Encode.Value
encodedFlags =
--{"secrets":
-- {"API_KEY": "ABCD1234","BEARER": "XYZ789"}, "mode": "prod", "staticHttpCache": {}
-- }
Encode.object
[ ( "secrets"
, [ ( "API_KEY", "ABCD1234" )
, ( "BEARER", "XYZ789" )
]
|> Dict.fromList
|> Encode.dict identity Encode.string
)
, ( "mode", Encode.string "prod" )
, ( "staticHttpCache", encodedStaticHttpCache )
]
encodedStaticHttpCache : Encode.Value
encodedStaticHttpCache =
staticHttpCache
|> List.map
(\( request, httpResponseString ) ->
( Request.hash request, Encode.string httpResponseString )
)
|> Encode.object
in
{-
(Model -> model)
-> ContentCache.ContentCache metadata view
-> Result (List BuildError) (List ( PagePath, metadata ))
-> Config pathKey userMsg userModel metadata view
-> Decode.Value
-> ( model, Effect pathKey )
-}
ProgramTest.createDocument
{ init =
init
site
(RenderRequest.SinglePage
RenderRequest.OnlyJson
(RenderRequest.Page
{ path = Path.fromString (pageToLoad |> String.join "/")
, frontmatter = Route (pageToLoad |> String.join "/")
}
)
(Encode.object [])
)
config
, update = update site config
, view = \_ -> { title = "", body = [] }
}
|> ProgramTest.withSimulatedEffects simulateEffects
|> ProgramTest.start (flags (Encode.encode 0 encodedFlags))
site : SiteConfig ()
site =
{ data = DataSource.succeed ()
, canonicalUrl = "canonical-site-url"
, manifest = \_ -> manifest
, head = \_ -> []
}
startSimple : List String -> DataSource a -> ProgramTest (Model Route) Msg Effect
startSimple route dataSources =
startWithRoutes route [ route ] [] [ ( route, dataSources ) ]
startSimpleWithCache : List String -> DataSource a -> List ( Request.Request, String ) -> ProgramTest (Model Route) Msg Effect
startSimpleWithCache route dataSources cache =
startWithRoutes route [ route ] cache [ ( route, dataSources ) ]
startWithRoutes :
List String
-> List (List String)
-> List ( Request.Request, String )
-> List ( List String, DataSource a )
-> ProgramTest (Model Route) Msg Effect
startWithRoutes pageToLoad staticRoutes staticHttpCache pages =
let
config : ProgramConfig Msg () Route () () ()
config =
{ toJsPort = toJsPort
, fromJsPort = fromJsPort
, init = \_ _ _ _ _ -> ( (), Cmd.none )
, getStaticRoutes =
staticRoutes
|> List.map (String.join "/")
|> List.map Route
|> DataSource.succeed
, handleRoute =
\(Route route) ->
staticRoutes
|> List.map (String.join "/")
|> List.member route
|> (\found ->
if found then
Nothing
else
Just Pages.Internal.NotFoundReason.NoMatchingRoute
)
|> DataSource.succeed
, urlToRoute = .path >> Route
, update = \_ _ _ _ _ -> ( (), Cmd.none )
, basePath = []
, data =
\(Route pageRoute) ->
let
thing : Maybe (DataSource a)
thing =
pages
|> Dict.fromList
|> Dict.get
(pageRoute
|> String.split "/"
|> List.filter (\pathPart -> pathPart /= "")
)
in
case thing of
Just request ->
request
|> DataSource.map (\_ -> Response.render ())
Nothing ->
DataSource.fail <| "Couldn't find page: " ++ pageRoute ++ "\npages: " ++ Debug.toString pages
, site = Just site
, view =
\page _ ->
let
thing : Maybe (DataSource a)
thing =
pages
|> Dict.fromList
|> Dict.get
(page.path |> Path.toSegments)
in
case thing of
Just _ ->
\_ _ -> { view = \_ -> { title = "Title", body = Html.text "" }, head = [] }
Nothing ->
Debug.todo <| "Couldn't find page: " ++ Debug.toString page ++ "\npages: " ++ Debug.toString pages
, subscriptions = \_ _ _ -> Sub.none
, routeToPath = \(Route route) -> route |> String.split "/"
, sharedData = DataSource.succeed ()
, onPageChange = \_ -> Continue
, apiRoutes = \_ -> []
, pathPatterns = []
, byteDecodePageData = \_ -> Bytes.Decode.fail
, sendPageData = \_ -> Cmd.none
, encodeResponse = \_ -> Bytes.Encode.signedInt8 0
, hotReloadData = Sub.none
, decodeResponse = Bytes.Decode.fail
, byteEncodePageData = \_ -> Bytes.Encode.signedInt8 0
, fetchPageData = \_ -> Task.fail Http.NetworkError
}
encodedFlags : Encode.Value
encodedFlags =
--{"secrets":
-- {"API_KEY": "ABCD1234","BEARER": "XYZ789"}, "mode": "prod", "staticHttpCache": {}
-- }
Encode.object
[ ( "secrets"
, [ ( "API_KEY", "ABCD1234" )
, ( "BEARER", "XYZ789" )
]
|> Dict.fromList
|> Encode.dict identity Encode.string
)
, ( "staticHttpCache", encodedStaticHttpCache )
, ( "mode", Encode.string "dev-server" )
]
encodedStaticHttpCache : Encode.Value
encodedStaticHttpCache =
staticHttpCache
|> List.map
(\( request, httpResponseString ) ->
( Request.hash request, Encode.string httpResponseString )
)
|> Encode.object
in
{-
(Model -> model)
-> ContentCache.ContentCache metadata view
-> Result (List BuildError) (List ( PagePath, metadata ))
-> Config pathKey userMsg userModel metadata view
-> Decode.Value
-> ( model, Effect pathKey )
-}
ProgramTest.createDocument
{ init =
init
site
(RenderRequest.SinglePage
RenderRequest.OnlyJson
(RenderRequest.Page
{ path = Path.fromString (pageToLoad |> String.join "/")
, frontmatter = Route (pageToLoad |> String.join "/")
}
)
(Encode.object [])
)
config
, update = update site config
, view = \_ -> { title = "", body = [] }
}
|> ProgramTest.withSimulatedEffects simulateEffects
|> ProgramTest.withSimulatedSubscriptions simulateSubscriptions
|> ProgramTest.start (flags (Encode.encode 0 encodedFlags))
flags : String -> JD.Value
flags jsonString =
case JD.decodeString JD.value jsonString of
Ok value ->
value
Err _ ->
Debug.todo "Invalid JSON value."
sendToJsPort : ToJsPayload.ToJsSuccessPayloadNewCombined -> ProgramTest.SimulatedEffect msg
sendToJsPort value =
SimulatedEffect.Ports.send "toJsPort" (value |> Codec.encoder (ToJsPayload.successCodecNew2 "" ""))
simulateEffects : Effect -> ProgramTest.SimulatedEffect Msg
simulateEffects effect =
case effect of
Effect.NoEffect ->
SimulatedEffect.Cmd.none
-- toJsPort value |> Cmd.map never
Effect.Batch list ->
list
|> List.map simulateEffects
|> SimulatedEffect.Cmd.batch
Effect.FetchHttp unmasked ->
if unmasked.url |> String.startsWith "file://" then
let
filePath : String
filePath =
String.dropLeft 7 unmasked.url
in
ToJsPayload.ReadFile filePath
|> sendToJsPort
|> SimulatedEffect.Cmd.map never
else if unmasked.url |> String.startsWith "glob://" then
let
globPattern : String
globPattern =
String.dropLeft 7 unmasked.url
in
ToJsPayload.Glob globPattern
|> sendToJsPort
|> SimulatedEffect.Cmd.map never
else if unmasked.url |> String.startsWith "port://" then
let
portName : String
portName =
String.dropLeft 7 unmasked.url
in
ToJsPayload.Port portName
|> sendToJsPort
|> SimulatedEffect.Cmd.map never
else
ToJsPayload.DoHttp unmasked
|> sendToJsPort
|> SimulatedEffect.Cmd.map never
Effect.SendSinglePage done info ->
SimulatedEffect.Cmd.batch
[ info
|> Codec.encoder (ToJsPayload.successCodecNew2 "" "")
|> SimulatedEffect.Ports.send "toJsPort"
, if done then
SimulatedEffect.Cmd.none
else
SimulatedEffect.Task.succeed ()
|> SimulatedEffect.Task.perform (\_ -> Continue)
]
Effect.Continue ->
SimulatedEffect.Cmd.none
Effect.SendSinglePageNew done _ toJsSuccessPayloadNewCombined ->
SimulatedEffect.Cmd.batch
[ toJsSuccessPayloadNewCombined
|> Codec.encoder (ToJsPayload.successCodecNew2 "" "")
|> SimulatedEffect.Ports.send "toJsPort"
, if done then
SimulatedEffect.Cmd.none
else
SimulatedEffect.Task.succeed ()
|> SimulatedEffect.Task.perform (\_ -> Continue)
]
expectErrorsPort : String -> List ToJsPayload.ToJsSuccessPayloadNewCombined -> Expect.Expectation
expectErrorsPort expectedPlainString actualPorts =
case actualPorts |> List.reverse |> List.head of
Just (ToJsPayload.Errors actualRichTerminalString) ->
actualRichTerminalString
|> List.map .title
|> String.join "\n"
|> normalizeErrorExpectEqual expectedPlainString
Nothing ->
Expect.fail "Expected single error port. Didn't receive any ports."
_ ->
Expect.fail <| "Expected single error port. Got\n" ++ String.join "\n\n" (List.map Debug.toString actualPorts)
normalizeErrorExpectEqual : String -> String -> Expect.Expectation
normalizeErrorExpectEqual expectedPlainString actualRichTerminalString =
actualRichTerminalString
|> Regex.replace
-- strip out all possible ANSI sequences
(Regex.fromString "(\\x9B|\\x1B\\[)[0-?]*[ -/]*[@-~]"
|> Maybe.withDefault Regex.never
)
(\_ -> "")
|> String.replace "\u{001B}" ""
|> normalizeNewlines
|> Expect.equal
(expectedPlainString |> normalizeNewlines)
normalizeNewlines : String -> String
normalizeNewlines string =
string
|> Regex.replace
(Regex.fromString "(\n)+" |> Maybe.withDefault Regex.never)
(\_ -> "")
|> Regex.replace
(Regex.fromString "( )+" |> Maybe.withDefault Regex.never)
(\_ -> " ")
|> String.replace "\u{000D}" ""
|> Regex.replace
(Regex.fromString "\\s" |> Maybe.withDefault Regex.never)
(\_ -> "")
toJsPort : a -> Cmd msg
toJsPort _ =
Cmd.none
fromJsPort : Sub msg
fromJsPort =
Sub.none
manifest : Manifest.Config
manifest =
Manifest.init
{ description = "elm-pages - A statically typed site generator."
, name = "elm-pages docs"
, startUrl = Path.join []
, icons = []
}
starDecoder : Decoder Int
starDecoder =
JD.field "stargazer_count" JD.int
expectSuccess : List ( Request.Request, String ) -> ProgramTest model msg effect -> Expect.Expectation
expectSuccess expectedRequests previous =
expectSuccessNew expectedRequests [] previous
expectSuccessNew : List ( Request.Request, String ) -> List (ToJsPayload.ToJsSuccessPayloadNew -> Expect.Expectation) -> ProgramTest model msg effect -> Expect.Expectation
expectSuccessNew expectedRequest expectations previous =
previous
|> ProgramTest.expectOutgoingPortValues
"toJsPort"
(Codec.decoder (ToJsPayload.successCodecNew2 "" ""))
(\value ->
case value of
(ToJsPayload.PageProgress portPayload) :: _ ->
let
singleExpectation : ToJsPayload.ToJsSuccessPayloadNew -> Expect.Expectation
singleExpectation =
\subject ->
subject.contentJson
|> Expect.equal
(expectedRequest
|> List.map
(\( request, response ) ->
( Request.hash request, response )
)
|> Dict.fromList
)
in
portPayload
|> Expect.all
(singleExpectation
:: expectations
)
[ errorPort ] ->
Expect.fail <| "Expected success port. Got:\n" ++ Debug.toString errorPort
_ ->
Expect.fail ("Expected ports to be called once, but instead there were " ++ String.fromInt (List.length value) ++ " calls.")
)
simulateSubscriptions : a -> ProgramTest.SimulatedSub Msg
simulateSubscriptions _ =
SimulatedEffect.Ports.subscribe "fromJsPort"
(JD.field "tag" JD.string
|> JD.andThen
(\tag ->
case tag of
"GotGlob" ->
JD.field "data"
(JD.map2 Tuple.pair
(JD.field "pattern" JD.string)
(JD.field "result" JD.value)
)
|> JD.map
(\( globPattern, response ) ->
GotDataBatch
[ { request =
{ url = "glob://" ++ globPattern
, method = "GET"
, headers = []
, body = StaticHttpBody.EmptyBody
}
, response = Encode.encode 0 response
}
]
)
"GotFile" ->
JD.field "data"
(JD.map2 Tuple.pair
(JD.field "filePath" JD.string)
JD.value
)
|> JD.map
(\( filePath, response ) ->
GotDataBatch
[ { request =
{ url = "file://" ++ filePath
, method = "GET"
, headers = []
, body = StaticHttpBody.EmptyBody
}
, response = Encode.encode 0 response
}
]
)
"GotBatch" ->
JD.field "data"
(JD.list
(JD.map2
(\request response ->
{ request = request
, response = response
}
)
(JD.field "request" requestDecoder)
(JD.field "response" JD.string)
)
)
|> JD.map GotDataBatch
_ ->
JD.fail "Unexpected subscription tag."
)
)
identity
get : String -> Request.Request
get url =
{ method = "GET"
, url = url
, headers = []
, body = DataSource.Http.emptyBody
}
post : String -> Request.Request
post url =
{ method = "POST"
, url = url
, headers = []
, body = DataSource.Http.emptyBody
}
simulateHttp : Request.Request -> String -> ProgramTest model msg effect -> ProgramTest model msg effect
simulateHttp request response program =
program
|> ProgramTest.ensureOutgoingPortValues
"toJsPort"
(Codec.decoder (ToJsPayload.successCodecNew2 "" ""))
(\actualPorts ->
case actualPorts of
[ ToJsPayload.DoHttp _ ] ->
Expect.pass
_ ->
Expect.fail <|
"Expected an HTTP request, got:\n"
++ Debug.toString actualPorts
)
|> ProgramTest.simulateIncomingPort "fromJsPort"
(Encode.object
[ ( "tag", Encode.string "GotBatch" )
, ( "data"
, Encode.list
(\req ->
Encode.object
[ ( "request"
, Encode.object
[ ( "masked"
, Codec.encodeToValue Request.codec req
)
, ( "unmasked"
, Codec.encodeToValue Request.codec req
)
]
)
, ( "response", Encode.string response )
]
)
[ request ]
)
]
)
simulateMultipleHttp : List ( Request.Request, String ) -> ProgramTest model msg effect -> ProgramTest model msg effect
simulateMultipleHttp requests program =
program
|> ProgramTest.ensureOutgoingPortValues
"toJsPort"
(Codec.decoder (ToJsPayload.successCodecNew2 "" ""))
(\actualPorts ->
case actualPorts of
(ToJsPayload.DoHttp _) :: _ ->
-- TODO check count of HTTP requests, and check the URLs
Expect.pass
_ ->
Expect.fail <|
"Expected an HTTP request, got:\n"
++ Debug.toString actualPorts
)
|> ProgramTest.simulateIncomingPort "fromJsPort"
(Encode.object
[ ( "tag", Encode.string "GotBatch" )
, ( "data"
, Encode.list
(\( req, response ) ->
Encode.object
[ ( "request"
, Encode.object
[ ( "masked"
, Codec.encodeToValue Request.codec req
)
, ( "unmasked"
, Codec.encodeToValue Request.codec req
)
]
)
, ( "response", Encode.string response )
]
)
requests
)
]
)