Set up ProgramTest against the styleguide

This commit is contained in:
Tessa Kelly 2022-04-12 12:17:41 -07:00
parent b416afea46
commit 5e25c58ebb
4 changed files with 120 additions and 26 deletions

View File

@ -1,4 +1,4 @@
module App exposing (Model, Msg(..), init, subscriptions, update, view) module App exposing (Effect(..), Model, Msg(..), init, perform, subscriptions, update, view)
import Accessibility.Styled as Html exposing (Html) import Accessibility.Styled as Html exposing (Html)
import Browser exposing (Document, UrlRequest(..)) import Browser exposing (Document, UrlRequest(..))
@ -25,17 +25,17 @@ import Task
import Url exposing (Url) import Url exposing (Url)
type alias Model = type alias Model key =
{ -- Global UI { -- Global UI
route : Route route : Route
, previousRoute : Maybe Route , previousRoute : Maybe Route
, moduleStates : Dict String (Example Examples.State Examples.Msg) , moduleStates : Dict String (Example Examples.State Examples.Msg)
, navigationKey : Key , navigationKey : key
, elliePackageDependencies : Result Http.Error (Dict String String) , elliePackageDependencies : Result Http.Error (Dict String String)
} }
init : () -> Url -> Key -> ( Model, Cmd Msg ) init : () -> Url -> key -> ( Model key, Effect )
init () url key = init () url key =
( { route = Routes.fromLocation url ( { route = Routes.fromLocation url
, previousRoute = Nothing , previousRoute = Nothing
@ -49,6 +49,7 @@ init () url key =
[ loadPackage [ loadPackage
, loadApplicationDependencies , loadApplicationDependencies
] ]
|> Command
) )
@ -59,10 +60,10 @@ type Msg
| ChangeRoute Route | ChangeRoute Route
| SkipToMainContent | SkipToMainContent
| LoadedPackages (Result Http.Error (Dict String String)) | LoadedPackages (Result Http.Error (Dict String String))
| NoOp | Focused (Result Browser.Dom.Error ())
update : Msg -> Model -> ( Model, Cmd Msg ) update : Msg -> Model key -> ( Model key, Effect )
update action model = update action model =
case action of case action of
UpdateModuleStates key exampleMsg -> UpdateModuleStates key exampleMsg ->
@ -78,36 +79,35 @@ update action model =
model.moduleStates model.moduleStates
} }
) )
|> Tuple.mapSecond (Cmd.map (UpdateModuleStates key)) |> Tuple.mapSecond (Cmd.map (UpdateModuleStates key) >> Command)
Nothing -> Nothing ->
( model, Cmd.none ) ( model, None )
OnUrlRequest request -> OnUrlRequest request ->
case request of case request of
Internal loc -> Internal loc ->
( model, Browser.Navigation.pushUrl model.navigationKey (Url.toString loc) ) ( model, GoToUrl loc )
External loc -> External loc ->
( model, Browser.Navigation.load loc ) ( model, Load loc )
OnUrlChange route -> OnUrlChange route ->
( { model ( { model
| route = Routes.fromLocation route | route = Routes.fromLocation route
, previousRoute = Just model.route , previousRoute = Just model.route
} }
, Cmd.none , None
) )
ChangeRoute route -> ChangeRoute route ->
( model ( model
, Browser.Navigation.pushUrl model.navigationKey , GoToRoute route
(Routes.toString route)
) )
SkipToMainContent -> SkipToMainContent ->
( model ( model
, Task.attempt (\_ -> NoOp) (Browser.Dom.focus "maincontent") , FocusOn "maincontent"
) )
LoadedPackages newPackagesResult -> LoadedPackages newPackagesResult ->
@ -141,21 +141,52 @@ update action model =
(Result.map2 Dict.union model.elliePackageDependencies newPackagesResult) (Result.map2 Dict.union model.elliePackageDependencies newPackagesResult)
removedPackages removedPackages
} }
, Cmd.none , None
) )
NoOp -> Focused _ ->
( model, Cmd.none ) ( model, None )
subscriptions : Model -> Sub Msg type Effect
= GoToRoute Route
| GoToUrl Url
| Load String
| FocusOn String
| None
| Command (Cmd Msg)
perform : Key -> Effect -> Cmd Msg
perform navigationKey effect =
case effect of
GoToRoute route ->
Browser.Navigation.pushUrl navigationKey (Routes.toString route)
GoToUrl url ->
Browser.Navigation.pushUrl navigationKey (Url.toString url)
Load loc ->
Browser.Navigation.load loc
FocusOn id ->
Task.attempt Focused (Browser.Dom.focus id)
None ->
Cmd.none
Command cmd ->
cmd
subscriptions : Model key -> Sub Msg
subscriptions model = subscriptions model =
Dict.values model.moduleStates Dict.values model.moduleStates
|> List.map (\example -> Sub.map (UpdateModuleStates example.name) (example.subscriptions example.state)) |> List.map (\example -> Sub.map (UpdateModuleStates example.name) (example.subscriptions example.state))
|> Sub.batch |> Sub.batch
view : Model -> Document Msg view : Model key -> Document Msg
view model = view model =
let let
findExampleByName name = findExampleByName name =
@ -196,7 +227,7 @@ view model =
} }
viewExample : Model -> Example a msg -> Html msg viewExample : Model key -> Example a msg -> Html msg
viewExample model example = viewExample model example =
Html.div [ css [ maxWidth (Css.px 1400), margin auto ] ] Html.div [ css [ maxWidth (Css.px 1400), margin auto ] ]
[ Example.view model.previousRoute [ Example.view model.previousRoute
@ -213,7 +244,7 @@ notFound =
} }
viewAll : Model -> Html Msg viewAll : Model key -> Html Msg
viewAll model = viewAll model =
withSideNav model.route withSideNav model.route
[ mainContentHeader "All" [ mainContentHeader "All"
@ -221,7 +252,7 @@ viewAll model =
] ]
viewCategory : Model -> Category -> Html Msg viewCategory : Model key -> Category -> Html Msg
viewCategory model category = viewCategory model category =
withSideNav model.route withSideNav model.route
[ mainContentHeader (Category.forDisplay category) [ mainContentHeader (Category.forDisplay category)

View File

@ -2,13 +2,26 @@ module Main exposing (main)
import App exposing (..) import App exposing (..)
import Browser import Browser
import Browser.Navigation as Navigation
main : Program () Model Msg main : Program () (Model Navigation.Key) Msg
main = main =
Browser.application Browser.application
{ init = init { init =
, update = update \() flags key ->
let
( model, effect ) =
init () flags key
in
( model, perform model.navigationKey effect )
, update =
\msg oldModel ->
let
( model, effect ) =
update msg oldModel
in
( model, perform model.navigationKey effect )
, subscriptions = subscriptions , subscriptions = subscriptions
, view = view , view = view
, onUrlRequest = OnUrlRequest , onUrlRequest = OnUrlRequest

View File

@ -44,8 +44,14 @@
}, },
"test-dependencies": { "test-dependencies": {
"direct": { "direct": {
"avh4/elm-program-test": "3.6.3",
"elm-explorations/test": "1.2.2" "elm-explorations/test": "1.2.2"
}, },
"indirect": {} "indirect": {
"avh4/elm-fifo": "1.0.4",
"elm-community/list-extra": "8.5.2",
"hecrj/html-parser": "2.4.0",
"mgold/elm-nonempty-list": "4.2.0"
}
} }
} }

View File

@ -0,0 +1,44 @@
module TestApp exposing (app)
import App exposing (..)
import ProgramTest exposing (SimulatedEffect, createApplication)
import Routes
import SimulatedEffect.Cmd
import SimulatedEffect.Navigation
import Url
app =
createApplication
{ init = init
, view = view
, update = update
, onUrlRequest = OnUrlRequest
, onUrlChange = OnUrlChange
}
|> ProgramTest.withSimulatedEffects simulateEffect
|> ProgramTest.start ()
simulateEffect : Effect -> SimulatedEffect Msg
simulateEffect effect =
case effect of
GoToRoute route ->
SimulatedEffect.Navigation.pushUrl (Routes.toString route)
GoToUrl url ->
SimulatedEffect.Navigation.pushUrl (Url.toString url)
Load loc ->
SimulatedEffect.Navigation.load loc
FocusOn id ->
-- TODO: mock an implementation
SimulatedEffect.Cmd.none
None ->
SimulatedEffect.Cmd.none
Command cmd ->
-- TODO: mock an implementation
SimulatedEffect.Cmd.none