lustre/dev/simulate

Types

A simulated lustre.App ready to be started. This module exposes constructor functions that mirrors those provided by the main lustre module:

Note: running a simulated app is not the same as running a real app! Any effects that would normally be run after update will be discarded. If you want to simulate messages coming from the outside world, you can use the message or event functions.

pub opaque type App(args, model, msg)

The simulation keeps a log of events that occur. This includes both simulated DOM events and messages dispatched using the event and message functions but also entries for failures like an event target not existing in the current view or an event that was fired but not handled.

The event log is primarily useful for debugging simulations that don’t produce the results you expect. You can use the history function to introspect a simulation for this event log.

pub type Event(msg) {
  Dispatch(message: msg)
  Event(target: query.Query, name: String, data: json.Json)
  Problem(name: String, message: String)
}

Constructors

  • Dispatch(message: msg)
  • Event(target: query.Query, name: String, data: json.Json)
  • Problem(name: String, message: String)

A running simulation of a Lustre application, produced by calling start.This is similar to the Runtime type and both DOM events and messages dispatched by effects can be simulated using the event and message functions respectively.

Each simulated event returns an updated simulation, making it convenient to pipe multiple events in sequence.

pub opaque type Simulation(model, msg)

Values

pub fn application(
  init init: fn(args) -> #(model, effect.Effect(msg)),
  update update: fn(model, msg) -> #(model, effect.Effect(msg)),
  view view: fn(model) -> @internal Element(msg),
) -> App(args, model, msg)

Construct a simulated Lustre application. The simulation can be started with the start function by providing the initial arguments for your app’s init function.

DOM events and messages dispatched by effects can be simulated using the event and messgae functions.

Note: simulated apps do not run any effects! You can simulate the result of an effect by using the message function, but to test side effects you should test your application in a real environment.

pub fn click(
  simulation: Simulation(model, msg),
  on query: query.Query,
) -> Simulation(model, msg)

A convenience function that simulates a click event on the first element matching the given query. This event will have no payload and is only appropriate for event handlers that use Lustre’s on_click handler or custom handlers that do not decode the event payload.

pub fn event(
  simulation: Simulation(model, msg),
  on query: query.Query,
  name event: String,
  data payload: List(#(String, json.Json)),
) -> Simulation(model, msg)

Simulate a DOM event on the first element that matches the given query. The payload represents a simulated event object, and should be used to pass data you expect your event handlers to decode.

If no element matches the query, an EventTargetNotFound event is logged in the simulation history. If an element is found, but the application has no handler for the event, the EventHandlerNotFound event is logged instead.

Note: this is not a perfect simulation of a real DOM event. There is no capture phase of a simulated event and simulated events will not bubble up to parent elements.

pub fn history(
  simulation: Simulation(model, msg),
) -> List(Event(msg))

Receive the current Event log of a running simulation. You can use this to produce more detailed snapshots by also rendering the sequence of events that produced the given view.

In addition to simulated DOM events and message dispatch, the event log will also include entries for when the queried event target could not be found in the view and cases where an event was fired but not handled by your application.

pub fn input(
  simulation: Simulation(model, msg),
  on query: query.Query,
  value value: String,
) -> Simulation(model, msg)

Simulate an input event on the first element matching the given query. This helper has an event payload that looks like this:

{
  "target": {
    "value": value
  }
}

and is appropriate for event handlers that use Lustre’s on_input handler or custom handlers that only decode the event target value.

pub fn message(
  simulation: Simulation(model, msg),
  msg: msg,
) -> Simulation(model, msg)

Simulate a message sent directly to the runtime. This is often used to mimic the result of some effect you would have run in a real environment. For example, you might simulate a click event on a login button and then simulate the successful response from the server by calling this function with the message you would dispatch from the effect:

import birdie
import lustre/dev/simulate
import lustre/dev/query
import lustre/element

pub fn login_test() {
  let app = simulate.application(init:, update:, view:)
  let login_button = query.element(matching: query.id("login"))
  let user = User(name: "Lucy")

  simulate.start(app, Nil)
  |> simulate.event(on: login_button, name: "click", data: [])
  // Simulate a successful response from the server
  |> simulate.message(ApiReturnedUser(Ok(user)))
  |> simulate.view
  |> element.to_readable_string
  |> birdie.snap("Successful login")
}

Note: your app’s view function will probably be rendering quite a lot of HTML! To make your snapshots more meaningful, you might want to couple this with the query module to only snapshot parts of the page that are relevant to the test.

pub fn model(simulation: Simulation(model, msg)) -> model

Introspect the current model of a running simulation. This can be useful to debug why a simulation is not producing the view you expect.

pub fn problem(
  simulation: Simulation(model, msg),
  name name: String,
  message message: String,
) -> Simulation(model, msg)

Log a problem that occured during the simulation. This function is useful for external packages that want to provide functions to simulate certain effects that may fail in the real world. For example, a routing package may log a problem if a link has an invalid href attribute that would cause no message to be dispatched.

Note: logging a problem will not stop the simulation from running, just like a real application!

pub fn simple(
  init init: fn(args) -> model,
  update update: fn(model, msg) -> model,
  view view: fn(model) -> @internal Element(msg),
) -> App(args, model, msg)

Construct a simulated simple Lustre application. The simulation can be started with the start function by providing the initial arguments for your app’s init function.

DOM events and messages dispatched by effects can be simulated using the event and messgae functions.

pub fn start(
  app: App(args, model, msg),
  args: args,
) -> Simulation(model, msg)

Start a simulated Lustre application. Once a simulation is running you can use the message and event functions to simulate events

pub fn submit(
  simulation: Simulation(model, msg),
  on query: query.Query,
  fields form_data: List(#(String, String)),
) -> Simulation(model, msg)

Simulate a submit event on the first element matching the given query. The simulated event payload looks like this:

{
  "detail": {
    "formData": [
      ...
    ]
  }
}

and is appropriate for event handlers that use Lustre’s on_submit handler or custom handlers that only decode the non-standard detail.formData property.

pub fn view(
  simulation: Simulation(model, msg),
) -> @internal Element(msg)

Introspect the current view of a running simulation. Typically you would use this with a snapshot testing library like birdie and/or with the query api to make assertions about the state of the page.

Search Document