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. form/3 view |> form("#attendee-form", %{ email: "gandalf@thefellowship.com" }) %Element{ selector:

    "#attendee-form", form_data: %{ email: "gandalf@thefellowship.com" } }
  15. form/3 view |> form("#attendee-form", %{ email: "gandalf@thefellowship.com" }) %Element{ selector:

    "#attendee-form", form_data: %{ email: "gandalf@thefellowship.com" } }
  16. 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
  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. 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
  22. 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
  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. 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
  25. 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
  26. 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>
  27. !

  28. 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
  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. 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
  31. 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
  32. 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
  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. 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
  38. 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
  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. 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
  45. 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
  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. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. 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
  56. 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
  57. 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
  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. 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>
  60. 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>
  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. 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
  63. 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
  64. 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
  65. 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
  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") # 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
  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") # 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
  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) # 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
  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) # 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
  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 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
  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 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
  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-#{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
  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-#{todo.id}-details [data-role=todo-notes]", notes) assert has_element?(view, "#todo-#{todo.id} [data-role=notes-icon]") end
  86. 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
  87. 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
  88. Effective LiveView Testing Write tests from the perspective of the

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

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