Search documentation of Plushie

# Plushie.Test.Helpers (Plushie v0.7.2)

Copy Markdown View Source

Test helper functions imported by `Plushie.Test.Case`.

These functions retrieve the current session from the process dictionary, so they can be called without passing a session explicitly.

## Usage

These are automatically imported when using `Plushie.Test.Case`:

    use Plushie.Test.Case, app: MyApp

    test "example" do
      click("#my-button")
      assert model().count == 1
      assert_text("#label", "Count: 1")
    end

They can also be used with an explicit session by calling through `Plushie.Test.Session` directly.

## Summary

### Functions

advance_frame(timestamp)

Advances the renderer animation clock, driving both SDK-side animation frame events and renderer-side transitions.

assert_a11y(selector, expected)

Asserts that a widget has the expected accessibility attributes.

assert_exists(selector)

Asserts that a widget exists in the tree.

assert_model(expected)

Asserts that the current model equals `expected`.

assert_no_diagnostics()

Asserts that no prop validation diagnostics have been emitted.

assert_not_exists(selector)

Asserts that a widget does NOT exist in the tree.

assert_role(selector, expected_role)

Asserts that a widget has the expected accessibility role.

assert_screenshot(name, opts \\ [])

Captures a pixel screenshot and asserts it matches the golden file.

assert_screenshot_match(screenshot, golden_dir, opts \\ [])

Asserts that an existing screenshot matches the golden file.

assert_text(selector, expected)

Asserts that a widget contains the expected text.

assert_tree_hash(name)

Captures a structural tree hash and asserts it matches the golden file.

assert_tree_hash_match(tree_hash, golden_dir)

Asserts that an existing tree hash matches the golden file.

await_async(tag, timeout \\ 5000)

Waits for a tagged async task to complete.

canvas_move(selector, x, y, opts \\ [])

Moves on a canvas to the given coordinates.

canvas_press(selector, x, y, button \\ "left", opts \\ [])

Presses on a canvas at the given coordinates.

canvas_release(selector, x, y, button \\ "left", opts \\ [])

Releases on a canvas at the given coordinates.

click(selector, opts \\ [])

Clicks a widget identified by selector.

find(selector, opts \\ [])

Finds an element by selector. Returns nil if not found.

find!(selector, opts \\ [])

Finds an element by selector. Raises if not found.

find_by_label(label)

Finds an element by its accessibility label. Returns nil if not found.

find_by_role(role)

Finds an element by its accessibility role. Returns nil if not found.

find_focused()

Finds the currently focused element. Returns nil if none is focused.

model()

Returns the current app model.

move_to(x, y)

Moves the cursor to the given coordinates.

pane_focus_cycle(selector, opts \\ [])

Cycles focus in a pane grid.

paste(selector, text, opts \\ [])

Pastes text into a widget.

press(key)

Presses a key (key down). Key string may include modifiers, e.g. `"ctrl+s"`.

register_effect_stub(kind, response)

Registers an effect stub with the renderer for the current session.

release(key)

Releases a key (key up). Key string may include modifiers, e.g. `"ctrl+s"`.

reset()

Resets the session to initial state.

resolved_a11y(selector, opts \\ [])

Returns the resolved a11y map for the widget matching `selector`.

save_screenshot(name, opts \\ [])

Save a screenshot as a PNG file to `test/screenshots/{name}.png`.

screenshot(name, opts \\ [])

Captures a pixel screenshot with the given name.

scroll(selector, delta_x \\ 0, delta_y \\ 0, opts \\ [])

Scrolls a widget by the given deltas.

select(selector, value, opts \\ [])

Selects a value from a pick_list, combo_box, or radio group.

session()

Returns the current test session from the process dictionary.

skip_transitions()

Skips all active renderer-side transitions to completion.

slide(selector, value, opts \\ [])

Slides a slider to the given value.

sort(selector, column, direction \\ "asc", opts \\ [])

Sorts a table column.

start(app)

Creates and registers a new test session for `app`.

submit(selector, opts \\ [])

Submits a text input (simulates pressing enter).

text(element)

Extracts text content from an element.

toggle(selector, value \\ nil, opts \\ [])

Toggles a checkbox or toggler.

tree()

Returns the current normalized UI tree.

tree_hash(name)

