TermUI.Test.ComponentHarness (TermUI v0.2.0)

View Source

Test harness for isolated component testing.

Mounts a component in isolation with a test renderer, allowing event simulation and state/render inspection.

Usage

# Mount component
{:ok, harness} = ComponentHarness.mount_test(MyButton, label: "Click me")

# Render
harness = ComponentHarness.render(harness)

# Send events
harness = ComponentHarness.send_event(harness, Event.key(:enter))

# Inspect state and render
state = ComponentHarness.get_state(harness)
renderer = ComponentHarness.get_renderer(harness)

# Cleanup
ComponentHarness.unmount(harness)

Component Interface

Components must implement these callbacks:

  • init/1 - Initialize state from props
  • render/1 - Render component to nodes
  • handle_event/2 (optional) - Handle events

Example Component

defmodule Counter do
  def init(props) do
    %{count: Keyword.get(props, :initial, 0)}
  end

  def render(state) do
    text("Count: #{state.count}")
  end

  def handle_event(%Event.Key{key: :up}, state) do
    {:noreply, %{state | count: state.count + 1}}
  end

  def handle_event(_event, state) do
    {:noreply, state}
  end
end

Summary

Functions

Simulates an event cycle: send event -> render -> check.

Gets the render area dimensions.

Gets all events sent (most recent first).

Gets the most recent render result.

Gets the test renderer for inspection.

Gets all render results (most recent first).

Gets the current component state.

Gets state value at path.

Mounts a component in isolation for testing.

Renders the component to the test renderer.

Simulates a render cycle: render -> wait -> check.

Resets the harness to initial state.

Sends an event to the component.

Sends multiple events in sequence.

Sets component state directly.

Checks if state has changed since last render.

Unmounts the component and cleans up resources.

Updates component state directly (for testing edge cases).

Types

t()

@type t() :: %TermUI.Test.ComponentHarness{
  area: map(),
  events: [term()],
  module: module(),
  props: keyword(),
  renderer: TermUI.Test.TestRenderer.t(),
  renders: [term()],
  state: term()
}

Functions

event_cycle(harness, event)

@spec event_cycle(t(), term()) :: t()

Simulates an event cycle: send event -> render -> check.

get_area(component_harness)

@spec get_area(t()) :: map()

Gets the render area dimensions.

get_events(component_harness)

@spec get_events(t()) :: [term()]

Gets all events sent (most recent first).

get_render(component_harness)

@spec get_render(t()) :: term() | nil

Gets the most recent render result.

get_renderer(component_harness)

@spec get_renderer(t()) :: TermUI.Test.TestRenderer.t()

Gets the test renderer for inspection.

get_renders(component_harness)

@spec get_renders(t()) :: [term()]

Gets all render results (most recent first).

get_state(component_harness)

@spec get_state(t()) :: term()

Gets the current component state.

get_state_at(component_harness, path)

@spec get_state_at(t(), [atom() | String.t()]) :: term()

Gets state value at path.

mount_test(module, opts \\ [])

@spec mount_test(
  module(),
  keyword()
) :: {:ok, t()} | {:error, term()}

Mounts a component in isolation for testing.

Options

  • :width - Renderer width (default: 80)
  • :height - Renderer height (default: 24)
  • :props - Initial props to pass to component

Examples

{:ok, harness} = ComponentHarness.mount_test(MyButton, label: "Click")
{:ok, harness} = ComponentHarness.mount_test(MyWidget, width: 40, height: 10)

render(harness)

@spec render(t()) :: t()

Renders the component to the test renderer.

Returns the updated harness with render result stored.

render_cycle(harness)

@spec render_cycle(t()) :: t()

Simulates a render cycle: render -> wait -> check.

Renders the component and returns the harness for assertions.

reset(harness)

@spec reset(t()) :: {:ok, t()} | {:error, term()}

Resets the harness to initial state.

send_event(harness, event)

@spec send_event(t(), term()) :: t()

Sends an event to the component.

Returns the updated harness with new state.

send_events(harness, events)

@spec send_events(t(), [term()]) :: t()

Sends multiple events in sequence.

set_state(harness, new_state)

@spec set_state(t(), term()) :: t()

Sets component state directly.

state_changed?(harness)

@spec state_changed?(t()) :: boolean()

Checks if state has changed since last render.

unmount(component_harness)

@spec unmount(t()) :: :ok

Unmounts the component and cleans up resources.

update_state(harness, fun)

@spec update_state(t(), (term() -> term())) :: t()

Updates component state directly (for testing edge cases).

Use sparingly - prefer sending events for realistic testing.