Resolve mock data in test prototype for initial route and on route change.

This commit is contained in:
Dillon Kearns 2022-03-15 21:10:32 -07:00
parent 7ff9b555f3
commit b8b4729821
4 changed files with 265 additions and 206 deletions

View File

@ -3,6 +3,7 @@ module Route.Blog.Slug_ exposing (Data, Model, Msg, route)
import DataSource exposing (DataSource)
import Head
import Head.Seo as Seo
import Html
import Pages.PageUrl exposing (PageUrl)
import Pages.Url
import RouteBuilder exposing (StatelessRoute, StaticPayload)
@ -34,7 +35,9 @@ route =
pages : DataSource (List RouteParams)
pages =
DataSource.succeed []
DataSource.succeed
[ { slug = "hello" }
]
type alias Data =
@ -74,4 +77,6 @@ view :
-> StaticPayload Data RouteParams
-> View Msg
view maybeUrl sharedModel static =
View.placeholder "Blog.Slug_"
{ title = "Placeholder - Blog.Slug_"
, body = [ Html.text "You're on the page Blog.Slug_" ]
}

View File

@ -82,5 +82,6 @@ view maybeUrl sharedModel static =
, Html.p []
[ Html.text <| "The message is: " ++ static.data.message
]
, Html.a [ Attr.href "/blog/hello" ] [ Html.text "My blog post" ]
]
}

View File

