Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Testing LiveView

Testing LiveView

Curious how to test LiveView?

Look no further! We’ll learn to write effective LiveView tests using our application's domain language. We'll use the exceptional tools built into LiveViewTest. And we'll see how our LiveView tests can to teach us about the design of our LiveViews. So join me, and let's take a deep dive into LiveView testing.

Key takeaway:

Write tests from the perspective of the user (testing behavior, not implementation) in the domain of your application.

Links mentioned in the talk:

- https://hexdocs.pm/phoenix_live_view/Phoenix.LiveViewTest.html
- https://www.testingliveview.com/
- https://www.tddphoenix.com/
- https://twitter.com/germsvel

German Velasco

September 04, 2020
Tweet

More Decks by German Velasco

Other Decks in Programming

Transcript

  1. Phoenix.LiveViewTest josevalim - chrismccord - preciz alexgaribay - SherSpock -

    wojtekmach nthock - NikitaAvvakumov - sneako mcrumm - leandrocp - jmbejar henrik - JonRowe - feliperenan orthodoX - davydog187 - qrede cadebward - axelclark - glennr adamvaughan - SolbiatiAlessandro rhruiz - hasmanyguitars - sevenseacat LostKobrakai - egze - dvic
  2. Learn by doing → How to write LiveView tests →

    Testing the right things → Writing explicit tests
  3. Anatomy of a test {:ok, view, _html} = live(conn, "/todo")

    rendered = view |> element("li", "Hold council of Elrond") |> render_click() assert rendered =~ "Tell tale of Elendil" assert has_element?(view, "p", "Tell tale of Elendil")
  4. Anatomy of a test {:ok, view, _html} = live(conn, "/todo")

    rendered = view |> element("li", "Hold council of Elrond") |> render_click() assert rendered =~ "Tell tale of Elendil" assert has_element?(view, "p", "Tell tale of Elendil")
  5. Anatomy of a test {:ok, view, _html} = live(conn, "/todo")

    rendered = view |> element("li", "Hold council of Elrond") |> render_click() assert rendered =~ "Tell tale of Elendil" assert has_element?(view, "p", "Tell tale of Elendil")
  6. Anatomy of a test {:ok, view, _html} = live(conn, "/todo")

    rendered = view |> element("li", "Hold council of Elrond") |> render_click() assert rendered =~ "Tell tale of Elendil" assert has_element?(view, "p", "Tell tale of Elendil")
  7. Anatomy of a test {:ok, view, _html} = live(conn, "/todo")

    rendered = view |> element("li", "Hold council of Elrond") |> render_click() assert rendered =~ "Tell tale of Elendil" assert has_element?(view, "p", "Tell tale of Elendil")
  8. Anatomy of a test {:ok, view, _html} = live(conn, "/todo")

    rendered = view |> element("li", "Hold council of Elrond") |> render_click() assert rendered =~ "Tell tale of Elendil" assert has_element?(view, "p", "Tell tale of Elendil")
  9. Anatomy of a test {:ok, view, _html} = live(conn, "/todo")

    rendered = view |> element("li", "Hold council of Elrond") |> render_click() assert rendered =~ "Tell tale of Elendil" assert has_element?(view, "p", "Tell tale of Elendil")
  10. live/2 {:ok, view, html} = live(conn, "/todo") assert html =~

    "Your Todo" assert render(view) =~ "Your Todo"
  11. live/2 {:ok, view, html} = live(conn, "/todo") assert html =~

    "Your Todo" assert render(view) =~ "Your Todo"
  12. live/2 {:ok, view, html} = live(conn, "/todo") assert html =~

    "Your Todo" assert render(view) =~ "Your Todo"
  13. live/2 {:ok, view, html} = live(conn, "/todo") assert html =~

    "Your Todo" assert render(view) =~ "Your Todo"
  14. render_* functions render_blur/2 render_blur/3 render_change/2 render_change/3 render_click/2 render_click/3 render_focus/2 render_focus/3

    render_keydown/2 render_keydown/3 render_keyup/2 render_keyup/3 render_submit/2 render_submit/3
  15. Adding a new todo test "user can add a new

    todo", %{conn: conn} do {:ok, view, _html} = live(conn, "/todo") view |> form("#new-todo", todo: %{text: "Decide fate of ring"}) |> render_submit() assert has_element?(view, ".todo", "Decide fate of ring") end
  16. Adding a new todo test "user can add a new

    todo", %{conn: conn} do {:ok, view, _html} = live(conn, "/todo") view |> form("#new-todo", todo: %{text: "Decide fate of ring"}) |> render_submit() assert has_element?(view, ".todo", "Decide fate of ring") end
  17. Adding a new todo test "user can add a new

    todo", %{conn: conn} do {:ok, view, _html} = live(conn, "/todo") view |> form("#new-todo", todo: %{text: "Decide fate of ring"}) |> render_submit() assert has_element?(view, ".todo", "Decide fate of ring") end
  18. Adding a new todo test "user can add a new

    todo", %{conn: conn} do {:ok, view, _html} = live(conn, "/todo") view |> form("#new-todo", todo: %{text: "Decide fate of ring"}) |> render_submit() assert has_element?(view, ".todo", "Decide fate of ring") end
  19. Adding a new todo test "user can add a new

    todo", %{conn: conn} do {:ok, view, _html} = live(conn, "/todo") view |> form("#new-todo", todo: %{text: "Decide fate of ring"}) |> render_submit() assert has_element?(view, ".todo", "Decide fate of ring") end
  20. Adding a new todo test "user can add a new

    todo", %{conn: conn} do {:ok, view, _html} = live(conn, "/todo") view |> form("#new-todo", todo: %{text: "Decide fate of ring"}) |> render_submit() assert has_element?(view, ".todo", "Decide fate of ring") end
  21. render_submit/3 test "user can add a new todo", %{conn: conn}

    do {:ok, view, _html} = live(conn, "/todo") view - |> form("#new-todo", todo: %{text: "Decide fate of ring"}) - |> render_submit() + |> render_submit("create-todo", todo: %{text: "Decide fate of ring"}) assert has_element?(view, ".todo", "Decide fate of ring") end
  22. render_submit/3 test "user can add a new todo", %{conn: conn}

    do {:ok, view, _html} = live(conn, "/todo") view - |> form("#new-todo", todo: %{text: "Decide fate of ring"}) - |> render_submit() + |> render_submit("create-todo", todo: %{text: "Decide fate of ring"}) assert has_element?(view, ".todo", "Decide fate of ring") end
  23. render_submit/3 test "user can add a new todo", %{conn: conn}

    do {:ok, view, _html} = live(conn, "/todo") view - |> form("#new-todo", todo: %{text: "Decide fate of ring"}) - |> render_submit() + |> render_submit("create-todo", todo: %{text: "Decide fate of ring"}) assert has_element?(view, ".todo", "Decide fate of ring") end
  24. You could delete the entire form - <%= f =

    form_for @changeset, "#", - phx_submit: "create-todo", - id: "new-todo" %> - - <%= text_input f, :text, placeholder: "Enter todo here" %> - <%= submit "Add todo" %> - </form>
  25. !

  26. Adding a new todo test "user can add a new

    todo", %{conn: conn} do {:ok, view, _html} = live(conn, "/todo") view |> form("#new-todo", todo: %{text: "Decide fate of ring"}) |> render_submit() assert has_element?(view, ".todo", "Decide fate of ring") end
  27. Asserting text test "user can add a new todo", %{conn:

    conn} do {:ok, view, _html} = live(conn, "/todo") + rendered = view |> form("#new-todo", todo: %{text: "Decide fate of ring"}) |> render_submit() - assert has_element?(view, ".todo", "Decide fate of ring") + assert rendered =~ "Decide fate of ring" end
  28. Asserting text test "user can add a new todo", %{conn:

    conn} do {:ok, view, _html} = live(conn, "/todo") + rendered = view |> form("#new-todo", todo: %{text: "Decide fate of ring"}) |> render_submit() - assert has_element?(view, ".todo", "Decide fate of ring") + assert rendered =~ "Decide fate of ring" end
  29. Asserting text test "user can add a new todo", %{conn:

    conn} do {:ok, view, _html} = live(conn, "/todo") + rendered = view |> form("#new-todo", todo: %{text: "Decide fate of ring"}) |> render_submit() - assert has_element?(view, ".todo", "Decide fate of ring") + assert rendered =~ "Decide fate of ring" end
  30. Adding a new todo test "user can add a new

    todo", %{conn: conn} do {:ok, view, _html} = live(conn, "/todo") view |> form("#new-todo", todo: %{text: "Decide fate of ring"}) |> render_submit() assert has_element?(view, ".todo", "Decide fate of ring") end
  31. Showing a todo's details test "selecting a todo opens todo

    details panel", %{conn: conn} do todo = create_todo(text: "Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() assert has_element?(view, "#todo-#{todo.id}-details") end
  32. Showing a todo's details test "selecting a todo opens todo

    details panel", %{conn: conn} do todo = create_todo(text: "Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() assert has_element?(view, "#todo-#{todo.id}-details") end
  33. Showing a todo's details test "selecting a todo opens todo

    details panel", %{conn: conn} do todo = create_todo(text: "Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() assert has_element?(view, "#todo-#{todo.id}-details") end
  34. Showing a todo's details test "selecting a todo opens todo

    details panel", %{conn: conn} do todo = create_todo(text: "Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() assert has_element?(view, "#todo-#{todo.id}-details") end
  35. Showing a todo's details test "selecting a todo opens todo

    details panel", %{conn: conn} do todo = create_todo(text: "Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() assert has_element?(view, "#todo-#{todo.id}-details") end
  36. Showing a todo's details test "selecting a todo opens todo

    details panel", %{conn: conn} do todo = create_todo(text: "Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() assert has_element?(view, "#todo-#{todo.id}-details") end
  37. Testing the details defmodule MyAppWeb.TodoComponentTest do use MyAppWeb.ConnCase import Phoenix.LiveViewTest

    alias MyAppWeb.TodoComponent test "renders todo with notes" do todo = create_todo(text: "Decide fate of ring", notes: "Cannot be destroyed by axe") html = render_component(TodoComponent, todo: todo) assert html =~ todo.text assert html =~ todo.notes end end
  38. Testing the details defmodule MyAppWeb.TodoComponentTest do use MyAppWeb.ConnCase import Phoenix.LiveViewTest

    alias MyAppWeb.TodoComponent test "renders todo with notes" do todo = create_todo(text: "Decide fate of ring", notes: "Cannot be destroyed by axe") html = render_component(TodoComponent, todo: todo) assert html =~ todo.text assert html =~ todo.notes end end
  39. Testing the details defmodule MyAppWeb.TodoComponentTest do use MyAppWeb.ConnCase import Phoenix.LiveViewTest

    alias MyAppWeb.TodoComponent test "renders todo with notes" do todo = create_todo(text: "Decide fate of ring", notes: "Cannot be destroyed by axe") html = render_component(TodoComponent, todo: todo) assert html =~ todo.text assert html =~ todo.notes end end
  40. Testing the details defmodule MyAppWeb.TodoComponentTest do use MyAppWeb.ConnCase import Phoenix.LiveViewTest

    alias MyAppWeb.TodoComponent test "renders todo with notes" do todo = create_todo(text: "Decide fate of ring", notes: "Cannot be destroyed by axe") html = render_component(TodoComponent, todo: todo) assert html =~ todo.text assert html =~ todo.notes end end
  41. Testing the details defmodule MyAppWeb.TodoComponentTest do use MyAppWeb.ConnCase import Phoenix.LiveViewTest

    alias MyAppWeb.TodoComponent test "renders todo with notes" do todo = create_todo(text: "Decide fate of ring", notes: "Cannot be destroyed by axe") html = render_component(TodoComponent, todo: todo) assert html =~ todo.text assert html =~ todo.notes end end
  42. Testing the details defmodule MyAppWeb.TodoComponentTest do use MyAppWeb.ConnCase import Phoenix.LiveViewTest

    alias MyAppWeb.TodoComponent test "renders todo with notes" do todo = create_todo(text: "Decide fate of ring", notes: "Cannot be destroyed by axe") html = render_component(TodoComponent, todo: todo) assert html =~ todo.text assert html =~ todo.notes end end
  43. Testing the details defmodule MyAppWeb.TodoComponentTest do use MyAppWeb.ConnCase import Phoenix.LiveViewTest

    alias MyAppWeb.TodoComponent test "renders todo with notes" do todo = create_todo(text: "Decide fate of ring", notes: "Cannot be destroyed by axe") html = render_component(TodoComponent, todo: todo) assert html =~ todo.text assert html =~ todo.notes end end
  44. Marking a todo as complete test "user can mark todo

    as complete", %{conn: conn} do todo = create_todo("Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} input[name=todo]") |> render_click() assert has_element?(view, "s", "Hold council of Elrond") end
  45. Marking a todo as complete test "user can mark todo

    as complete", %{conn: conn} do todo = create_todo("Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} input[name=todo]") |> render_click() assert has_element?(view, "s", "Hold council of Elrond") end
  46. Marking a todo as complete test "user can mark todo

    as complete", %{conn: conn} do todo = create_todo("Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} input[name=todo]") |> render_click() assert has_element?(view, "s", "Hold council of Elrond") end
  47. Marking a todo as complete test "user can mark todo

    as complete", %{conn: conn} do todo = create_todo("Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} input[name=todo]") |> render_click() assert has_element?(view, "s", "Hold council of Elrond") end
  48. Marking a todo as complete test "user can mark todo

    as complete", %{conn: conn} do todo = create_todo("Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} input[name=todo]") |> render_click() assert has_element?(view, "s", "Hold council of Elrond") end
  49. Marking a todo as complete test "user can mark todo

    as complete", %{conn: conn} do todo = create_todo("Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} input[name=todo]") |> render_click() assert has_element?(view, "s", "Hold council of Elrond") end
  50. Assert database changes? test "user can mark todo as complete",

    %{conn: conn} do {:ok, todo} = create_todo("Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} input[name=todo]") |> render_click() assert has_element?(view, "s", "Hold council of Elrond") updated_todo = Repo.get(MyApp.Todo, todo.id) assert updated_todo.complete end
  51. Assert database changes? test "user can mark todo as complete",

    %{conn: conn} do {:ok, todo} = create_todo("Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} input[name=todo]") |> render_click() assert has_element?(view, "s", "Hold council of Elrond") updated_todo = Repo.get(MyApp.Todo, todo.id) assert updated_todo.complete end
  52. Marking a todo as complete test "user can mark todo

    as complete", %{conn: conn} do todo = create_todo("Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} input[name=todo]") |> render_click() assert has_element?(view, "s", "Hold council of Elrond") end
  53. But how to test this logic? def handle_event("complete-todo", %{"value" =>

    todo_id}, socket) do Todo |> Repo.get(todo_id) |> Todo.changeset(%{complete: true}) |> Repo.update!() {:noreply, assign(socket, :todos, Todos.get_all())} end
  54. But how to test this logic? def handle_event("complete-todo", %{"value" =>

    todo_id}, socket) do Todo |> Repo.get(todo_id) |> Todo.changeset(%{complete: true}) |> Repo.update!() {:noreply, assign(socket, :todos, Todos.get_all())} end
  55. Extract logic def handle_event("complete-todo", %{"value" => todo_id}, socket) do -

    Todo - |> Repo.get(todo_id) - |> Todo.changeset(%{complete: true}) - |> Repo.update!() + Todos.complete_todo(todo_id) {:noreply, assign(socket, :todos, Todos.get_all())} end
  56. Implementation Changes <span - phx-click="show-todo" - phx-value-id="<%= todo.id %>" -

    data-role="todo-text"> - - <%= todo.text %> + <%= live_patch todo.text, + to: Routes.todo_path(@socket, :show, todo), + data: [role: "todo-text"] + %> </span>
  57. Implementation Changes <span - phx-click="show-todo" - phx-value-id="<%= todo.id %>" -

    data-role="todo-text"> - - <%= todo.text %> + <%= live_patch todo.text, + to: Routes.todo_path(@socket, :show, todo), + data: [role: "todo-text"] + %> </span>
  58. Implementation Changes <span - phx-click="show-todo" - phx-value-id="<%= todo.id %>" -

    data-role="todo-text"> - - <%= todo.text %> + <%= live_patch todo.text, + to: Routes.todo_path(@socket, :show, todo), + data: [role: "todo-text"] + %> </span>
  59. Change to assert_patched/2? test "selecting a todo opens todo details

    panel", %{conn: conn} do todo = create_todo(text: "Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() - assert has_element?(view, "#todo-#{todo.id}-details") + assert_patched(view, "/todo/#{todo.id}") end
  60. Change to assert_patched/2? test "selecting a todo opens todo details

    panel", %{conn: conn} do todo = create_todo(text: "Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() - assert has_element?(view, "#todo-#{todo.id}-details") + assert_patched(view, "/todo/#{todo.id}") end
  61. Change to assert_patched/2? test "selecting a todo opens todo details

    panel", %{conn: conn} do todo = create_todo(text: "Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() - assert has_element?(view, "#todo-#{todo.id}-details") + assert_patched(view, "/todo/#{todo.id}") end
  62. What does the user see? Existing behavior New behavior clicking

    on todo's text opens details panel clicking on todo's text opens details panel
  63. Test is unchanged test "selecting a todo opens todo details

    panel", %{conn: conn} do todo = create_todo(text: "Hold council of Elrond") {:ok, view, _html} = live(conn, "/todo") view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() assert has_element?(view, "#todo-#{todo.id}-details") end
  64. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") # Opening todo details view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  65. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") # Opening todo details view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  66. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") # Opening todo details view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  67. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") # Opening todo details view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  68. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") # Opening todo details view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  69. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") # Opening todo details view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  70. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") # Opening todo details view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  71. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") # Opening todo details view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  72. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") # Opening todo details view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  73. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") # Opening todo details view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  74. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") # Opening todo details view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  75. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") # Opening todo details view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  76. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") # Opening todo details view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  77. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") # Opening todo details view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  78. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") view |> open_todo_details(todo) # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end defp open_todo_details(view, todo) do view |> element("#todo-#{todo.id} [data-role=todo-text]", todo.text) |> render_click() view end
  79. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") view |> open_todo_details(todo) # Opening editable notes view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  80. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") view |> open_todo_details(todo) |> edit_notes(todo) # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end defp edit_notes(view, todo) do view |> element("#todo-#{todo.id}-details [data-role=edit-todo-notes]", "Edit notes") |> render_click() view end
  81. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") view |> open_todo_details(todo) |> edit_notes(todo) # Add notes view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  82. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") view |> open_todo_details(todo) |> edit_notes(todo) |> add_notes(todo, notes) assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end defp add_notes(view, todo, notes) do view |> form("#todo-#{todo.id}-details [data-role=update-todo-notes]", todo: %{notes: notes}) |> render_submit() view end
  83. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") view |> open_todo_details(todo) |> edit_notes(todo) |> add_notes(todo, notes) assert has_element?(view, "#todo-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  84. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") view |> open_todo_details(todo) |> edit_notes(todo) |> add_notes(todo, notes) assert has_element?(view, todo_details(todo), notes) assert has_element?(view, todo_notes_icon(todo)) end defp todo_details(todo) do "#todo-#{todo.id}-details [data-role=todo-notes]" end defp todo_notes_icon(todo) do "#todo-#{todo.id} [data-role=notes-icon]" end
  85. test "user can edit notes", %{conn: conn} do todo =

    create_todo(text: "Decide fate of the ring") notes = "We might have to cast it into mount doom" {:ok, view, _html} = live(conn, "/todo") view |> open_todo_details(todo) |> edit_notes(todo) |> add_notes(todo, notes) assert has_element?(view, todo_details(todo), notes) assert has_element?(view, todo_notes_icon(todo)) end
  86. Effective LiveView Testing Write tests from the perspective of the

    user (testing behavior, not implementation)
  87. Effective LiveView Testing Write tests from the perspective of the

    user (testing behavior, not implementation) in the domain of your application.