Captures a structural tree hash with the given name.

type_key(key)

Types a key (press + release). Key string may include modifiers, e.g. `"enter"`.

type_text(selector, text, opts \\ [])

Types text into a widget identified by selector.

unregister_effect_stub(kind)

Removes a previously registered effect stub.

## Functions

# advance_frame(timestamp)

    @spec advance_frame(timestamp :: non_neg_integer()) :: :ok

Advances the renderer animation clock, driving both SDK-side animation frame events and renderer-side transitions.

In mock mode this is a no-op for renderer-side transitions (they resolve instantly). In headless/windowed mode it advances the animation clock by the given timestamp.

## Example

    click("#toggle")
    advance_frame(150)   # 150ms into the animation

# assert_a11y(selector, expected)

(macro)

Asserts that a widget has the expected accessibility attributes.

Finds the widget by selector and checks that its resolved a11y map (via `resolved_a11y/2`, which layers widget-level inference like placeholder -> description) contains all the expected key-value pairs.

    assert_a11y("#heading", %{role: :heading, level: 1})

# assert_exists(selector)

(macro)

Asserts that a widget exists in the tree.

# assert_model(expected)

(macro)

Asserts that the current model equals `expected`.

Uses strict equality (`==`). Raises `ExUnit.AssertionError` with a diff- friendly message on mismatch.

# assert_no_diagnostics()

    @spec assert_no_diagnostics() :: :ok

Asserts that no prop validation diagnostics have been emitted.

Checks the telemetry-based diagnostic collector set up by `Plushie.Test.Case`. Raises `ExUnit.AssertionError` with the diagnostic details if any diagnostics were received.

# assert_not_exists(selector)

(macro)

Asserts that a widget does NOT exist in the tree.

# assert_role(selector, expected_role)

(macro)

Asserts that a widget has the expected accessibility role.

Checks the inferred role (from widget type + any a11y override).

    assert_role("#save-button", "button")
    assert_role("#heading", "heading")

# assert_screenshot(name, opts \\ [])

    @spec assert_screenshot(name :: String.t(), opts :: keyword()) :: :ok

Captures a pixel screenshot and asserts it matches the golden file.

On first run, creates the golden file. On subsequent runs, compares hashes. Set `PLUSHIE_UPDATE_SCREENSHOTS=1` to force-update golden files. No-op on backends that don't support pixel capture (empty hash is silently accepted).

# assert_screenshot_match(screenshot, golden_dir, opts \\ [])

    @spec assert_screenshot_match(
      screenshot :: Plushie.Test.Screenshot.t(),
      golden_dir :: String.t(),
      opts :: keyword()
    ) :: :ok

Asserts that an existing screenshot matches the golden file.

Use this when you already have a `%Plushie.Automation.Screenshot{}` and want the imported helper form instead of calling `Plushie.Test.Screenshot.assert_match/2` directly.

# assert_text(selector, expected)

(macro)

Asserts that a widget contains the expected text.

Finds the widget by selector and compares its text content.

# assert_tree_hash(name)

    @spec assert_tree_hash(name :: String.t()) :: :ok

Captures a structural tree hash and asserts it matches the golden file.

On first run, creates the golden file. On subsequent runs, compares hashes. Set `PLUSHIE_UPDATE_SNAPSHOTS=1` to force-update golden files.

# assert_tree_hash_match(tree_hash, golden_dir)

    @spec assert_tree_hash_match(
      tree_hash :: Plushie.Test.TreeHash.t(),
      golden_dir :: String.t()
    ) :: :ok

Asserts that an existing tree hash matches the golden file.

Use this when you already have a `%Plushie.Test.TreeHash{}` and want the imported helper form instead of calling `Plushie.Test.TreeHash.assert_match/2` directly.

# await_async(tag, timeout \\ 5000)

    @spec await_async(tag :: atom(), timeout :: non_neg_integer()) :: :ok

Waits for a tagged async task to complete.

# canvas_move(selector, x, y, opts \\ [])

    @spec canvas_move(
      selector :: Plushie.Test.Session.selector(),
      x :: number(),
      y :: number(),
      opts :: keyword()
    ) :: :ok

Moves on a canvas to the given coordinates.

## Options

- `window:` - target window ID for multi-window apps

