lamdera/program-test/3.0.0

NOTE: the latest version is 4.0.0

A Lamdera package. It is not on package.elm-lang.org: the Lamdera compiler provides it, from static.lamdera.com, so lamdera install works and elm install does not.

Effect.Test

Setting up end to end tests

String
-> Posix
-> Config
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
-> List
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel

Start a end-to-end test

import Backend
import Effect.Test
import Frontend
import Test exposing (Test)
import Url


config =
    { frontendApp = Frontend.appFunctions
    , backendApp = Backend.appFunctions
    , handleHttpRequest = always Effect.Test.NetworkErrorResponse
    , handlePortToJs = always Nothing
    , handleFileUpload = always Effect.Test.UnhandledFileUpload
    , handleMultipleFilesUpload = always Effect.Test.UnhandledMultiFileUpload
    , domain = unsafeUrl "https://my-app.lamdera.app"
    }


test : Test
test =
    Effect.Test.start "myButton is clickable"
        |> Effect.Test.connectFrontend
            sessionId0
            myDomain
            { width = 1920, height = 1080 }
            (\( state, frontendActions ) ->
                state
                    |> frontendActions.click { htmlId = "myButton" }
            )
        |> Effect.Test.toTest


unsafeUrl : String -> Url
unsafeUrl urlText =
    case Url.fromString urlText of
        Just url ->
            url


        Nothing ->
            unsafeUrl urlText
String
-> List
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel

Group together multiple end to end tests so they are easier to find.

import Effect.Test


Effect.Test.testGroup
    "Login tests"
    [ Effect.Test.start "Login via homepage" ...
    , Effect.Test.start "Login via promotional link" ...
    ]
type alias Config toBackend frontendMsg frontendModel toFrontend backendMsg backendModel =
{ frontendApp :
FrontendApp toBackend frontendMsg frontendModel toFrontend
, backendApp :
BackendApp toBackend toFrontend backendMsg backendModel
, handleHttpRequest :
{ data : Data frontendModel backendModel
, currentRequest : HttpRequest
}
, handlePortToJs :
{ data : Data frontendModel backendModel
, currentRequest : PortToJs
}
-> Maybe ( String, Value )
, handleFileUpload :
{ data : Data frontendModel backendModel
, mimeTypes : List String
}
, handleMultipleFilesUpload :
{ data : Data frontendModel backendModel
, mimeTypes : List String
}
, domain : Url
}

Configure the end to end test before starting it

import Backend
import Effect.Test
import Frontend
import Test exposing (Test)
import Url


config =
    { frontendApp = Frontend.appFunctions
    , backendApp = Backend.appFunctions
    , handleHttpRequest = always Effect.Test.UnhandledHttpRequest
    , handlePortToJs = always Nothing
    , handleFileUpload = always Effect.Test.UnhandledFileUpload
    , handleMultipleFilesUpload = always Effect.Test.UnhandledMultiFileUpload
    , domain = unsafeUrl "https://my-app.lamdera.app"
    }


test : Test
test =
    Effect.Test.start "myButton is clickable"
        |> Effect.Test.connectFrontend
            sessionId0
            myDomain
            { width = 1920, height = 1080 }
            (\( state, frontendActions ) ->
                state
                    |> frontendActions.click { htmlId = "myButton" }
            )
        |> Effect.Test.toTest


unsafeUrl : String -> Url
unsafeUrl urlText =
    case Url.fromString urlText of
        Just url ->
            url


        Nothing ->
            unsafeUrl urlText
DelayInMs
-> String
-> { width : Int
, height : Int
}
-> ( FrontendActions
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
-> List
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)
)
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel

Add a frontend client to the end to end test

import Effect.Test
import Test exposing (Test)
import Url


testApp =
    Effect.Test.testApp
        Frontend.appFunctions
        Backend.appFunctions
        (always NetworkError_)
        (always Nothing)
        (always Nothing)
        (unsafeUrl "https://my-app.lamdera.app")


test : Test
test =
    testApp "myButton is clickable"
        |> Effect.Test.connectFrontend
            sessionId0
            "/"
            { width = 1920, height = 1080 }
            (\( state, frontendActions ) ->
                state
                    |> frontendActions.click { htmlId = "myButton" }
            )
        |> Effect.Test.toTest


unsafeUrl : String -> Url
unsafeUrl urlText =
    case Url.fromString urlText of
        Just url ->
            url


        Nothing ->
            unsafeUrl urlText
