The sandbox module is helpful when developing components. It lets you
run the component using elm reactor
outside the system and define several test cases.
If you want to run your sandbox on a CI you can add #markdown to the URL. This will output the test results as a markdown string inside a pre element:
<pre id="markdown-output"> Results here </pre>
This way you can complile the sandbox, run it in a headless Chrome and dump the DOM. From the DOM you can extract the results.
Platform.Program () (Model model msgOut) (Msg msgIn msgOut)
The Program type of your main function.
Wrap a component in a sandbox application.
This will render each test case and log all messages.
Create a test file with a main
function where you declare all
test cases.
A sandbox module example for YourComponent
:
import YourComponent exposing (Model, MsgIn, MsgOut)
main : SandboxProgram Model MsgIn MsgOut
main =
Sandbox.ui
{ title = "Title of your component"
, component = YourComponent.component
, cases =
[ testCase1
, testCase2
]
, stringifyMsgIn = Debug.toString -- Or roll your own if you want prettier messages.
, stringifyMsgOut = Debug.toString
, wrapView = identity
}
ui : { title : String, component : Webbhuset.Component.UI model msgIn msgOut, cases : List (TestCase msgIn msgOut), stringifyMsgIn : msgIn -> String, stringifyMsgOut : msgOut -> String, wrapView : Html msgIn -> Html msgIn } -> SandboxProgram model msgIn msgOut
Sandbox a UI Component
layout : { title : String, component : Webbhuset.Component.Layout model msgIn msgOut (Msg msgIn msgOut), cases : List (TestCase msgIn msgOut), stringifyMsgIn : msgIn -> String, stringifyMsgOut : msgOut -> String, wrapView : (msgIn -> Msg msgIn msgOut) -> Html (Msg msgIn msgOut) -> Html (Msg msgIn msgOut) } -> SandboxProgram model msgIn msgOut
Sandbox a Layout Component
service : { title : String, component : Webbhuset.Component.Service model msgIn msgOut, cases : List (TestCase msgIn msgOut), view : model -> Html msgIn, stringifyMsgIn : msgIn -> String, stringifyMsgOut : msgOut -> String } -> SandboxProgram model msgIn msgOut
Sandbox a Service Component
You need to provied a view
function which renders the model of
your service component.
Test cases defines scenarios for the requirements of your component.
A Test Case is just a record with a title and description together with a list of Actions you want to perform on your sandboxed component. You can also map the component's out messages to actions to simulate the outside system.
{ title : String
, desc : String
, init : List (Action msgIn)
, onMsgOut : msgOut -> List (Action msgIn)
}
A test case for the Component
Layout.Action msgIn
An action to perform on your sandboxed component.
sendMsg : msgIn -> Action msgIn
Send a message to you sandboxed component
Sandbox.sendMsg YourComponent.SomeMessage
spawnChild : String -> (Webbhuset.Internal.PID.PID -> msgIn) -> Action msgIn
Spawn a child component and send the PID to your component.
You can provide a String which will be displayed when the child
component is rendered (using renderPID
in your layout component).
Sandbox.spawnChild "Hello child" YourComponent.ReceiveChild
delay : Basics.Float -> Action msgIn -> Action msgIn
Perform a delayed action on your sandboxed component. Delay in milliseconds.
Sandbox.sendMsg YourComponent.SomeMessage
|> Sandbox.delay 1000
Sometimes it is useful to test your expectations or requirements on a component.
You can express them using assertions. Assertions have three states: waiting, pass or fail. The state of a test case is visible in the sandbox UI.
In this example we expect that GoodMsg
is sent by the component within 1s.
testGoodMsg : Sandbox.TestCase MsgIn MsgOut
testGoodMsg =
{ title = "Good messages are good"
, desc = "`GoodMsg` must be sent within 1 second. No other messages are allowed."
, init =
[ Sandbox.timeout 1000
, Sandbox.sendMsg YourComponent.SomeInput
]
, onMsgOut = \msgOut ->
case msgOut of
YourComponent.GoodMsg ->
[ Sandbox.pass
]
YourComponent.BadMsg ->
[ Sandbox.fail "I don't like bad messages"
]
pass : Action msgIn
Flag test case as passed.
Sandbox.pass
fail : String -> Action msgIn
Flag test case as failed. You can supply a message explaining what went wrong.
Sandbox.fail "Didn't receive some important out msg"
timeout : Basics.Float -> Action msgIn
Set a timeout in milliseconds. This will cause the test to automatically fail after the timeout if the test havn't been flagged as passed by then.
Sandbox.timeout 1000
Sometimes it is useful to test if the order of your init messages would affect the test result. One way to do so is by permuting all possible orders and test them.
permuteInitOrder : TestCase msgIn msgOut -> List (TestCase msgIn msgOut)
Take one test case and permute all possible orders of init messages.
mockPID : String -> Webbhuset.Internal.PID.PID
Create a mock PID for testing purposes.
mockPID "form-component"
checkPID : String -> Webbhuset.Internal.PID.PID -> Basics.Bool
Check that a mock pid matches an expected label.
This will return True
mockPID "form-component"
|> checkPID "form-component"
assertPID : String -> Webbhuset.Internal.PID.PID -> Action msgIn
Assert that a PID matches a label.
This will result in action pass
mockPID "form-component"
|> assertPID "form-component"
This will result in the action
fail "PID form-component does not match expectation other-component"
mockPID "form-component"
|> assertPID "other-component"
Webbhuset.ActorSystem.SysMsg ActorName (AppMsg msgIn msgOut)
Sadbox Msg