Helpers for Phoenix.LiveViewTest, to read and change the canvases of
a LiveView with no browser.
import Hakushi.Test
test "joins two tables", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/schema")
assert :ok = connect(view, "schema", {"orders", "user_id"}, {"users", "id"})
assert [%{source: "orders", target: "users"}] = edges(view, "schema")
assert {:error, "A node cannot connect to itself."} =
connect(view, "schema", "users", "users")
endThe functions send the events that the browser sends (see
Hakushi.Canvas), so each change goes the way of a person's: the rules,
on_change, the room of a shared canvas. A change the canvas refuses
gives {:error, reason}, with the words the person sees.
nodes/2 and edges/2 read what the canvas shows: the nodes and edges
as the browser has them, in their order.
Summary
Functions
Connects two nodes, as a person draws an edge from a handle to a
handle: each end a node's id, or {node, handle}.
Copies nodes, and pastes them at {x, y} (or next to them): gives
{:ok, ids} of the new nodes.
Draws a line with a kit's tool ("ink:pen"), as a pen does on the
canvas when it lifts: its points on the canvas ([x, y] or [x, y, pressure]). Gives {:ok, id} of the node the kit made of it.
Options: :color, :size, and :parent, the group it is drawn over.
Draws a box with a kit's tool of the mode "box" ("shapes:rect"), as
a drag on the paper does: [x, y, width, height] of the canvas (a box
of no size, as a click). Gives {:ok, id} of the node the kit made.
Drops an item (Hakushi.Components.canvas_item/1) on the canvas at
{x, y}, or double-clicks there (item is nil): gives {:ok, [id]}
of the node that new_node made. parent: group drops it in a group,
as a drop on the group does.
Duplicates nodes, as ⌘D does: gives {:ok, ids} of the copies.
The edges of the canvas id: maps with :id, :source,
:source_handle, :target, :target_handle, :path, :label,
:marker and :animated.
Edits a kit's node, as its controls do (Hakushi.Kit): the action,
with the event's params ("value", a column's index, ...). A list of
nodes is edited in one change, as the style panel of a kit does
(Hakushi.Kit.styles/1).
Sets the label of an edge, or takes it away (nil).
Moves nodes, as a person drags them: {id, x, y} for each, or one
node's id and {x, y}. A tuple {id, x, y, width, height} resizes it
too. A map %{id: id, x: x, y: y, parent: group} drops it into a group
(or out of one, with parent: nil), as a drop on a group does.
The nodes of the canvas id, in the order they are drawn: maps with
:id, :type, :x, :y, :width, :height (nil for the size of
the content), :locked, :parent (the group it is in, or nil) and
:label (the name screen readers say).
The words the canvas shows for the last change it refused, or nil.
The paper the canvas shows: %{background: name, grid: size}, its
graph's when people chose one, else its own (Hakushi.Paper).
Chooses the canvas's paper with its picker (papers), as a person does:
a paper it offers, by name, and the size of its squares (one of
Hakushi.Paper.grids/0).
The people the canvas id shows on a shared canvas (not the person
of this LiveView): their ids.
Moves an end of an edge to another handle, as a person drags the end of
a selected edge: source: or target:, a node's id or
{node, handle}.
Does again the last change undone, as ⇧⌘Z does.
Deletes nodes (with their edges) and edges, as the Delete key does:
nodes: ids, edges: ids. (Not delete, which Phoenix.ConnTest has.)
Undoes the last change of this LiveView's person, as ⌘Z does.
Functions
@spec connect( struct(), String.t(), String.t() | {String.t(), String.t()}, String.t() | {String.t(), String.t()} ) :: :ok | {:error, String.t()}
Connects two nodes, as a person draws an edge from a handle to a
handle: each end a node's id, or {node, handle}.
@spec copy_paste(struct(), String.t(), [String.t()], {number(), number()} | nil) :: {:ok, [String.t()]} | {:error, String.t()}
Copies nodes, and pastes them at {x, y} (or next to them): gives
{:ok, ids} of the new nodes.
@spec draw(struct(), String.t(), String.t(), [[number()]], keyword()) :: {:ok, String.t()} | {:error, String.t() | nil}
Draws a line with a kit's tool ("ink:pen"), as a pen does on the
canvas when it lifts: its points on the canvas ([x, y] or [x, y, pressure]). Gives {:ok, id} of the node the kit made of it.
Options: :color, :size, and :parent, the group it is drawn over.
{:ok, id} = draw(view, "board", "ink:pen", [[0, 0], [40, 12], [80, 0]], color: "red")
@spec draw_box(struct(), String.t(), String.t(), [number()], keyword()) :: {:ok, String.t()} | {:error, String.t() | nil}
Draws a box with a kit's tool of the mode "box" ("shapes:rect"), as
a drag on the paper does: [x, y, width, height] of the canvas (a box
of no size, as a click). Gives {:ok, id} of the node the kit made.
@spec drop(struct(), String.t(), String.t() | nil, {number(), number()}, keyword()) :: {:ok, [String.t()]} | {:error, String.t()}
Drops an item (Hakushi.Components.canvas_item/1) on the canvas at
{x, y}, or double-clicks there (item is nil): gives {:ok, [id]}
of the node that new_node made. parent: group drops it in a group,
as a drop on the group does.
Duplicates nodes, as ⌘D does: gives {:ok, ids} of the copies.
The edges of the canvas id: maps with :id, :source,
:source_handle, :target, :target_handle, :path, :label,
:marker and :animated.
@spec edit(struct(), String.t(), String.t() | [String.t()], String.t(), map()) :: :ok | {:error, String.t()}
Edits a kit's node, as its controls do (Hakushi.Kit): the action,
with the event's params ("value", a column's index, ...). A list of
nodes is edited in one change, as the style panel of a kit does
(Hakushi.Kit.styles/1).
edit(view, "model", "orders", "add")
edit(view, "board", ["a", "b"], "style", %{"style" => %{"fill" => "red"}})
edit(view, "model", "orders", "column", %{"column" => 1, "value" => "customer_id"})
Sets the label of an edge, or takes it away (nil).
@spec move(struct(), String.t(), String.t() | [tuple()], {number(), number()} | nil) :: :ok | {:error, String.t()}
Moves nodes, as a person drags them: {id, x, y} for each, or one
node's id and {x, y}. A tuple {id, x, y, width, height} resizes it
too. A map %{id: id, x: x, y: y, parent: group} drops it into a group
(or out of one, with parent: nil), as a drop on a group does.
The nodes of the canvas id, in the order they are drawn: maps with
:id, :type, :x, :y, :width, :height (nil for the size of
the content), :locked, :parent (the group it is in, or nil) and
:label (the name screen readers say).
The words the canvas shows for the last change it refused, or nil.
@spec paper(struct(), String.t()) :: %{background: String.t(), grid: pos_integer()}
The paper the canvas shows: %{background: name, grid: size}, its
graph's when people chose one, else its own (Hakushi.Paper).
@spec paper(struct(), String.t(), String.t(), pos_integer()) :: :ok | {:error, String.t()}
Chooses the canvas's paper with its picker (papers), as a person does:
a paper it offers, by name, and the size of its squares (one of
Hakushi.Paper.grids/0).
:ok = paper(view, "board", "lines", 40)
The people the canvas id shows on a shared canvas (not the person
of this LiveView): their ids.
Moves an end of an edge to another handle, as a person drags the end of
a selected edge: source: or target:, a node's id or
{node, handle}.
Does again the last change undone, as ⇧⌘Z does.
Deletes nodes (with their edges) and edges, as the Delete key does:
nodes: ids, edges: ids. (Not delete, which Phoenix.ConnTest has.)
Undoes the last change of this LiveView's person, as ⌘Z does.