type alias FrontendApp toBackend frontendMsg frontendModel toFrontend =
{ init :
Url
-> Key
-> ( frontendModel, Command FrontendOnly toBackend frontendMsg )
, onUrlRequest : UrlRequest -> frontendMsg
, onUrlChange : Url -> frontendMsg
, update :
frontendMsg
-> frontendModel
-> ( frontendModel, Command FrontendOnly toBackend frontendMsg )
, updateFromBackend :
toFrontend
-> frontendModel
-> ( frontendModel, Command FrontendOnly toBackend frontendMsg )
, view : frontendModel -> Document frontendMsg
, subscriptions :
frontendModel -> Subscription FrontendOnly frontendMsg
}
type alias BackendApp toBackend toFrontend backendMsg backendModel =
{ init :
( backendModel, Command BackendOnly toFrontend backendMsg )
, update :
backendMsg
-> backendModel
-> ( backendModel, Command BackendOnly toFrontend backendMsg )
, updateFromFrontend :
-> toBackend
-> backendModel
-> ( backendModel, Command BackendOnly toFrontend backendMsg )
, subscriptions :
backendModel -> Subscription BackendOnly backendMsg
}
type alias HttpRequest =
{ requestedBy : RequestedBy
, method : String
, url : String
, body : HttpBody
, headers : List ( String, String )
, sentAt : Posix
, tracker : Maybe String
}
= BadUrlResponse String
| TimeoutResponse
| NetworkErrorResponse
| BadStatusResponse Metadata String
| BytesHttpResponse Metadata Bytes
| StringHttpResponse Metadata String
| JsonHttpResponse Metadata Value
| TextureHttpResponse Metadata Texture
| UnhandledHttpRequest

The response for an http request. Note that if the http request was expecting one form of data (for example json) and the response contains a different type of data (for example a String) then the data will automatically be converted. The exception to this is Texture. If the request is expecting a Texture (this will only happen with Effect.WebGL.Texture.load and Effect.WebGL.Texture.loadWith) then the response has to contain a Texture if you want the request to succeed. In other words, sending the Bytes that represent that texture won't work.

= RequestedByFrontend ClientId
| RequestedByBackend

Who made this http request?

type alias PortToJs =
{ clientId : ClientId
, portName : String
, value : Value
}
type alias PortToJsBytes =
{ clientId : ClientId
, portName : String
, value : Bytes
}

What a client sent over a port that carries Bytes rather than JSON.

File data for when simulating a user uploading a file via Effect.File.Select.file or Effect.File.Select.files

= UploadFile FileData
| UnhandledFileUpload

Possible simulated user actions for when Effect.File.Select.file is triggered.

= UploadMultipleFiles FileData (List FileData)
| UnhandledMultiFileUpload

Possible simulated user actions for when Effect.File.Select.files is triggered.

uploadBytesFile : String -> String -> Bytes -> Posix -> FileData

Create a file upload containing binary data

uploadStringFile : String -> String -> String -> Posix -> FileData

Create a file upload containing text data

type alias Data frontendModel backendModel =
{ httpRequests : List HttpRequest
, websockets : SeqDict ( RequestedBy, Connection ) WebsocketState
, portRequests : List PortToJs
, portBytesRequests : List PortToJsBytes
, fileUploads :
List
{ uploadedAt : Posix
, uploadedBy : ClientId
, upload : FileUpload
}
, multipleFileUploads :
List
{ uploadedAt : Posix
, uploadedBy : ClientId
}
, time : Posix
, backend : backendModel
, frontends : SeqDict ClientId frontendModel
, downloads :
List
{ filename : String
, mimeType : String
, content : FileContents
, downloadedAt : Posix
}
}
= StringFile String
| BytesFile Bytes

Control the tests