@ -1,191 +1,221 @@
module Tests exposing (suite)
import Base64
import Browser
import Bytes.Encode
import Dict
import Expect
import Json.Encode as Encode
import Main
import Main exposing (config)
import PageServerResponse
import Pages.Flags exposing (Flags(..))
import Pages.Internal.NotFoundReason exposing (NotFoundReason)
import Pages.Internal.Platform as Platform
import Pages.Internal.ResponseSketch as ResponseSketch
import Pages.StaticHttp.Request
import Pages.StaticHttpRequest
import Path
import ProgramTest
import RequestsAndPending
import Route
import Route.Index
import Test exposing (Test, test)
import Shared
import SimulatedEffect.Cmd
import SimulatedEffect.Navigation
import SimulatedEffect.Task
import Test exposing (Test, describe, only, test)
import Test.Html.Selector exposing (text)
import Url exposing (Url)
suite : Test
suite =
test "wire up hello" <|
\() ->
start2
|> ProgramTest.clickButton "Open Menu"
|> ProgramTest.expectViewHas
[ text "elm-pages is up and running!"
, text "Close Menu"
, text "The message is: This is my message!!"
]
describe "end to end tests"
[ test "wire up hello" <|
\() ->
start "/"
|> ProgramTest.ensureViewHas
[ text "elm-pages is up and running!"
, text "The message is: This is my message!!"
]
|> ProgramTest.clickButton "Open Menu"
|> ProgramTest.clickButton "Close Menu"
|> ProgramTest.expectViewHas
[ text "Open Menu"
]
, test "data is fetched when navigating to new Route" <|
\() ->
start "/"
|> ProgramTest.ensureBrowserUrl (\currentUrl -> currentUrl |> Expect.equal "https://localhost:1234/")
|> ProgramTest.routeChange "/blog/hello"
-- TODO elm-program-test does not yet intercept link clicks when using Browser.application
-- see <https://github.com/avh4/elm-program-test/issues/107>
--|> ProgramTest.clickLink "My blog post" "/blog/hello"
|> ProgramTest.ensureBrowserUrl (\currentUrl -> currentUrl |> Expect.equal "https://localhost:1234/blog/hello")
|> ProgramTest.expectViewHas
[ text "You're on the page Blog.Slug_"
]
, test "initial page blog" <|
\() ->
start "/blog/hello"
|> ProgramTest.ensureBrowserUrl (\currentUrl -> currentUrl |> Expect.equal "https://localhost:1234/blog/hello")
|> ProgramTest.expectViewHas
[ text "You're on the page Blog.Slug_"
]
]
start =
ProgramTest.createApplication
{ onUrlRequest =
\urlRequest ->
case urlRequest of
Browser.Internal url ->
Main.OnPageChange
{ protocol = url.protocol
, host = url.host
, port_ = url.port_
, path = url.path |> Path.fromString
, query = url.query
, fragment = url.fragment
, metadata = route
start :
String
->
ProgramTest.ProgramTest
(Platform.Model Main.Model Main.PageData Shared.Data)
(Platform.Msg Main.Msg Main.PageData Shared.Data)
(Platform.Effect Main.Msg Main.PageData Shared.Data)
start initialPath =
let
resolvedSharedData : Shared.Data
resolvedSharedData =
Pages.StaticHttpRequest.mockResolve
Shared.template.data
mockData
|> expectOk
flagsWithData =
Encode.object
[ ( "pageDataBase64"
, (case initialRouteNotFoundReason of
Just notFoundReason ->
{ reason = notFoundReason
, path = Path.fromString initialPath
}
|> ResponseSketch.NotFound
Browser.External _ ->
Debug.todo "Unhandled"
, onUrlChange =
\url ->
Main.OnPageChange
{ protocol = url.protocol
, host = url.host
, port_ = url.port_
, path = url.path |> Path.fromString
, query = url.query
, fragment = url.fragment
, metadata = route
}
, init =
\flags initialUrl () ->
Main.init
sharedModel
flags
sharedData
pageData
-- navKey
Nothing
-- Path and stuff
(Just
{ path =
{ path = Path.join []
, query = Nothing
, fragment = Nothing
}
, metadata = route
, pageUrl = Nothing -- TODO --Maybe PageUrl
}
Nothing ->
ResponseSketch.HotUpdate
responseSketchData
resolvedSharedData
)
, update =
\msg model ->
Main.update
sharedData
pageData
Nothing
msg
model
, view =
\model ->
model
|> (Main.view
{ path = path
, route = route
}
Nothing
sharedData
pageData
|> .view
)
|> (\{ title, body } -> { title = title, body = [ body ] })
}
|> ProgramTest.withBaseUrl "https://my-app.com/"
|> ProgramTest.start Pages.Flags.PreRenderFlags
|> Main.encodeResponse
|> Bytes.Encode.encode
|> Base64.fromBytes
|> expectJust
|> Encode.string
)
]
initialRoute : Maybe Route.Route
initialRoute =
Main.config.urlToRoute { path = initialPath }
start2 =
initialRouteNotFoundReason : Maybe NotFoundReason
initialRouteNotFoundReason =
Pages.StaticHttpRequest.mockResolve
(config.handleRoute initialRoute)
mockData
|> expectOk
newDataMock : Result Pages.StaticHttpRequest.Error (PageServerResponse.PageServerResponse Main.PageData)
newDataMock =
Pages.StaticHttpRequest.mockResolve
(Main.config.data initialRoute)
mockData
responseSketchData : Main.PageData
responseSketchData =
case newDataMock of
Ok (PageServerResponse.RenderPage info newPageData) ->
newPageData
_ ->
Debug.todo "Unhandled"
in
ProgramTest.createApplication
{ onUrlRequest = Platform.LinkClicked
, onUrlChange = Platform.UrlChanged
, init =
\flags url () ->
Platform.init Main.config
flags
url
Nothing
Platform.init Main.config flags url Nothing
, update =
\msg model ->
Platform.update Main.config
msg
model
Platform.update Main.config msg model
, view =
\model ->
Platform.view Main.config model
}
|> ProgramTest.withBaseUrl "https://my-app.com/"
|> ProgramTest.withBaseUrl ("https://localhost:1234" ++ initialPath)
|> ProgramTest.withSimulatedEffects perform
|> ProgramTest.start flagsWithData
path =
Path.join []
mockData : Pages.StaticHttp.Request.Request -> Maybe RequestsAndPending.Response
mockData request =
RequestsAndPending.Response Nothing
(RequestsAndPending.JsonBody
(Encode.object
[ ( "message", Encode.string "This is my message!!" )
]
)
)
|> Just
sharedData =
()
perform :
Platform.Effect userMsg Main.PageData Shared.Data
-> ProgramTest.SimulatedEffect (Platform.Msg userMsg Main.PageData Shared.Data)
perform effect =
case effect of
Platform.NoEffect ->
SimulatedEffect.Cmd.none
Platform.ScrollToTop ->
SimulatedEffect.Cmd.none
Platform.BrowserLoadUrl url ->
SimulatedEffect.Navigation.load url
Platform.BrowserPushUrl url ->
SimulatedEffect.Navigation.pushUrl url
Platform.Batch effects ->
effects
|> List.map perform
|> SimulatedEffect.Cmd.batch
Platform.FetchPageData maybeRequestInfo url toMsg ->
let
newRoute : Maybe Route.Route
newRoute =
Main.config.urlToRoute url
newDataMock : Result Pages.StaticHttpRequest.Error (PageServerResponse.PageServerResponse Main.PageData)
newDataMock =
Pages.StaticHttpRequest.mockResolve
(Main.config.data newRoute)
mockData
responseSketchData : ResponseSketch.ResponseSketch Main.PageData shared
responseSketchData =
case newDataMock of
Ok (PageServerResponse.RenderPage info newPageData) ->
ResponseSketch.RenderPage newPageData
_ ->
Debug.todo "Unhandled"
msg : Result error ( Url, ResponseSketch.ResponseSketch Main.PageData shared )
msg =
Ok ( url, responseSketchData )
in
SimulatedEffect.Task.succeed msg
|> SimulatedEffect.Task.perform toMsg
Platform.UserCmd cmd ->
-- TODO need to turn this into an `Effect` defined by user - this is a temporary intermediary step to get there
-- TODO need to expose a way for the user to simulate their own Effect type (similar to elm-program-test's withSimulatedEffects)
SimulatedEffect.Cmd.none
pageData =
Main.DataIndex { message = "Hi!" }
route =
Just Route.Index
sharedModel =
Just { showMenu = False }
flagsWithData =
let
indexPageData =
Pages.StaticHttpRequest.mockResolve
(Route.Index.route.data {})
(\_ ->
RequestsAndPending.Response Nothing
(RequestsAndPending.JsonBody
(Encode.object
[ ( "message", Encode.string "This is my message!!" )
]
)
)
|> Just
)
|> expectOk
|> expectRenderResponse
|> Main.DataIndex
in
Encode.object
[ ( "pageDataBase64"
, ResponseSketch.HotUpdate
indexPageData
()
|> Main.encodeResponse
|> Bytes.Encode.encode
|> Base64.fromBytes
|> expectOrError
|> Encode.string
)
]
expectOrError thing =
case thing of
expectJust : Maybe a -> a
expectJust maybeValue =
case maybeValue of
Just justThing ->
justThing
@ -193,6 +223,7 @@ expectOrError thing =
Debug.todo "Expected Just but got Nothing"
expectOk : Result error a -> a
expectOk thing =
case thing of
Ok okThing ->
@ -200,12 +231,3 @@ expectOk thing =
Err error ->
Debug.todo <| "Expected Ok but got Err " ++ Debug.toString error
expectRenderResponse response =
case response of
PageServerResponse.RenderPage info pageData_ ->
pageData_
PageServerResponse.ServerResponse _ ->
Debug.todo "Unhandled: ServerResponse"

View File

@ -1,6 +1,6 @@
module Pages.Internal.Platform exposing
( Flags, Model, Msg(..), Program, application, init, update
, view
, Effect(..), view
)
{-| Exposed for internal use only (used in generated code).
@ -136,7 +136,7 @@ init :
-> Flags
-> Url
-> Maybe Browser.Navigation.Key
-> ( Model userModel pageData sharedData, Cmd (Msg userMsg pageData sharedData) )
-> ( Model userModel pageData sharedData, Effect userMsg pageData sharedData )
init config flags url key =
let
pageDataResult : Result BuildError (InitKind sharedData pageData)
@ -172,7 +172,7 @@ init config flags url key =
|> StaticHttpRequest.toBuildError url.path
)
in
case pageDataResult |> Debug.log "@@@pageDataResult" of
case pageDataResult of
Ok (OkPage sharedData pageData) ->
let
urls : { currentUrl : Url, basePath : List String }
@ -213,14 +213,9 @@ init config flags url key =
}
|> config.init userFlags sharedData pageData key
cmd : Cmd (Msg userMsg pageData sharedData)
cmd : Effect userMsg pageData sharedData
cmd =
[ userCmd
|> Cmd.map UserMsg
|> Just
]
|> List.filterMap identity
|> Cmd.batch
UserCmd userCmd
initialModel : Model userModel pageData sharedData
initialModel =
@ -251,7 +246,7 @@ init config flags url key =
, userFlags = flags
, notFound = Just info
}
, Cmd.none
, NoEffect
)
Err error ->
@ -262,7 +257,7 @@ init config flags url key =
, userFlags = flags
, notFound = Nothing
}
, Cmd.none
, NoEffect
)
@ -294,12 +289,22 @@ type alias Model userModel pageData sharedData =
}
type Effect userMsg pageData sharedData
= ScrollToTop
| NoEffect
| BrowserLoadUrl String
| BrowserPushUrl String
| FetchPageData (Maybe RequestInfo) Url (Result Http.Error ( Url, ResponseSketch pageData sharedData ) -> Msg userMsg pageData sharedData)
| Batch (List (Effect userMsg pageData sharedData))
| UserCmd (Cmd userMsg)
{-| -}
update :
ProgramConfig userMsg userModel route pageData sharedData
-> Msg userMsg pageData sharedData
-> Model userModel pageData sharedData
-> ( Model userModel pageData sharedData, Cmd (Msg userMsg pageData sharedData) )
-> ( Model userModel pageData sharedData, Effect userMsg pageData sharedData )
update config appMsg model =
case appMsg of
LinkClicked urlRequest ->
@ -313,16 +318,19 @@ update config appMsg model =
if navigatingToSamePage then
-- this is a workaround for an issue with anchor fragment navigation
-- see https://github.com/elm/browser/issues/39
( model, Browser.Navigation.load (Url.toString url) )
( model
, BrowserLoadUrl (Url.toString url)
)
else
( model
, config.fetchPageData url Nothing
|> Task.attempt (UpdateCacheAndUrlNew True url)
, FetchPageData Nothing url (UpdateCacheAndUrlNew True url)
)
Browser.External href ->
( model, Browser.Navigation.load href )
( model
, BrowserLoadUrl href
)
UrlChanged url ->
let
@ -375,29 +383,19 @@ update config appMsg model =
| url = url
, pageData = updatedPageData
}
, Cmd.none
--Cmd.batch
-- [ userCmd |> Cmd.map UserMsg
-- , Task.perform (\_ -> PageScrollComplete) (Dom.setViewport 0 0)
-- ]
, NoEffect
)
)
|> Result.withDefault ( model, Cmd.none )
|> Result.withDefault ( model, NoEffect )
else
( model
, config.fetchPageData url Nothing
|> Task.attempt (UpdateCacheAndUrlNew False url)
, FetchPageData Nothing url (UpdateCacheAndUrlNew False url)
)
ReloadCurrentPageData requestInfo ->
( model
, config.fetchPageData model.url (Just requestInfo)
|> Task.attempt (UpdateCacheAndUrlNew False model.url)
-- @@@ TODO re-implement with Bytes decoding
--model.contentCache
-- |> ContentCache.eagerLoad urls
-- |> Task.attempt (UpdateCacheAndUrl model.url)
, FetchPageData (Just requestInfo) model.url (UpdateCacheAndUrlNew False model.url)
)
UserMsg userMsg ->
@ -411,10 +409,12 @@ update config appMsg model =
updatedPageData =
Ok { pageData | userModel = userModel }
in
( { model | pageData = updatedPageData }, userCmd |> Cmd.map UserMsg )
( { model | pageData = updatedPageData }
, UserCmd userCmd
)
Err _ ->
( model, Cmd.none )
( model, NoEffect )
UpdateCacheAndUrlNew fromLinkClick urlWithoutRedirectResolution updateResult ->
case Result.map2 Tuple.pair (updateResult |> Result.mapError (\_ -> "Http error")) model.pageData of
@ -465,19 +465,14 @@ update config appMsg model =
( { updatedModel
| ariaNavigationAnnouncement = mainView config updatedModel |> .title
}
, Cmd.batch
[ userCmd |> Cmd.map UserMsg
, Task.perform (\_ -> PageScrollComplete) (Dom.setViewport 0 0)
, Batch
[ UserCmd userCmd
, ScrollToTop
, if fromLinkClick || urlWithoutRedirectResolution.path /= newUrl.path then
model.key
|> Maybe.map
(\key ->
Browser.Navigation.pushUrl key newUrl.path
)
|> Maybe.withDefault Cmd.none
BrowserPushUrl newUrl.path
else
Cmd.none
NoEffect
]
)
@ -498,11 +493,11 @@ update config appMsg model =
( model
, urlWithoutRedirectResolution
|> Url.toString
|> Browser.Navigation.load
|> BrowserLoadUrl
)
PageScrollComplete ->
( model, Cmd.none )
( model, NoEffect )
HotReloadCompleteNew pageDataBytes ->
model.pageData
@ -525,7 +520,7 @@ update config appMsg model =
}
, notFound = Nothing
}
, Cmd.none
, NoEffect
)
Just (ResponseSketch.HotUpdate newPageData newSharedData) ->
@ -538,16 +533,49 @@ update config appMsg model =
}
, notFound = Nothing
}
, Cmd.none
, NoEffect
)
Just (ResponseSketch.NotFound info) ->
( { model | notFound = Just info }, Cmd.none )
( { model | notFound = Just info }, NoEffect )
_ ->
( model, Cmd.none )
( model, NoEffect )
)
|> Result.withDefault ( model, Cmd.none )
|> Result.withDefault ( model, NoEffect )
perform : ProgramConfig userMsg userModel route pageData sharedData -> Maybe Browser.Navigation.Key -> Effect userMsg pageData sharedData -> Cmd (Msg userMsg pageData sharedData)
perform config maybeKey effect =
case effect of
NoEffect ->
Cmd.none
Batch effects ->
effects
|> List.map (perform config maybeKey)
|> Cmd.batch
ScrollToTop ->
Task.perform (\_ -> PageScrollComplete) (Dom.setViewport 0 0)
BrowserLoadUrl url ->
Browser.Navigation.load url
BrowserPushUrl url ->
maybeKey
|> Maybe.map
(\key ->
Browser.Navigation.pushUrl key url
)
|> Maybe.withDefault Cmd.none
FetchPageData maybeRequestInfo url toMsg ->
config.fetchPageData url maybeRequestInfo
|> Task.attempt toMsg
UserCmd cmd ->
cmd |> Cmd.map UserMsg
{-| -}
@ -559,8 +587,11 @@ application config =
{ init =
\flags url key ->
init config flags url (Just key)
|> Tuple.mapSecond (perform config (Just key))
, view = view config
, update = update config
, update =
\msg model ->
update config msg model |> Tuple.mapSecond (perform config model.key)
, subscriptions =
\model ->
case model.pageData of