brian-watkins / elm-spec / Spec.Port

Functions for working with ports during a spec.

Suppose your app sends a port command when a button is clicked, via a function called my-command-port that takes a string ("TRIGGER_REQUEST" in this case), and then displays a message received over a port subscription. You could write a scenario like so:

Spec.describe "command ports and subscription ports"
[ Spec.scenario "send and receive" (
    Spec.given (
      Spec.Setup.withInit (App.init testFlags)
        |> Spec.Setup.withUpdate App.update
        |> Spec.Setup.withView App.view
        |> Spec.Setup.withSubscriptions App.subscriptions
    )
    |> Spec.when "a message is sent out"
      [ Spec.Markup.target << by [ tag "button" ]
      , Spec.Markup.Event.click
      ]
    |> Spec.when "a response is received"
      [ Spec.Port.send "my-subscription-port" <|
          Json.Encode.object
            [ ("message", Encode.string "Have fun!")
            ]
      ]
    |> Spec.observeThat
      [ Spec.it "sent the right message over the port" (
          Spec.Port.observe "my-command-port" Json.Decode.string
            |> Spec.expect (Spec.Claim.isListWhere
              [ Spec.Claim.isEqual Debug.toString "TRIGGER_REQUEST"
              ]
            )
        )
      , Spec.it "shows the message received" (
          Spec.Markup.observeElement
            |> Spec.Markup.query << by [ id "message" ]
            |> Spec.expect (
              Spec.Claim.isSomethingWhere <|
              Spec.Markup.text <|
              Spec.Claim.isEqual Debug.toString "Have fun!"
            )
        )
      ]
  )
]

Observe Command Ports

observe : String -> Json.Decode.Decoder a -> Spec.Observer.Observer model (List a)

Observe messages sent out via a command port.

Provide the name of the port (the function name) and a JSON decoder that can decode messages sent out over the port.

Simulate Subscription Ports

send : String -> Json.Encode.Value -> Spec.Step.Step model msg

A step that sends a message to a port subscription.

Provide the name of the port and an encoded JSON value that should be sent from the JavaScript side.

For example, if you have a port defined in Elm like so:

port listenForStuff : (String -> msg) -> Sub msg

Then you could send a message through this port during a scenario like so:

Spec.when "a message is sent to the subscription"
[ Json.Encode.string "Some words"
    |> Spec.Port.send "listenForStuff"
]

Respond to a Command Port

respond : String -> Json.Decode.Decoder a -> (a -> Spec.Step.Step model msg) -> Spec.Step.Step model msg

Use this step to respond to values sent out over the given command port.

Provide the name of the port (the function name) and a JSON decoder that can decode values sent out over the port. Then, provide a function that generates a Step given a value sent out over the port. The function will be applied to values that have been sent since the last time respond was called. If no values have been sent out over that port since the last time respond was called, the step will fail.

Use this function to simulate request/response style communication via ports, when the response depends on some aspect of the request that you may not know at the time of writing the scenario.

Spec.when "a response is sent to the request"
[ Spec.Port.respond "myPortCommand" Json.string <| \message ->
    Json.Encode.string ("You sent: " ++ message)
      |> Spec.Port.send "myPortSubscription"
]