type alias FrontendActions toBackend frontendMsg frontendModel toFrontend backendMsg backendModel =
{ clientId : ClientId
, keyDown :
DelayInMs
-> HtmlId
-> KeyEvent
-> List KeyOptions
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, keyUp :
DelayInMs
-> HtmlId
-> KeyEvent
-> List KeyOptions
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, pointerDown :
DelayInMs
-> HtmlId
-> PointerEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, pointerUp :
DelayInMs
-> HtmlId
-> PointerEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, pointerEnter :
DelayInMs
-> HtmlId
-> PointerEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, pointerOut :
DelayInMs
-> HtmlId
-> PointerEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, pointerCancel :
DelayInMs
-> HtmlId
-> PointerEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, pointerMove :
DelayInMs
-> HtmlId
-> PointerEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, pointerLeave :
DelayInMs
-> HtmlId
-> PointerEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, pointerOver :
DelayInMs
-> HtmlId
-> PointerEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, touchStart :
DelayInMs
-> HtmlId
-> TouchEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, touchMove :
DelayInMs
-> HtmlId
-> TouchEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, touchEnd :
DelayInMs
-> HtmlId
-> TouchEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, touchCancel :
DelayInMs
-> HtmlId
-> TouchEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, mouseDown :
DelayInMs
-> HtmlId
-> MouseEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, mouseUp :
DelayInMs
-> HtmlId
-> MouseEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, mouseEnter :
DelayInMs
-> HtmlId
-> MouseEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, mouseOut :
DelayInMs
-> HtmlId
-> MouseEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, mouseMove :
DelayInMs
-> HtmlId
-> MouseEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, mouseLeave :
DelayInMs
-> HtmlId
-> MouseEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, mouseOver :
DelayInMs
-> HtmlId
-> MouseEvent
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, wheel :
DelayInMs
-> HtmlId
-> Float
-> ( Float, Float )
-> List WheelOptions
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, focus :
DelayInMs
-> HtmlId
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, blur :
DelayInMs
-> HtmlId
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, click :
DelayInMs
-> HtmlId
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, input :
DelayInMs
-> HtmlId
-> String
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, clickLink :
DelayInMs
-> String
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, resizeWindow :
DelayInMs
-> { width : Int
, height : Int
}
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, checkView :
DelayInMs
-> (Single frontendMsg -> Expectation)
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, update :
DelayInMs
-> frontendMsg
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, sendToBackend :
DelayInMs
-> toBackend
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, snapshotView :
DelayInMs
-> { name : String }
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, checkModel :
DelayInMs
-> (frontendModel -> Result String ())
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, custom :
DelayInMs
-> HtmlId
-> String
-> Value
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, portEvent :
DelayInMs
-> String
-> Value
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, portEventBytes :
DelayInMs
-> String
-> Bytes
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, navigateBack :
DelayInMs
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, navigateForward :
DelayInMs
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, setNetworkLatency :
DelayInMs
-> Latency
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, websocketSendString :
DelayInMs
-> String
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
, websocketClose :
DelayInMs
-> String
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
}

FrontendActions contains the possible functions we can call on the client we just connected.

import Effect.Test
import Test exposing (Test)
import Url


config =
    { frontendApp = Frontend.appFunctions
    , backendApp = Backend.appFunctions
    , handleHttpRequest = always Effect.Test.UnhandledHttpRequest
    , handlePortToJs = always Nothing
    , handleFileUpload = always Effect.Test.UnhandledFileUpload
    , handleMultipleFilesUpload = always Effect.Test.UnhandledMultiFileUpload
    , domain = unsafeUrl "https://my-app.lamdera.app"
    }


test : Test
test =
    Effect.Test.start "myButton is clickable"
        |> Effect.Test.connectFrontend
            sessionId0
            myDomain
            { width = 1920, height = 1080 }
            (\( state, frontendActions ) ->
                -- frontendActions is a record we can use on this specific frontend we just connected
                state
                    |> frontendActions.click { htmlId = "myButton" }
            )
        |> Effect.Test.toTest


unsafeUrl : String -> Url
unsafeUrl urlText =
    case Url.fromString urlText of
        Just url ->
            url


        Nothing ->
            unsafeUrl urlText
DelayInMs
-> backendMsg
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel

Directly trigger an update on the backend. Normally you shouldn't need to do this as things like Time.every and completed tasks will generate updates automatically.

Duration
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel

Similar to wait but this will not trigger any Browser.onAnimationFrame or Time.every subscriptions.

This is useful if you need to move the clock forward a week and it would take too long to simulate it perfectly.

List
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
String
-> List
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel

All actions and events that happen within this group can be minimized in the UI. Useful for organizing different parts of your end to end test.

DelayInMs
-> ( Data frontendModel backendModel
-> List
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)
)
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel

Sometimes you need to decide what should happen next based on some current state. To do that you can write something like this:

import Effect.Test


Effect.Test.andThen
    100 -- Simulate 100 milliseconds before performing this action
    (\data ->
        case List.filterMap isLoginEmail data.httpRequests |> List.head of
            Just loginEmail ->
                [ Effect.Test.connectFrontend
                    100
                    sessionIdFromEmail
                    "/user-overview"
                    (\clientIdFromEmail ->
                        [ ... ]
                    )
                ]


            Nothing ->
                [ Effect.Test.checkState (\_ -> Err "Should have gotten a login email") ]
    )
