thematthopkins / elm-test-journey / TestJourney

Write easy-to-maintain acceptance-like tests.

Setup

startSandbox : SandboxProgram model msg -> TestState model msg effect

Defines the application under test and starts the test pipeline. Simular to Browser.sandbox.


type alias SandboxProgram model msg =
{ view : model -> Html msg
, update : msg -> model -> model
, model : model 
}

For use in startSandbox.

effectToString should always be set to Debug.toString. Having the caller supply this allows elm-test-program to avoid it's use, and be published as a package.

startApplication : ApplicationProgram model msg effect -> TestState model msg effect

Defines the application under test and starts the test pipeline. Simular to Browser.application, but without subscriptions or init, and List effect instead of Cmd.


type alias ApplicationProgram model msg effect =
{ view : model -> Browser.Document msg
, update : msg -> model -> ( model
, List effect )
, model : model
, onUrlRequest : Browser.UrlRequest -> msg
, onUrlChange : Url -> msg
, effectToString : effect -> String 
}

For use in startApplication.

effectToString should always be set to Debug.toString. Having the caller supply this allows elm-test-program to avoid it's use, and be published as a package.

startElement : ElementProgram model msg effect -> TestState model msg effect

Defines the application under test and starts the test pipeline. Simular to Browser.element, but without subscriptions or init, and List effect instead of Cmd.

This is also useful if you want to want to limite the system under test to a subsection of your application, like a single page of an application, or maybe a single widget within your application.


type alias ElementProgram model msg effect =
{ view : model -> Html msg
, update : msg -> model -> ( model
, List effect )
, model : model
, effectToString : effect -> String 
}

For use in startElement.

effectToString should always be set to Debug.toString. Having the caller supply this allows elm-test-program to avoid it's use, and be published as a package.

startDocument : DocumentProgram model msg effect -> TestState model msg effect

Defines the application under test and starts the test pipeline. Simular to Browser.document, but without subscriptions or init, and List effect instead of Cmd.


type alias DocumentProgram model msg effect =
{ view : model -> Browser.Document msg
, update : msg -> model -> ( model
, List effect )
, model : model
, effectToString : effect -> String 
}

For use in startDocument.

effectToString should always be set to Debug.toString. Having the caller supply this allows elm-test-program to avoid it's use, and be published as a package.

startView : Html msg -> TestState model msg effect

Defines the application under test as a static Html.Html. Useful if you want to test a view function in isolation.


type TestState model msg effect

The state of the elm program under test, and its pending effects. Create using one of the start* functions.

finish : TestState model msg effect -> Expectation

Call at the end of your test. Automatically add an expectations that there are no pending effects left to process, since that is usually inadvertant in practice.

Direct model access

mapModel : (model -> model) -> TestState model msg effect -> TestState model msg effect

Directly transform the underlying model. This can be useful for altering your model supplied in ProgramDefinition. This should generally be avoided in the body of your tests.

expectModel : (model -> Expectation) -> TestState model msg effect -> TestState model msg effect

Directly run an expectation on the current model of the application under tests. Useful when there are changes that aren't represented in the UI (e.g. I want to make sure I get a new session id after logging in).

Effects

handleEffect : (effect -> EffectHandlerResult msg) -> TestState model msg effect -> TestState model msg effect

Processes the next effect waiting to be processed.

This is usually simulating the other end of your effect. In the case of an effect representing an HTTP Cmd, this takes place of the server. For an out port, this plays the role simulating the javascript code.

    -- Application Under Test
    type alias ItemID = Int

    type Msg =
        ItemAdded ItemID

    type MyEffect =
        EffectAddItem (Result Http.Error ItemID) String
        EffectRemoveItem ItemID


    -- Test
    handleAddItem effect =
        case effect of
            EffectAddItem msg label ->
                EffectProcessed
                    (Expect.equal
                        input
                        "myLabel"
                    )
                    (msg
                        (Ok ItemID 55))
                    )

            _ ->
                EffectUnexpected


type EffectHandlerResult msg
    = EffectProcessed Expectation msg
    | EffectSeen Expectation
    | EffectUnexpected

Used by handleEffect to create expectations around the next Effect, and optionally inject a message simulating the result of the effect.

To play the part of the server in an http request, use EffectProcessed:

    J.startDocument program
        |> J.click page.addItemButton
        |> J.handleEffect
            (\effect ->
                case effect of
                    TodoExample.EffectAddItem msg input ->
                        J.EffectProcessed
                            (Expect.equal
                                "myNewItem"
                                input
                            )
                            (msg
                                (Ok ())
                            )

                    _ ->
                        J.EffectUnexpected
            )
        |> J.finish