# canvas_press(selector, x, y, button \\ "left", opts \\ [])

    @spec canvas_press(
      selector :: Plushie.Test.Session.selector(),
      x :: number(),
      y :: number(),
      button :: String.t(),
      opts :: keyword()
    ) :: :ok

Presses on a canvas at the given coordinates.

## Options

- `window:` - target window ID for multi-window apps

# canvas_release(selector, x, y, button \\ "left", opts \\ [])

    @spec canvas_release(
      selector :: Plushie.Test.Session.selector(),
      x :: number(),
      y :: number(),
      button :: String.t(),
      opts :: keyword()
    ) :: :ok

Releases on a canvas at the given coordinates.

## Options

- `window:` - target window ID for multi-window apps

# click(selector, opts \\ [])

    @spec click(selector :: Plushie.Test.Session.selector(), opts :: keyword()) :: :ok

Clicks a widget identified by selector.

## Options

- `window:` - target window ID for multi-window apps

# find(selector, opts \\ [])

    @spec find(selector :: Plushie.Test.Session.selector(), opts :: keyword()) ::
      Plushie.Automation.Element.t() | nil

Finds an element by selector. Returns nil if not found.

## Options

- `window:` - target window ID for multi-window apps

# find!(selector, opts \\ [])

    @spec find!(selector :: Plushie.Test.Session.selector(), opts :: keyword()) ::
      Plushie.Automation.Element.t()

Finds an element by selector. Raises if not found.

## Options

- `window:` - target window ID for multi-window apps

# find_by_label(label)

    @spec find_by_label(label :: String.t()) :: Plushie.Automation.Element.t() | nil

Finds an element by its accessibility label. Returns nil if not found.

# find_by_role(role)

    @spec find_by_role(role :: atom() | String.t()) ::
      Plushie.Automation.Element.t() | nil

Finds an element by its accessibility role. Returns nil if not found.

# find_focused()

    @spec find_focused() :: Plushie.Automation.Element.t() | nil

Finds the currently focused element. Returns nil if none is focused.

# model()

    @spec model() :: term()

Returns the current app model.

# move_to(x, y)

    @spec move_to(x :: number(), y :: number()) :: :ok

Moves the cursor to the given coordinates.

# pane_focus_cycle(selector, opts \\ [])

    @spec pane_focus_cycle(selector :: Plushie.Test.Session.selector(), opts :: keyword()) ::
      :ok

Cycles focus in a pane grid.

## Options

- `window:` - target window ID for multi-window apps

# paste(selector, text, opts \\ [])

    @spec paste(
      selector :: Plushie.Test.Session.selector(),
      text :: String.t(),
      opts :: keyword()
    ) :: :ok

Pastes text into a widget.

## Options

- `window:` - target window ID for multi-window apps

# press(key)

    @spec press(key :: String.t()) :: :ok

Presses a key (key down). Key string may include modifiers, e.g. `"ctrl+s"`.

# register_effect_stub(kind, response)

    @spec register_effect_stub(kind :: Plushie.Effect.kind(), response :: term()) :: :ok

Registers an effect stub with the renderer for the current session.

The renderer will return `response` immediately for any effect of the given `kind`, without executing the real effect. The `kind` matches the effect function name as an atom (e.g. `:file_open`, `:clipboard_write`).

# release(key)

    @spec release(key :: String.t()) :: :ok

Releases a key (key up). Key string may include modifiers, e.g. `"ctrl+s"`.

# reset()

    @spec reset() :: :ok

Resets the session to initial state.

# resolved_a11y(selector, opts \\ [])

    @spec resolved_a11y(selector :: Plushie.Test.Session.selector(), opts :: keyword()) ::
      map()

Returns the resolved a11y map for the widget matching `selector`.

Unlike reading the raw `:a11y` prop, this runs the same inference the render pipeline applies: placeholder flows into `:description` for text inputs and related widgets, `alt` flows into `:label` for media widgets. The normalizer has already populated cross-widget defaults (role, implicit `radio_group`), so the returned map is what assistive technology will see.

Raises if the selector doesn't match any widget.

# save_screenshot(name, opts \\ [])

    @spec save_screenshot(name :: String.t(), opts :: keyword()) ::
      Plushie.Test.Screenshot.t()

