Musubi.Testing (musubi v0.2.0)

Copy Markdown View Source

Test entry point for Musubi root stores, analogous to Phoenix.LiveViewTest. Wraps Musubi.Page.Server.start_link/1 with test-friendly defaults and exposes the primary assertion surface (render/2).

Primary surface

Assert against the rendered wire-shape map — the same contract a client observes — not internal socket.assigns. assigns/2 is an escape hatch for state not surfaced through render/1; prefer render/2 for contract assertions.

Example

page = Musubi.Testing.mount(RoomStore, %{"room_code" => "AB12"})
Musubi.Testing.dispatch_command(page, :ko, %{"target" => "p2"})

assert Musubi.Testing.render(page) == %{
  winner: :p1,
  hp: %{p1: 100, p2: 0}
}

Summary

Types

t()

Handle returned by mount/3; passed back into the other helpers.

Functions

Returns the raw socket.assigns for the addressed store.

Dispatches a command to a mounted store. Defaults to the root (store_id: []); pass a child path to address a nested store.

Mounts module as a root page. Push patches are delivered to the calling process; consume them with ExUnit.Assertions.assert_receive/2. Tears down on test exit via start_supervised!.

Runs the addressed store's render/1 against its current socket and returns the wire-shape map. Primary assertion surface — what the client would observe after the next reconcile.

Types

t()

@type t() :: %Musubi.Testing{pid: pid(), root: module(), transport: pid()}

Handle returned by mount/3; passed back into the other helpers.

Functions

assigns(testing, store_id \\ [])

@spec assigns(t(), Musubi.Socket.store_id()) :: map()

Returns the raw socket.assigns for the addressed store.

Escape hatch — prefer render/2 for contract assertions. Use only when the value you need is not exposed through render/1 (e.g. a private field captured for later use).

dispatch_command(testing, name, payload, store_id \\ [])

@spec dispatch_command(t(), atom(), map(), Musubi.Socket.store_id()) ::
  {:ok, map()} | {:error, term()}

Dispatches a command to a mounted store. Defaults to the root (store_id: []); pass a child path to address a nested store.

Mirrors the client-side proxy.dispatchCommand(name, payload) contract.

mount(module, params \\ %{}, opts \\ [])

@spec mount(module(), map(), keyword()) :: t()

Mounts module as a root page. Push patches are delivered to the calling process; consume them with ExUnit.Assertions.assert_receive/2. Tears down on test exit via start_supervised!.

Options

  • :transport_pid — pid that receives push patches. Defaults to self().

render(testing, store_id \\ [])

@spec render(t(), Musubi.Socket.store_id()) :: map()

Runs the addressed store's render/1 against its current socket and returns the wire-shape map. Primary assertion surface — what the client would observe after the next reconcile.

Values are returned as native Elixir terms (atom literals stay atoms); the JSON-string transformation happens on the way out to the client, not inside render/1.