DelayInMs
-> String
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel

Send data to a websocket listener on the backend.

DelayInMs
-> String
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel

Simulate the server closing a websocket that the backend opened, delivering the close event (with the given reason) to the backend's websocket onClose listeners. This is the close-event counterpart to websocketSendString, which delivers to the onData listeners. Use it to test how the backend reacts to an unexpected websocket close, such as an authentication failure.

type alias WebsocketState =
{ createdAt : Posix
, closedAt : Maybe Posix
, dataSent :
Array
{ data : String
, sentAt : Posix
}
}
type EndToEndTest toBackend frontendMsg frontendModel toFrontend backendMsg backendModel
type Action toBackend frontendMsg frontendModel toFrontend backendMsg backendModel
= EmptyBody
| StringBody
{ contentType : String
, content : String
}
| JsonBody Value
| MultipartBody (List HttpPart)
| BytesBody String Bytes
| FileBody File
= StringPart String String
| FilePart String File
| BytesPart
{ key : String
, mimeType : String
, content : Bytes
}
type alias DelayInMs =
Float

How many milliseconds to delay something by

type alias KeyEvent =
String
= Key_ShiftHeld
| Key_CtrlHeld
| Key_MetaHeld
| Key_AltHeld
type alias PointerEvent =
( Float, Float )
= PointerId Int
| ScreenXY Float Float
| PageXY Float Float
| ClientXY Float Float
| ShiftHeld
| CtrlHeld
| MetaHeld
| AltHeld
| PointerType String
| IsNotPrimary
| PointerWidth Float
| PointerHeight Float
| PointerPressure Float
| PointerTilt Float Float
| PointerButton Button

Check the current state

DelayInMs
-> (Data frontendModel backendModel -> Result String ())
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel

Make the test fail if it doesn't seem some condition.

DelayInMs
-> (backendModel -> Result String ())
-> Action
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel

Make the test fail if the backend model doesn't seem some condition.

toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
-> Test
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
-> List (Snapshot frontendMsg)

Get all snapshots from a test. This can be used with Effect.Snapshot.uploadSnapshots to perform visual regression testing.

Test viewer

Sometimes it's hard to tell what's going on in an end to end test. One way to make this easier to use the viewer function. It's like a test runner for your browser that also lets you see the frontend of an app as simulated inputs are being triggered.

Only use this for tests!

List
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)
-> Program
()
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)
(Msg
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)

View your end-to-end tests in a elm reactor style app.

    main =
        viewer [{- End to end tests go here -}]
type Msg toBackend frontendMsg frontendModel toFrontend backendMsg backendModel
type Model toBackend frontendMsg frontendModel toFrontend backendMsg backendModel
viewerWith : a -> ViewerWith a

View your end-to-end tests in a elm reactor style app.

import Effect.Test


main =
    Effect.Test.viewerWith
        (\image jsonData ->
            [{- End to end tests go here -}]
        )
        |> Effect.Test.addBytesFile "/test.png"
        |> Effect.Test.addBytesFile "/data.json"
        |> Effect.Test.startViewer
type alias ViewerWith a =
{ cmds : Task FileLoadError a }
ViewerWith
(List
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)
)
-> Program
()
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)
(Msg
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)

View your end-to-end tests in a elm reactor style app.

import Effect.Test


main =
    Effect.Test.viewerWith
        (\image jsonData ->
            [{- End to end tests go here -}]
        )
        |> Effect.Test.addBytesFile "/test.png"
        |> Effect.Test.addBytesFile "/data.json"
        |> Effect.Test.startViewer
addStringFile : String -> ViewerWith (String -> b) -> ViewerWith b

Add a file containing text data to your tests.

import Effect.Test


main =
    Effect.Test.viewerWith
        (\text jsonData ->
            [{- End to end tests go here -}]
        )
        |> Effect.Test.addStringFile "/test.txt"
        |> Effect.Test.addStringFile "/data.json"
        |> Effect.Test.startViewer
addStringFiles : List String -> ViewerWith (Dict String String -> b) -> ViewerWith b

Add multiple files containing text data to your tests.

import Effect.Test


main =
    Effect.Test.viewerWith
        (\dict ->
            [{- End to end tests go here -}]
        )
        |> Effect.Test.addStringFiles [ "/test.txt", "/data.json" ]
        |> Effect.Test.startViewer