Save a screenshot as a PNG file to `test/screenshots/{name}.png`.

Takes a screenshot, saves it, and returns the screenshot struct. Creates the `test/screenshots/` directory if it doesn't exist. No-op on backends that don't support pixel capture.

# screenshot(name, opts \\ [])

    @spec screenshot(name :: String.t(), opts :: keyword()) :: Plushie.Test.Screenshot.t()

Captures a pixel screenshot with the given name.

# scroll(selector, delta_x \\ 0, delta_y \\ 0, opts \\ [])

    @spec scroll(
      selector :: Plushie.Test.Session.selector(),
      delta_x :: number(),
      delta_y :: number(),
      opts :: keyword()
    ) :: :ok

Scrolls a widget by the given deltas.

## Options

- `window:` - target window ID for multi-window apps

# select(selector, value, opts \\ [])

    @spec select(
      selector :: Plushie.Test.Session.selector(),
      value :: term(),
      opts :: keyword()
    ) :: :ok

Selects a value from a pick_list, combo_box, or radio group.

## Options

- `window:` - target window ID for multi-window apps

# session()

    @spec session() :: Plushie.Test.Session.t()

Returns the current test session from the process dictionary.

# skip_transitions()

    @spec skip_transitions() :: :ok

Skips all active renderer-side transitions to completion.

Advances the animation clock far enough to complete any reasonable animation (10 seconds). Useful in tests that trigger animations but only care about the final state.

## Example

    click("#toggle")
    skip_transitions()
    assert find!("#box").props[:opacity] == 0.0

# slide(selector, value, opts \\ [])

    @spec slide(
      selector :: Plushie.Test.Session.selector(),
      value :: number(),
      opts :: keyword()
    ) :: :ok

Slides a slider to the given value.

## Options

- `window:` - target window ID for multi-window apps

# sort(selector, column, direction \\ "asc", opts \\ [])

    @spec sort(
      selector :: Plushie.Test.Session.selector(),
      column :: String.t(),
      direction :: String.t(),
      opts :: keyword()
    ) :: :ok

Sorts a table column.

## Options

- `window:` - target window ID for multi-window apps

# start(app)

    @spec start(app :: module()) :: Plushie.Test.Session.t()

Creates and registers a new test session for `app`.

Resolves the backend from `PLUSHIE_TEST_BACKEND` env var, application config, or defaults to `:mock`. Stores the session in the process dictionary so all helper functions work without explicit session threading.

Call this in a test or setup block when not using `Plushie.Test.Case`.

# submit(selector, opts \\ [])

    @spec submit(selector :: Plushie.Test.Session.selector(), opts :: keyword()) :: :ok

Submits a text input (simulates pressing enter).

## Options

- `window:` - target window ID for multi-window apps

# text(element)

    @spec text(element :: Plushie.Automation.Element.t()) :: String.t() | nil

Extracts text content from an element.

# toggle(selector, value \\ nil, opts \\ [])

    @spec toggle(
      selector :: Plushie.Test.Session.selector(),
      value :: boolean() | nil,
      opts :: keyword()
    ) ::
      :ok

Toggles a checkbox or toggler.

Without a value, reads the current state and negates it. With an explicit boolean, sets the widget directly without reading state.

    toggle("#dark_mode")          # flip current state
    toggle("#agree_check", true)  # check
    toggle("#agree_check", false) # uncheck

## Options

- `window:` - target window ID for multi-window apps

# tree()

    @spec tree() :: map()

Returns the current normalized UI tree.

# tree_hash(name)

    @spec tree_hash(name :: String.t()) :: Plushie.Test.TreeHash.t()

Captures a structural tree hash with the given name.

# type_key(key)

    @spec type_key(key :: String.t()) :: :ok

Types a key (press + release). Key string may include modifiers, e.g. `"enter"`.

# type_text(selector, text, opts \\ [])

    @spec type_text(
      selector :: Plushie.Test.Session.selector(),
      text :: String.t(),
      opts :: keyword()
    ) ::
      :ok

Types text into a widget identified by selector.

## Options

- `window:` - target window ID for multi-window apps

# unregister_effect_stub(kind)

    @spec unregister_effect_stub(kind :: Plushie.Effect.kind()) :: :ok

Removes a previously registered effect stub.