Coelho.LiveViewTest (coelho v0.3.1)

Copy Markdown View Source

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.

Summary

Types

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

Functions

The document an editor is currently holding, decoded.

The parameters a change carrying this document would arrive with.

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

Types

name()

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

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

Functions

document(view_or_html, name)

@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(name, document, extra \\ %{})

@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(view, name, document, opts \\ [])

@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
  • :form — a selector, to go through the form rather than through the event. Use this when the change has to carry the form's other fields: form: "#page-form"
  • :params — extra parameters merged in, for a change the application expects to arrive with company