Masu.Test (Masu v0.7.0)

Copy Markdown View Source

Helpers for Phoenix.LiveViewTest, to read and query the grids of a LiveView with no browser.

import Masu.Test

test "finds the late orders", %{conn: conn} do
  {:ok, view, _html} = live(conn, ~p"/orders")

  assert total(view, "orders") == 120

  view
  |> filter("orders", [{"status", :eq, "late"}])
  |> sort("orders", {"total", :desc})

  assert [%{"number" => "A-1042"} | _] = rows(view, "orders")
end

The functions send the events that the browser sends (see Masu.Grid), so the grid applies a sort, a filter or a search as it does for a person: the source sorts, filters and counts, and rows/3 reads the rows it gives.

Changes broadcast with Masu.Live before a call are in the rows it reads: the call waits for the LiveView to take them.

A row is a map of each column's key to its value as the browser gets it: numbers and booleans as they are, decimals as strings, dates and times in ISO 8601, a list for a column of many values (field={[:tags, :name]}), and for a column with content the text of its cell.

Summary

Functions

Runs the action label on the rows selected in the grid id (:action, see Masu.Action), as its button in the bar above the grid does, past its question (confirm). Rows are selected with select/4 or select_all/3.

The labels of the actions of your own (Masu.Action) that the grid id offers: on the rows selected (actions(view, id, :selection), while rows are selected), on the row row_id (actions(view, id, :row, row_id): those it shows, available or not), or for the column key in its header menu (:header) or in the menu of its cells (:cell).

Opens the row row_id of the grid id (on_row_activate), as a click on it or Enter does. The grid reads the row from its source.

Adds rows to the grid id, as the person does with "New row" or a paste below the last row: each a map of column keys (or atoms) to values, as the browser sends them. The grid saves each through its create, or its changeset of its struct.

Applies a view to the grid id, as a link or a saved view does: params as view/2 gives them (%{} for the grid's own view). Returns the keys of the columns of the parts that no longer apply (see Masu.View.from_params/3).

Asks the grid id for its count, as the person does when they go to the end of a grid that does not know it (Ctrl+End): {:exact, n}, or {:at_least, n} from a source that does not know it either.

Runs the item label of the menu of the cell of the row row_id and the column key of the grid id, as header_action/4 does. The grid reads the row and the value of the cell from its source.

Closes the editor of a cell of the grid id, as Escape does. Returns the view.

Closes the panel of an item of the grid id, as Escape does. Returns the view.

Closes the row row_id of a tree in the grid id. Returns the view.

The labels of the columns of the grid id, in order.

What the grid id knows of its count: {:exact, n}, or {:at_least, n} while it does not know it (count={:none}, a count not in yet, or a source that does not count). total/2 gives n.

Deletes the rows ids of the grid id, or, with :selection, the rows selected (select/4, select_all/3), as the person does with Ctrl+Delete. Returns {:ok, undo}, with the token that brings them back (restore_rows/3; nil when the grid cannot), :async when the grid's on_delete deletes them later, or {:error, errors, undo} with {row_id, message} for each row that was not deleted. Options: :origin, as edit/4.

Opens (or, with false, closes) the detail of the row row_id in the grid id: the grid renders its :detail slot for the row, which Phoenix.LiveViewTest.render/1 then has. Returns the view.

Edits cells of the grid id, as a person typing in them, pasting or undoing does: edits is a list of {row_id, key, value}, the value as the browser sends it (a string, or nil to empty the cell). The grid saves the edits of each row together.

Opens the row row_id of a tree in the grid id, as a click on its arrow (or →) does: its children show under it. Returns the view.

Opens every row beside row_id that has children, as * does. Returns the view.

Exports the grid id as its Export menu does (Masu.Export), and downloads the file from the link the browser gets, through the endpoint and the router's export route (Masu.Router): {name, binary}. A conn (Phoenix.ConnTest.build_conn/0, with what the route's pipeline wants of it, a signed-in user) is given with :conn.

The link the Export menu of the grid id gives the browser, for a test of the route itself: that it wants a signed-in user, say.

Filters the grid id: filters is a list of {key, op, value} (see Masu.Filter for the operators), which replaces the grid's filters. Returns the view.