addBytesFile : String -> ViewerWith (Bytes -> b) -> ViewerWith b

Add a file containing binary data to your tests.

import Effect.Test


main =
    Effect.Test.viewerWith
        (\image jsonData ->
            [{- End to end tests go here -}]
        )
        |> Effect.Test.addBytesFile "/test.png"
        |> Effect.Test.addBytesFile "/data.json"
        |> Effect.Test.startViewer
addBytesFiles : List String -> ViewerWith (Dict String Bytes -> b) -> ViewerWith b

Add multiple files containing binary data to your tests.

import Effect.Test


main =
    Effect.Test.viewerWith
        (\dict ->
            [{- End to end tests go here -}]
        )
        |> Effect.Test.addBytesFiles [ "/test.png", "/data.json" ]
        |> Effect.Test.startViewer
addTexture : String -> ViewerWith (Texture -> b) -> ViewerWith b

Add a file containing data for a Effect.WebGL.Texture.Texture to your tests. Right now this is performed with HTTP get requests which means you can only access files in /public (or make get requests to other websites but this isn't recommended)

import Effect.Test


main =
    Effect.Test.viewerWith
        (\texture ->
            [{- End to end tests go here -}]
        )
        |> Effect.Test.addTextureFile "/texture.png"
        |> Effect.Test.startViewer
addTextureWithOptions : Options -> String -> ViewerWith (Texture -> b) -> ViewerWith b

Add a file containing data for a Effect.WebGL.Texture.Texture to your tests. Right now this is performed with HTTP get requests which means you can only access files in /public (or make get requests to other websites but this isn't recommended)

import Effect.Test


main =
    Effect.Test.viewerWith
        (\texture ->
            [{- End to end tests go here -}]
        )
        |> Effect.Test.addTextureFileWithOptions
            -- WebGL texture options go here
            "/texture.png"
        |> Effect.Test.startViewer
addTextures : List String -> ViewerWith (Dict String Texture -> b) -> ViewerWith b

Add a file containing data for a Effect.WebGL.Texture.Texture to your tests. Right now this is performed with HTTP get requests which means you can only access files in /public (or make get requests to other websites but this isn't recommended)

import Effect.Test


main =
    Effect.Test.viewerWith
        (\dict ->
            [{- End to end tests go here -}]
        )
        |> Effect.Test.addTextures [ "/cat.png", "/dog.jpg" ]
        |> Effect.Test.startViewer
Options
-> List String
-> ViewerWith (Dict String Texture -> b)
-> ViewerWith b

Add a file containing data for a Effect.WebGL.Texture.Texture to your tests. Right now this is performed with HTTP get requests which means you can only access files in /public (or make get requests to other websites but this isn't recommended)

import Effect.Test


main =
    Effect.Test.viewerWith
        (\dict ->
            [{- End to end tests go here -}]
        )
        |> Effect.Test.addTexturesWithOptions Effect.WebGL.Texture.defaultOptions [ "/cat.png", "/dog.jpg" ]
        |> Effect.Test.startViewer

Test runner

If you want to just run the end to end tests to make sure they work, or automatically check that they pass in your CI pipeline then you can setup a headless test runner with this function:

( Value
-> Cmd
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)
)
-> ViewerWith
(List
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)
)
-> Program
()
()
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)

Create a headless test runner.

import Effect.Test


main =
    Effect.Test.viewerWith
        (\image jsonData ->
            [{- End to end tests go here -}]
        )
        |> Effect.Test.addBytesFile "/test.png"
        |> Effect.Test.addBytesFile "/data.json"
        |> Effect.Test.startHeadless
type HeadlessMsg toBackend frontendMsg frontendModel toFrontend backendMsg backendModel

Msg type for a headless end to end test runner.

ViewerWith
(List
toBackend
frontendMsg
frontendModel
toFrontend
backendMsg
backendModel
)
)
-> Task FileLoadError (List ( String, () -> Result String () ))

Loads necessary files and returns a list of tests to run. Useful if you want to create your own test runner.

Types

type Button
= MainButton
| MiddleButton
| SecondButton
| BackButton
| ForwardButton
= DeltaLines
| DeltaPages

https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaMode. The default is DeltaPixels so it's not included here

