# `Plushie.Test.Helpers`
[🔗](https://github.com/plushie-ui/plushie-elixir/blob/v0.7.2/lib/plushie/test/helpers.ex#L1)

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.

# `advance_frame`

```elixir
@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`
*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`
*macro* 

Asserts that a widget exists in the tree.

# `assert_model`
*macro* 

Asserts that the current model equals `expected`.

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

# `assert_no_diagnostics`

```elixir
@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`
*macro* 

Asserts that a widget does NOT exist in the tree.

# `assert_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`

```elixir
@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`

```elixir
@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`
*macro* 

Asserts that a widget contains the expected text.

Finds the widget by selector and compares its text content.

# `assert_tree_hash`

```elixir
@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`

```elixir
@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`

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

Waits for a tagged async task to complete.

# `canvas_move`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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!`

```elixir
@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`

```elixir
@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`

```elixir
@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`

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

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

# `model`

```elixir
@spec model() :: term()
```

Returns the current app model.

# `move_to`

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

Moves the cursor to the given coordinates.

# `pane_focus_cycle`

```elixir
@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`

```elixir
@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`

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

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

# `register_effect_stub`

```elixir
@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`

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

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

# `reset`

```elixir
@spec reset() :: :ok
```

Resets the session to initial state.

# `resolved_a11y`

```elixir
@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`

```elixir
@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`

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

Captures a pixel screenshot with the given name.

# `scroll`

```elixir
@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`

```elixir
@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`

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

Returns the current test session from the process dictionary.

# `skip_transitions`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

```elixir
@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`

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

Extracts text content from an element.

# `toggle`

```elixir
@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`

```elixir
@spec tree() :: map()
```

Returns the current normalized UI tree.

# `tree_hash`

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

Captures a structural tree hash with the given name.

# `type_key`

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

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

# `type_text`

```elixir
@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`

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

Removes a previously registered effect stub.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
