# `Coelho.LiveViewTest`
[🔗](https://github.com/nseaSeb/coelho/blob/main/lib/coelho/live_view_test.ex#L2)

Driving and reading the editor from a LiveView test.

The editor's container carries `phx-update="ignore"` — ProseMirror owns
that subtree — so it is invisible to `render_change/2`: there is no input
to fill and no text to assert on. What the server sees is the hidden
input the hook writes the document into, and a test has to write it
itself.

Doing that by hand is three lines of JSON encoding and parameter nesting
at every call site, and getting the nesting wrong fails as "the form
ignored the change" rather than as a mistake. This is those three lines.

    import Coelho.LiveViewTest

    test "the intro is saved", %{conn: conn} do
      {:ok, view, _html} = live(conn, ~p"/portal/edit")

      type(view, "page[intro_doc]", paragraph("bonjour"))

      assert document(view, "page[intro_doc]") == paragraph("bonjour")
    end

Nothing here needs the browser: it writes what the hook would have
written, and reads what the server rendered back.

# `name`

```elixir
@type name() :: String.t() | struct()
```

A hidden input's name, or the form field it was rendered from.

# `document`

```elixir
@spec document(term(), name()) :: map() | String.t() | nil
```

The document an editor is currently holding, decoded.

Read off the hidden input the server rendered, which is where the
editor's state is visible from Elixir. Returns `nil` when there is no
such input, and the raw string when it does not hold a document — which
is what a rejected document comes back as, so that the writer can fix it
rather than lose it.

# `params`

```elixir
@spec params(name(), map(), map()) :: map()
```

The parameters a change carrying this document would arrive with.

`type/4` sends these; this is for a test that has its own way of sending —
a `render_submit`, a `render_hook`, a controller `post`. The nesting is
the browser's: `page[intro_doc]` becomes
`%{"page" => %{"intro_doc" => json}}`, which is what `Plug.Conn.Query`
reads back and what a form expects to see.

    params("page[intro_doc]", document, %{"page" => %{"title" => "Été"}})
    #=> %{"page" => %{"intro_doc" => "{…}", "title" => "Été"}}

# `type`

```elixir
@spec type(term(), name(), map(), keyword()) :: String.t()
```

Posts a document as the editor would, and returns the rendered result.

`name` is the hidden input's name — `"page[intro_doc]"` — or the
`%Phoenix.HTML.FormField{}` it was rendered from. The document is encoded
and nested into parameters the same way a browser would nest them.

## Options

  * `:event` — the `phx-change` event to send, `"validate"` by default
  * `:params` — the form's other fields, merged into the parameters as a
    browser would send them, which means **nested the way the form nests
    them**: `params: %{"post" => %{"title" => "t"}}`, not
    `%{"title" => "t"}`. A changeset wanting a title reports the *title*
    when a change arrives without one, so this is how the document under
    test stays the thing under test — and a title merged at the top level
    never reaches `handle_event("validate", %{"post" => params}, …)` at
    all

There used to be a `:form` option here, sending the change through
`Phoenix.LiveViewTest.form/3` instead. It cannot work for this field and
never could: `form/3` refuses a **hidden** input whose value differs from
the one that was rendered, and an editor's field is hidden and differs by
definition — that is what typing is. It raised
`value for hidden "post[body]" must be one of […]` at every call. Pass
the other fields through `:params`.

---

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