type alias EventFrontend frontendModel =
{ model : frontendModel
, sessionId : SessionId
, url : Url
, windowSize :
{ width : Int
, height : Int
}
}
type EventType toBackend frontendMsg frontendModel toFrontend backendMsg backendModel
type alias FileLoadError =
{ name : String
}
= HttpError Error
| TextureError Error
type alias MouseEvent =
( Float, Float )
type alias Touch =
{ id : Int
, clientPos : ( Float, Float )
, pagePos : ( Float, Float )
, screenPos : ( Float, Float )
}
type alias TouchEvent =
{ changedTouches : List Touch
, targetTouches : List Touch
}
type alias Latency =
{ toBackendLatency : DelayInMs
, toFrontendLatency : DelayInMs
}

Setting up end-to-end tests for non-Lamdera apps

{ flags : flags
, frontendApp :
{ init :
flags
-> Url
-> Key
-> ( frontendModel, Command FrontendOnly Never frontendMsg )
, view : frontendModel -> Document frontendMsg
, update :
frontendMsg
-> frontendModel
-> ( frontendModel, Command FrontendOnly Never frontendMsg )
, subscriptions :
frontendModel -> Subscription FrontendOnly frontendMsg
, onUrlRequest : UrlRequest -> frontendMsg
, onUrlChange : Url -> frontendMsg
}
, handleHttpRequest :
{ data : Data frontendModel ()
, currentRequest : HttpRequest
}
, handlePortToJs :
{ data : Data frontendModel ()
, currentRequest : PortToJs
}
-> Maybe ( String, Value )
, handleFileUpload :
{ data : Data frontendModel ()
, mimeTypes : List String
}
, handleMultipleFilesUpload :
{ data : Data frontendModel ()
, mimeTypes : List String
}
, domain : Url
}
-> Config Never frontendMsg frontendModel Never Never ()

Test config for a Browser.application app. See the examples folder for how to setup end-to-end tests for your app.

{ flags : flags
, noOpMsg : frontendMsg
, frontendApp :
{ init :
flags
-> ( frontendModel, Command FrontendOnly Never frontendMsg )
, view : frontendModel -> Document frontendMsg
, update :
frontendMsg
-> frontendModel
-> ( frontendModel, Command FrontendOnly Never frontendMsg )
, subscriptions :
frontendModel -> Subscription FrontendOnly frontendMsg
}
, handleHttpRequest :
{ data : Data frontendModel ()
, currentRequest : HttpRequest
}
, handlePortToJs :
{ data : Data frontendModel ()
, currentRequest : PortToJs
}
-> Maybe ( String, Value )
, handleFileUpload :
{ data : Data frontendModel ()
, mimeTypes : List String
}
, handleMultipleFilesUpload :
{ data : Data frontendModel ()
, mimeTypes : List String
}
, domain : Url
}
-> Config Never frontendMsg frontendModel Never Never ()

Test config for a Browser.document app. See the examples folder for how to setup end-to-end tests for your app.

{ flags : flags
, noOpMsg : frontendMsg
, frontendApp :
{ init :
flags
-> ( frontendModel, Command FrontendOnly Never frontendMsg )
, view : frontendModel -> Html frontendMsg
, update :
frontendMsg
-> frontendModel
-> ( frontendModel, Command FrontendOnly Never frontendMsg )
, subscriptions :
frontendModel -> Subscription FrontendOnly frontendMsg
}
, handleHttpRequest :
{ data : Data frontendModel ()
, currentRequest : HttpRequest
}
, handlePortToJs :
{ data : Data frontendModel ()
, currentRequest : PortToJs
}
-> Maybe ( String, Value )
, handleFileUpload :
{ data : Data frontendModel ()
, mimeTypes : List String
}
, handleMultipleFilesUpload :
{ data : Data frontendModel ()
, mimeTypes : List String
}
, domain : Url
}
-> Config Never frontendMsg frontendModel Never Never ()

Test config for a Browser.element app. See the examples folder for how to setup end-to-end tests for your app.

{ noOpMsg : frontendMsg
, frontendApp :
{ init : frontendModel
, view : frontendModel -> Html frontendMsg
, update : frontendMsg -> frontendModel -> frontendModel
}
, handleHttpRequest :
{ data : Data frontendModel ()
, currentRequest : HttpRequest
}
, handlePortToJs :
{ data : Data frontendModel ()
, currentRequest : PortToJs
}
-> Maybe ( String, Value )
, handleFileUpload :
{ data : Data frontendModel ()
, mimeTypes : List String
}
, handleMultipleFilesUpload :
{ data : Data frontendModel ()
, mimeTypes : List String
}
, domain : Url
}
-> Config Never frontendMsg frontendModel Never Never ()

Test config for a Browser.sandbox app. See the examples folder for how to setup end-to-end tests for your app.