brian-watkins / elm-spec / Spec

Functions for writing specs.

A spec is a collection of scenarios, each of which provides an example that illustrates a behavior belonging to your program. Each scenario follows the same basic plan:

  1. Configure the initial state of the world (and your program).
  2. Perform a sequence of steps.
  3. Validate your expectations about the new state of the world after the steps have been performed.

Here's a sample spec for a browser program called App:

Spec.describe "some part of my system"
[ Spec.scenario "the awesome path" (
    Spec.given (
      Spec.Setup.init (App.init testFlags)
        |> Spec.Setup.withView App.view
        |> Spec.Setup.withUpdate App.update
    )
    |> Spec.when "something happens"
      [ Spec.Markup.target << by [ id "some-button" ]
      , Spec.Markup.Event.click
      ]
    |> Spec.it "does the right thing" (
      Spec.Markup.observeElement
        |> Spec.Markup.query << by [ id "some-words" ]
        |> Spec.expect (
          Spec.Claim.isSomethingWhere <|
          Spec.Markup.text <|
          Spec.Claim.isStringContaining "something awesome"
        )
    )
  )
]

Creating a Spec


type alias Spec model msg =
Scenario.Internal.Spec model msg

Represents the spec.


type Scenario model msg

Represents a particular scenario in a spec.

describe : String -> List (Scenario model msg) -> Spec model msg

Specify a description and a list of scenarios that compose the spec.

scenario : String -> Plan model msg -> Scenario model msg

Create a scenario with a description and a plan.

Creating the Scenario Script


type Script model msg

Represents the setup and steps involved in a scenario.

given : Setup model msg -> Script model msg

Provide the Setup that represents the state of the world at the start of the scenario.

See Spec.Setup for functions to construct this representation.

when : String -> List (Step model msg) -> Script model msg -> Script model msg

Specify a description and the steps involved in a scenario.

Each step is a function from Spec.Step.Context to Spec.Step.Command, but usually you will use steps that are provided by other modules, like Spec.Markup.Event.

You may provide multiple when blocks as part of a scenario.

Turn a Script into a Plan


type Plan model msg

Represents the full plan (setup, steps, expectations) involved in a scenario.


type Expectation model

Represents what should be the case about some part of the world.

Expectations are checked at the end of the scenario, after all steps of the script have been performed.

it : String -> Expectation model -> Script model msg -> Plan model msg

Specify an expectation to be checked once the scenario's steps have been performed.

observeThat : List (Script model msg -> Plan model msg) -> Script model msg -> Plan model msg

Specify multiple expectations to be checked once the scenario's steps have been performed.

expect : Claim a -> Observer model a -> Expectation model

Provide an observer with a claim to evaluate.

Run Only Certain Scenarios

pick : (() -> Platform.Cmd.Cmd msg) -> Scenario model msg -> Scenario model msg

Pick this scenario to be executed when the spec suite runs.

When one or more scenarios are picked, only picked scenarios will be executed.

Note that the first argument to this function must be a port defined like so:

port elmSpecPick : () -> Cmd msg

I suggest adding a function to the main Runner file in your spec suite, where you've defined your Config and so on:

pick =
  Spec.pick elmSpecPick

Then, to pick a scenario to run, do something like this:

myFunSpec =
  Spec.describe "Some fun stuff"
  [ Runner.pick <| Spec.scenario "fun things happen" (
      ...
    )
  ]

Create a Spec Suite Program


type alias Config msg =
{ send : Message -> Platform.Cmd.Cmd (Msg msg)
, listen : (Message -> Msg msg) -> Platform.Sub.Sub (Msg msg) 
}

The spec suite runner must provide a Config, which must be implemented as follows:

Create two ports:

port elmSpecOut : Message -> Cmd msg
port elmSpecIn : (Message -> msg) -> Sub msg

And then create a Config like so:

config : Spec.Config msg
config =
  { send = elmSpecOut
  , listen = elmSpecIn
  }

browserProgram : Config msg -> List (Spec model msg) -> Platform.Program Flags (Model model msg) (Msg msg)

Create a spec suite program for describing the behavior of browser-based programs.

Once you've created the Config value, I suggest adding a function like so:

program : List (Spec model msg) -> Program Flags (Model model msg) (Msg msg)
program =
  Spec.browserProgram config

Then, each of your spec modules can implement their own main function:

main =
  Runner.program
    [ ... some specs ...
    ]

The elm-spec runner will find each spec module and run it as its own program.

program : Config msg -> List (Spec model msg) -> Platform.Program Flags (Model model msg) (Msg msg)

Create a spec suite program for describing the behavior of headless programs.

Once you've created the Config value, I suggest adding a function like so:

program : List (Spec model msg) -> Program Flags (Model model msg) (Msg msg)
program =
  Spec.program config

Then, each of your spec modules can implement their own main function:

main =
  Runner.program
    [ ... some specs ...
    ]

The elm-spec runner will find each spec module and run it as its own program.

Spec Suite Program Types


type alias Message =
Message

Represents a message to pass between elm-spec and the JavaScript elm-spec runner.


type alias Flags =
Program.Flags

Flags that the JavaScript runner will pass to the spec suite program.


type alias Msg msg =
Program.Msg msg

Used by the spec suite program.


type alias Model model msg =
Program.Model model msg

Used by the spec suite program.