To perform expectations on an effect without generating an msg (useful for testing ports, which are fire-and-forget):

    J.startDocument program
        |> J.click page.addItemButton
        |> J.handleEffect
            (\effect ->
                case effect of
                    TodoExample.EffectPopJsAlert alertText ->
                        J.EffectSeen
                            (Expect.equal
                                "Item already exists"
                                alertText
                            )

                    _ ->
                        J.EffectUnexpected
            )
        |> J.finish

injectEffects : List effect -> TestState model msg effect -> TestState model msg effect

Queues up the supplied addedEffects into application under test, so that handleEffects can subsequently be used to process them. This is useful if you have effects created during setup of your application, and need to feed them back into your tests.

Messages

injectMsg : msg -> TestState model msg effect -> TestState model msg effect

Sends the supplied msg into appliication under test. This is how ports and Subscriptions should be simulated.

Html Events

blur : Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates a blur event, and processes the generated message.

check : Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates a check true event, and processes the generated message.

click : Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates a click event, and processes the generated message.

custom : String -> Json.Encode.Value -> Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates a custom, and processes the generated message.

                    See (see [Test.Html.Event.custom](https://package.elm-lang.org/packages/elm-explorations/test/latest/Test-Html-Event#custom))

doubleClick : Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates a double-click event, and processes the generated message.

focus : Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates a focus event, and processes the generated message.

input : String -> Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates a input event w/ the given text, and processes the generated message.

mouseDown : Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates a mouseDown event, and processes the generated message.

mouseEnter : Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates a mouseEnter event, and processes the generated message.

mouseLeave : Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates a mouseLeave event, and processes the generated message.

mouseOut : Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates a mouseOut event, and processes the generated message.

mouseOver : Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates a mouseOver event, and processes the generated message.

mouseUp : Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates a mouseUp event, and processes the generated message.

submit : Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates a submit event, and processes the generated message.

uncheck : Page.Element children -> TestState model msg effect -> TestState model msg effect

Ensures the target element exists exactly once, simulates an check false event, and processes the generated message.

Expectations

To be used with your page

see : Page.Element children -> TestState model msg effect -> TestState model msg effect

Expect a given element to exist exactly once.

seeCount : Basics.Int -> (Basics.Int -> Page.Element children) -> TestState model msg effect -> TestState model msg effect

Expect exactly a given number of matching elements to exist.

seeText : String -> Page.Element children -> TestState model msg effect -> TestState model msg effect

Expect text within itself or a descendant.

seeClass : String -> Page.Element children -> TestState model msg effect -> TestState model msg effect

Expect a class to exist on the element or a descendant.

seeAttribute : String -> String -> Page.Element children -> TestState model msg effect -> TestState model msg effect

Expect an attribute attribute to exist on the element or a descendant.

seeChecked : Page.Element children -> TestState model msg effect -> TestState model msg effect

Expect the element or a descendant to be checked.

seeUnchecked : Page.Element children -> TestState model msg effect -> TestState model msg effect

Expect the element or a descendant to be unchecked.

seeDisabled : Page.Element children -> TestState model msg effect -> TestState model msg effect

Expect a disabled attribute to exist on the element or a descendant.

seeNotDisabled : Page.Element children -> TestState model msg effect -> TestState model msg effect

Expect a disabled attribute be false on an element or a descendant.

seeHref : String -> Page.Element children -> TestState model msg effect -> TestState model msg effect

Expect an href attribute attribute to exist on the element or a descendant.

seeSrc : String -> Page.Element children -> TestState model msg effect -> TestState model msg effect

Expect a src attribute to exist on the element or a descendant.

seeValue : String -> Page.Element children -> TestState model msg effect -> TestState model msg effect

Expect a value attribute to exist on the element or a descendant.

dontSee : Page.Element children -> TestState model msg effect -> TestState model msg effect

Expect a given element to not exist. If the target is a sequence of page finders (e.g. parent.child.grandchild), dontSee only passes if parent, and child exist, but grandchild does not. Otherwise, it's too easy for dontSee to inadvertantly pass.

dontSeeClass : String -> Page.Element children -> TestState model msg effect -> TestState model msg effect

Expect a class to not exist on the element and it's descendants.

fail : String -> TestState model msg effect -> TestState model msg effect

Cause the test to fail. Useful in cases where we want to fail during model construction, before the test gets going.