Groups the rows of the grid id by columns (Masu.Group), as the header menu does: keys, the first the outermost; [] stops grouping. Returns the view.

The rows of a grouped grid id that are groups, in order, as maps of :id (what expand/3 takes), :key, :value, :count, :level, :expanded and :totals (by column key). Options as rows/3.

Runs the item label of the header menu of the column key of the grid id, as a click on it does: its function, its event to the LiveView, or its panel, which Phoenix.LiveViewTest.render/1 then has. An item that is only a JS command runs in the browser, not here. Returns the view.

The history of the cell of the row row_id and the column key of the grid id (history, see Masu.History), or of the row when key is nil, newest first, as its popover shows it (none for a row the grid's query does not show): maps of :id, :key, :op, :before, :after (as the browser gets them), :user, :origin, :batch, :batch_size and :at.

The ids of the rows of the grid id, as rows/3 reads them.

Changes the layout of the grid id, as its columns menu, its headers and their edges do: any of order: keys (every column, in order), hidden: keys, pinned: %{key => :start | :end} and widths: %{key => pixels}. Returns the view.

The rows of a tree (Masu.Tree) in the grid id, in order, as maps of :id, :level (0 for a root), :children (whether it has any) and :expanded. Options as rows/3.

Opens the editor of the cell of the row row_id and the column key of the grid id, when the column's editor is a form of yours: the grid renders it, which Phoenix.LiveViewTest.render/1 then has. A value it would post is saved with edit/4, as the browser saves it. Returns the view.

Goes to a page of the grid id (paging={:pages}), from 1, as its pager does, and gives the ids of its rows.

The place (from 0) of the row row_id in the grid's current order, or nil when its query leaves the row out: where the grid scrolls to follow another person to it.

Brings back the value before an entry of the history of the grid id (its :id, history/4), or, with {:batch, batch}, every cell of a batch (someone's paste), as its Restore does: an edit, refused for a cell that changed since. Returns {:ok, count} of the cells restored, or {:error, errors} as edit/4 does.

Brings back the rows of a delete (its undo token, delete_rows/3), as the person does with Ctrl+Z. Returns {:ok, ids}.

Runs the action label on the row row_id of the grid id (:row_action, see Masu.Action), as its item in the row's menu does, past its question (confirm). The grid reads the row from its source.

The rows of the grid id, in its current order.

Searches the grid id as its search box does. Returns the view.

Selects rows of the grid id (selectable) by their ids, as their boxes do, or, with false, takes them out. Returns the view.

Selects every row of the query of the grid id (or, with false, none). Returns the view.

The selection of the grid id: %{all: boolean, ids: [...], except: [...], count: n}.

Sorts the grid id as clicks on its headers do: sorts is a key, a {key, :asc | :desc}, or a list of them (the first the most important). [] takes the order away. Returns the view.

How many rows the grid id has for its current query.

The totals of the query of the grid id, by column key: the row of totals at its bottom (Masu.Group). nil when no column has an aggregate.

The values that the filter menu of the column key offers: the values of the column among the rows of the query (without the column's own filters), each with its count, the most frequent first. :search narrows them as the menu's search box does.

The view of the grid id, as its params (see Masu.View): what differs from the grid's own view. %{} is the grid's own view.

Functions

action(view, id, label)

@spec action(struct(), String.t(), String.t()) ::
  :ok | {:ok, String.t()} | {:error, String.t() | :refused} | :async

Runs the action label on the rows selected in the grid id (:action, see Masu.Action), as its button in the bar above the grid does, past its question (confirm). Rows are selected with select/4 or select_all/3.

Returns the action's result: :ok, {:ok, message}, {:error, message} or :async (see Masu.Action), or {:error, :refused} when the server refuses it: the grid does not offer it (:if), or no row is selected.

actions(view, id, atom)

@spec actions(struct(), String.t(), :selection) :: [String.t()]

The labels of the actions of your own (Masu.Action) that the grid id offers: on the rows selected (actions(view, id, :selection), while rows are selected), on the row row_id (actions(view, id, :row, row_id): those it shows, available or not), or for the column key in its header menu (:header) or in the menu of its cells (:cell).

actions(view, id, kind, row_id)

@spec actions(struct(), String.t(), :header | :cell | :row, term()) :: [String.t()]

activate_row(view, id, row_id)

@spec activate_row(struct(), String.t(), term()) ::
  :ok | {:navigate | :patch, String.t()} | {:error, :refused}

Opens the row row_id of the grid id (on_row_activate), as a click on it or Enter does. The grid reads the row from its source.

Returns what the grid does: {:patch, path} or {:navigate, path} when the function gives one, else :ok; {:error, :refused} when the grid has no on_row_activate or no row row_id in its query.

add_rows(view, id, rows, opts \\ [])

@spec add_rows(struct(), String.t(), map() | [map()], keyword()) ::
  {:ok, [term()]}
  | {:error, ok: term(), error: [{String.t() | nil, String.t()}]}

Adds rows to the grid id, as the person does with "New row" or a paste below the last row: each a map of column keys (or atoms) to values, as the browser sends them. The grid saves each through its create, or its changeset of its struct.

Returns {:ok, ids} when every row was saved, or {:error, results}, a result for each row: {:ok, id} or {:error, [{key, message}]} (a key of nil for an error of the row). Options: :origin, as edit/4.

apply_view(view, id, params)

@spec apply_view(struct(), String.t(), map()) :: [String.t()]

Applies a view to the grid id, as a link or a saved view does: params as view/2 gives them (%{} for the grid's own view). Returns the keys of the columns of the parts that no longer apply (see Masu.View.from_params/3).

ask_count(view, id)

@spec ask_count(struct(), String.t()) :: {:exact | :at_least, non_neg_integer()}

Asks the grid id for its count, as the person does when they go to the end of a grid that does not know it (Ctrl+End): {:exact, n}, or {:at_least, n} from a source that does not know it either.

cell_action(view, id, label, key, row_id, opts \\ [])

@spec cell_action(
  struct(),
  String.t(),
  String.t(),
  String.t() | atom(),
  term(),
  keyword()
) :: struct()

Runs the item label of the menu of the cell of the row row_id and the column key of the grid id, as header_action/4 does. The grid reads the row and the value of the cell from its source.

Options: :range, {ids, keys}: a range of cells selected, its rows' ids and its columns' keys, that the cell is in.

close_editor(view, id)

@spec close_editor(struct(), String.t()) :: struct()

Closes the editor of a cell of the grid id, as Escape does. Returns the view.

close_panel(view, id)

@spec close_panel(struct(), String.t()) :: struct()

Closes the panel of an item of the grid id, as Escape does. Returns the view.

collapse(view, id, row_id)

@spec collapse(struct(), String.t(), term()) :: struct()

Closes the row row_id of a tree in the grid id. Returns the view.

columns(view, id)

@spec columns(struct(), String.t()) :: [String.t()]

The labels of the columns of the grid id, in order.

count(view, id)

@spec count(struct(), String.t()) :: {:exact | :at_least, non_neg_integer()}

What the grid id knows of its count: {:exact, n}, or {:at_least, n} while it does not know it (count={:none}, a count not in yet, or a source that does not count). total/2 gives n.

delete_rows(view, id, ids, opts \\ [])

@spec delete_rows(struct(), String.t(), [term()] | :selection, keyword()) ::
  {:ok, String.t() | nil}
  | :async
  | {:error, [{term(), String.t()}], String.t() | nil}

Deletes the rows ids of the grid id, or, with :selection, the rows selected (select/4, select_all/3), as the person does with Ctrl+Delete. Returns {:ok, undo}, with the token that brings them back (restore_rows/3; nil when the grid cannot), :async when the grid's on_delete deletes them later, or {:error, errors, undo} with {row_id, message} for each row that was not deleted. Options: :origin, as edit/4.

detail(view, id, row_id, open \\ true)

@spec detail(struct(), String.t(), term(), boolean()) :: struct()

Opens (or, with false, closes) the detail of the row row_id in the grid id: the grid renders its :detail slot for the row, which Phoenix.LiveViewTest.render/1 then has. Returns the view.

edit(view, id, edits, opts \\ [])

@spec edit(struct(), String.t(), [tuple()], keyword()) ::
  :ok | :async | {:error, [{term(), String.t(), String.t()}]}

Edits cells of the grid id, as a person typing in them, pasting or undoing does: edits is a list of {row_id, key, value}, the value as the browser sends it (a string, or nil to empty the cell). The grid saves the edits of each row together.

An edit may be {row_id, key, value, before}, with the value the person saw: the grid refuses it when the cell's value changed meanwhile, as it does for the browser.

Returns :ok when every edit was saved, :async when the grid's on_update saves them later (Masu.Write.async/1), or {:error, errors} with {row_id, key, message} for each cell that was not.

Options: :origin, what the person did, as Masu.Write's origin says (:paste, :fill...); :test unless given.

expand(view, id, row_id)

@spec expand(struct(), String.t(), term()) :: struct()

Opens the row row_id of a tree in the grid id, as a click on its arrow (or →) does: its children show under it. Returns the view.

expand_siblings(view, id, row_id)

@spec expand_siblings(struct(), String.t(), term()) :: struct()

Opens every row beside row_id that has children, as * does. Returns the view.

export(view, id, format, opts \\ [])

@spec export(struct(), String.t(), :csv | :xlsx, keyword()) :: {String.t(), binary()}

Exports the grid id as its Export menu does (Masu.Export), and downloads the file from the link the browser gets, through the endpoint and the router's export route (Masu.Router): {name, binary}. A conn (Phoenix.ConnTest.build_conn/0, with what the route's pipeline wants of it, a signed-in user) is given with :conn.

export_url(view, id, format)

@spec export_url(struct(), String.t(), :csv | :xlsx) :: String.t()

The link the Export menu of the grid id gives the browser, for a test of the route itself: that it wants a signed-in user, say.

filter(view, id, filters)

@spec filter(struct(), String.t(), [tuple()]) :: struct()

Filters the grid id: filters is a list of {key, op, value} (see Masu.Filter for the operators), which replaces the grid's filters. Returns the view.

filter(view, "orders", [{"customer.name", :eq, "Ana"}, {"tags", :all_of, ["gift"]}])

group(view, id, keys)

@spec group(struct(), String.t(), [String.t() | atom()]) :: struct()

Groups the rows of the grid id by columns (Masu.Group), as the header menu does: keys, the first the outermost; [] stops grouping. Returns the view.

groups(view, id, opts \\ [])

@spec groups(struct(), String.t(), keyword()) :: [map()]

The rows of a grouped grid id that are groups, in order, as maps of :id (what expand/3 takes), :key, :value, :count, :level, :expanded and :totals (by column key). Options as rows/3.

header_action(view, id, label, key)

@spec header_action(struct(), String.t(), String.t(), String.t() | atom()) :: struct()

Runs the item label of the header menu of the column key of the grid id, as a click on it does: its function, its event to the LiveView, or its panel, which Phoenix.LiveViewTest.render/1 then has. An item that is only a JS command runs in the browser, not here. Returns the view.

history(view, id, row_id, key \\ nil)

@spec history(struct(), String.t(), term(), String.t() | atom() | nil) :: [map()]

The history of the cell of the row row_id and the column key of the grid id (history, see Masu.History), or of the row when key is nil, newest first, as its popover shows it (none for a row the grid's query does not show): maps of :id, :key, :op, :before, :after (as the browser gets them), :user, :origin, :batch, :batch_size and :at.

ids(view, id, opts \\ [])

@spec ids(struct(), String.t(), keyword()) :: [term()]

The ids of the rows of the grid id, as rows/3 reads them.

layout(view, id, changes)

@spec layout(struct(), String.t(), keyword()) :: struct()

Changes the layout of the grid id, as its columns menu, its headers and their edges do: any of order: keys (every column, in order), hidden: keys, pinned: %{key => :start | :end} and widths: %{key => pixels}. Returns the view.

nodes(view, id, opts \\ [])

@spec nodes(struct(), String.t(), keyword()) :: [map()]

The rows of a tree (Masu.Tree) in the grid id, in order, as maps of :id, :level (0 for a root), :children (whether it has any) and :expanded. Options as rows/3.

open_editor(view, id, row_id, key)

@spec open_editor(struct(), String.t(), term(), String.t() | atom()) :: struct()

Opens the editor of the cell of the row row_id and the column key of the grid id, when the column's editor is a form of yours: the grid renders it, which Phoenix.LiveViewTest.render/1 then has. A value it would post is saved with edit/4, as the browser saves it. Returns the view.

page(view, id, page)

@spec page(struct(), String.t(), pos_integer()) :: [term()]

Goes to a page of the grid id (paging={:pages}), from 1, as its pager does, and gives the ids of its rows.

place(view, id, row_id)

@spec place(struct(), String.t(), term()) :: non_neg_integer() | nil

The place (from 0) of the row row_id in the grid's current order, or nil when its query leaves the row out: where the grid scrolls to follow another person to it.

restore(view, id, entry)

@spec restore(struct(), String.t(), String.t() | {:batch, String.t()}) ::
  {:ok, non_neg_integer()} | {:error, [{term(), String.t(), String.t()}]}

Brings back the value before an entry of the history of the grid id (its :id, history/4), or, with {:batch, batch}, every cell of a batch (someone's paste), as its Restore does: an edit, refused for a cell that changed since. Returns {:ok, count} of the cells restored, or {:error, errors} as edit/4 does.

restore_rows(view, id, undo)

@spec restore_rows(struct(), String.t(), String.t()) ::
  {:ok, [term()]} | {:error, term()}

Brings back the rows of a delete (its undo token, delete_rows/3), as the person does with Ctrl+Z. Returns {:ok, ids}.

row_action(view, id, label, row_id)

@spec row_action(struct(), String.t(), String.t(), term()) ::
  :ok
  | {:ok, String.t()}
  | {:error, String.t() | :refused}
  | :async
  | {:navigate | :patch, String.t()}

Runs the action label on the row row_id of the grid id (:row_action, see Masu.Action), as its item in the row's menu does, past its question (confirm). The grid reads the row from its source.

Returns the action's result, as action/3 does; for a link, the path it goes to, {:navigate, path} or {:patch, path}; and {:error, :refused} when the server refuses it: the grid does not offer it, the row does not show it, or it is disabled for the row.

rows(view, id, opts \\ [])

@spec rows(struct(), String.t(), keyword()) :: [map()]

The rows of the grid id, in its current order.

Options: :from (0), the first row, and :count (100), how many (at most 500, as the browser asks).

search(view, id, text)

@spec search(struct(), String.t(), String.t()) :: struct()

Searches the grid id as its search box does. Returns the view.

select(view, id, ids, on \\ true)

@spec select(struct(), String.t(), [term()], boolean()) :: struct()

Selects rows of the grid id (selectable) by their ids, as their boxes do, or, with false, takes them out. Returns the view.

select_all(view, id, on \\ true)

@spec select_all(struct(), String.t(), boolean()) :: struct()

Selects every row of the query of the grid id (or, with false, none). Returns the view.

selection(view, id)

@spec selection(struct(), String.t()) :: map()

The selection of the grid id: %{all: boolean, ids: [...], except: [...], count: n}.

sort(view, id, sorts)

@spec sort(struct(), String.t(), term()) :: struct()

Sorts the grid id as clicks on its headers do: sorts is a key, a {key, :asc | :desc}, or a list of them (the first the most important). [] takes the order away. Returns the view.

A column of a related row's field (field={[:customer, :name]}) is named by its key, the path with dots unless given: sort(view, "orders", {"customer.name", :desc}).

total(view, id)

@spec total(struct(), String.t()) :: non_neg_integer()

How many rows the grid id has for its current query.

totals(view, id)

@spec totals(struct(), String.t()) :: map() | nil

The totals of the query of the grid id, by column key: the row of totals at its bottom (Masu.Group). nil when no column has an aggregate.

values(view, id, key, opts \\ [])

@spec values(struct(), String.t(), String.t(), keyword()) :: [
  {term(), non_neg_integer()}
]

The values that the filter menu of the column key offers: the values of the column among the rows of the query (without the column's own filters), each with its count, the most frequent first. :search narrows them as the menu's search box does.

view(view, id)

@spec view(struct(), String.t()) :: map()

The view of the grid id, as its params (see Masu.View): what differs from the grid's own view. %{} is the grid's own view.