diff --git a/backlog/tasks/ml-182.5 - Wave-5-Files-with-sendview.pid-and-live_isolated-blockers-4-files.md b/backlog/tasks/ml-182.5 - Wave-5-Files-with-sendview.pid-and-live_isolated-blockers-4-files.md index c43fb9bc..8b948fd1 100644 --- a/backlog/tasks/ml-182.5 - Wave-5-Files-with-sendview.pid-and-live_isolated-blockers-4-files.md +++ b/backlog/tasks/ml-182.5 - Wave-5-Files-with-sendview.pid-and-live_isolated-blockers-4-files.md @@ -1,9 +1,11 @@ --- id: ML-182.5 title: "Wave 5: Files with send(view.pid) and live_isolated blockers (4 files)" -status: To Do -assignee: [] +status: Done +assignee: + - Codex created_date: "2026-05-14 22:15" +updated_date: "2026-05-17 20:18" labels: - testing - refactoring @@ -35,3 +37,46 @@ Blocking patterns: 2. `live_isolated/3` — no PhoenixTest equivalent; test through parent page 3. `render_change/2` with nested params — `fill_in` works for individual fields but not bulk nested changes + +## Implementation Plan + + + +Approved implementation plan: + +1. Convert index PubSub tests in `collection_live/index_test.exs` and `wishlist_live/index_test.exs` from raw `LiveViewTest.live/2` plus `send(view.pid, :records_index_changed)` to PhoenixTest `visit/2` sessions plus the real `MusicLibrary.Records.broadcast_index_changed/0`. Assert the newly-created record appears on `:index` pages and remains absent while the import modal action ignores the message. +2. Convert remaining raw `live/2` import-flow tests in collection/wishlist to PhoenixTest pipelines (`visit`, `fill_in`, `click_link`, `click_button`, `assert_path`) and use DB assertions for the dynamically-created redirected record. +3. For cart format-change and Release component nested-form interactions, use PhoenixTest as the outer session and keep the smallest `unwrap/2` bridge only for the label-less/nested LiveComponent form change that PhoenixTest cannot express directly. +4. Replace `release_test.exs` `live_isolated/3` coverage with parent LiveView coverage: `CollectionLive.Show` for `show_print?: true` and `ScrobbleLive.ReleaseShow` for `show_print?: false`. +5. Convert `record_actions_test.exs` to PhoenixTest `visit/2` where practical; for the chat-count test, trigger the real Chat component `send_message` flow instead of directly sending `{Chat, :chats_changed}` to the LiveView PID. +6. Run focused tests for the four task files, then `mix format` on changed files. Update the task notes/final summary and check any acceptance-style completion evidence even though this task has no explicit acceptance criteria. + + +## Implementation Notes + + + +Implemented the approved PhoenixTest migration plan for the four scoped files. Direct LiveView PID messaging was replaced with real PubSub broadcasts or UI/component flows; `live_isolated/3` Release coverage was replaced with routed parent LiveViews; raw `live/2` import flows were converted to PhoenixTest session pipelines. Kept small `unwrap/2` bridges only for interactions PhoenixTest cannot express directly: cart native-select `phx-change`, Release component nested form changes, record action event dispatches, and Chat component form submit. + + + +## Final Summary + + + +Converted the ML-182.5 test files away from raw LiveView PID/isolation patterns while preserving the existing behavioral coverage. + +Changed: + +- `collection_live/index_test.exs`: PubSub tests now use `Records.broadcast_index_changed/0`; import flows use PhoenixTest session pipelines; cart format changes keep a narrow `unwrap/2` helper for the component form change. +- `wishlist_live/index_test.exs`: PubSub and single-item import tests now use PhoenixTest-style navigation and async path assertions. +- `record_actions_test.exs`: page-level tests now mount with `visit/2`; the chat-count test triggers the real Chat component submit flow instead of `send(view.pid, ...)`. +- `components/release_test.exs`: removed the isolated host LiveView and replaced `show_print?` coverage with `CollectionLive.Show` and `ScrobbleLive.ReleaseShow` parent pages. + +Validation: + +- `mix format test/music_library_web/live/collection_live/index_test.exs test/music_library_web/live/wishlist_live/index_test.exs test/music_library_web/live_helpers/record_actions_test.exs test/music_library_web/components/release_test.exs` +- `mix test test/music_library_web/live/collection_live/index_test.exs test/music_library_web/live/wishlist_live/index_test.exs test/music_library_web/live_helpers/record_actions_test.exs test/music_library_web/components/release_test.exs --max-failures 5` -> 52 passed +- Blocker scan for `live_isolated`, `send(view.pid)`, raw `LiveViewTest.live`, `assert_redirect`, and `LVT` returned no matches in the scoped files. +- `git diff --check` passed. + diff --git a/test/music_library_web/components/release_test.exs b/test/music_library_web/components/release_test.exs index 3f6267cd..766eda07 100644 --- a/test/music_library_web/components/release_test.exs +++ b/test/music_library_web/components/release_test.exs @@ -12,7 +12,7 @@ defmodule MusicLibraryWeb.Components.ReleaseTest do import MusicLibrary.Fixtures.Records import Phoenix.LiveViewTest, - only: [element: 2, render: 1, render_change: 2, render_click: 1] + only: [element: 2, render_change: 2, render_click: 1] alias MusicBrainz.Fixtures.Release, as: ReleaseFixtures alias MusicLibrary.Secrets @@ -289,45 +289,18 @@ defmodule MusicLibraryWeb.Components.ReleaseTest do end end -defmodule ReleaseComponentHost do - @moduledoc false - use MusicLibraryWeb, :live_view - - @impl true - def mount(_params, session, socket) do - {:ok, - assign(socket, - release_id: session["release_id"], - show_print?: session["show_print?"], - timezone: "UTC" - )} - end - - @impl true - def render(assigns) do - ~H""" -
- <.live_component - id="host-release" - module={MusicLibraryWeb.Components.Release} - release_id={@release_id} - show_print?={@show_print?} - sheet_id="host-sheet" - timezone={@timezone} - /> -
- """ - end -end - defmodule MusicLibraryWeb.Components.ReleaseTest.ShowPrintTest do use MusicLibraryWeb.ConnCase, async: false - import Phoenix.LiveViewTest, only: [render: 1] + import MusicLibrary.Fixtures.Records alias MusicBrainz.Fixtures.Release, as: ReleaseFixtures + alias MusicBrainz.Fixtures.ReleaseGroup alias Req.Test + @rg_id ReleaseGroup.release_group_id(:marbles) + @release_id ReleaseFixtures.release_id(:marbles) + defp stub_musicbrainz_release(_) do Test.stub(MusicBrainz.API, fn conn -> case conn.request_path do @@ -346,31 +319,19 @@ defmodule MusicLibraryWeb.Components.ReleaseTest.ShowPrintTest do setup [:stub_musicbrainz_release] test "true renders Print tracklist dropdown entries", %{conn: conn} do - {:ok, view, _html} = - Phoenix.LiveViewTest.live_isolated(conn, ReleaseComponentHost, - session: %{ - "release_id" => ReleaseFixtures.release_id(:marbles), - "show_print?" => true - } - ) + record = record() - render_async(view) - - assert render(view) =~ "Print tracklist" + conn + |> visit(~p"/collection/#{record.id}") + |> render_async() + |> assert_has("a", text: "Print tracklist") end test "false hides Print tracklist dropdown entries", %{conn: conn} do - {:ok, view, _html} = - Phoenix.LiveViewTest.live_isolated(conn, ReleaseComponentHost, - session: %{ - "release_id" => ReleaseFixtures.release_id(:marbles), - "show_print?" => false - } - ) - - render_async(view) - - refute render(view) =~ "Print tracklist" + conn + |> visit(~p"/scrobble/#{@rg_id}/releases/#{@release_id}") + |> render_async() + |> refute_has("a", text: "Print tracklist") end end end diff --git a/test/music_library_web/live/collection_live/index_test.exs b/test/music_library_web/live/collection_live/index_test.exs index e9009266..88052769 100644 --- a/test/music_library_web/live/collection_live/index_test.exs +++ b/test/music_library_web/live/collection_live/index_test.exs @@ -6,13 +6,12 @@ defmodule MusicLibraryWeb.CollectionLive.IndexTest do import MusicBrainz.Fixtures.ReleaseGroup import MusicLibrary.Fixtures.Records import MusicLibraryWeb.RecordComponents, only: [format_label: 1, type_label: 1] - import Phoenix.LiveViewTest, only: [assert_redirect: 2] alias MusicLibrary.Assets alias MusicLibrary.Assets.{Image, Transform} + alias MusicLibrary.Records alias MusicLibrary.Records.Record alias MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroup - alias Phoenix.LiveViewTest alias Req.Test # make it a multiple of 4 for easier calculations @@ -24,6 +23,14 @@ defmodule MusicLibraryWeb.CollectionLive.IndexTest do %{collection: records} end + defp change_cart_format(session, selector, params) do + unwrap(session, fn view -> + view + |> element(selector) + |> Phoenix.LiveViewTest.render_change(params) + end) + end + describe "Collection" do setup [:fill_collection] @@ -96,32 +103,27 @@ defmodule MusicLibraryWeb.CollectionLive.IndexTest do describe "PubSub index_changed" do test "reloads stream when live_action is :index", %{conn: conn} do - {:ok, view, _html} = LiveViewTest.live(conn, ~p"/collection") - - html_before = LiveViewTest.render(view) + session = visit(conn, ~p"/collection") # Create a new record behind the scenes (simulating completed background import) - _new_record = record() + new_record = record() - # Send the index_changed message directly - send(view.pid, :records_index_changed) + refute_has(session, "#records-#{new_record.id}") - # The view should now include the new record - html_after = LiveViewTest.render(view) - assert html_after != html_before + Records.broadcast_index_changed() + + assert_has(session, "#records-#{new_record.id}", timeout: 200) end test "ignores message when live_action is :import (guard clause)", %{conn: conn} do - {:ok, view, _html} = LiveViewTest.live(conn, ~p"/collection/import") + session = visit(conn, ~p"/collection/import") - html_before = LiveViewTest.render(view) + new_record = record() - send(view.pid, :records_index_changed) + Records.broadcast_index_changed() - html_after = LiveViewTest.render(view) - - # Should be identical — the message is a no-op when grid is behind modal - assert html_after == html_before + # The message is a no-op when the grid is behind the import modal. + refute_has(session, "#records-#{new_record.id}", timeout: 100) end end @@ -384,65 +386,37 @@ defmodule MusicLibraryWeb.CollectionLive.IndexTest do end test "changes the format of a cart item", %{conn: conn} do - alias Phoenix.LiveViewTest, as: LVT - stub_release_group_search() [first | _] = Map.get(release_group_search_results(), "release-groups") first_id = first["id"] - {:ok, view, _html} = LVT.live(conn, ~p"/collection/import") - - view - |> LVT.form("#import_form", %{"mb_query" => "Marillion Marbles"}) - |> LVT.render_change() - - view - |> LVT.element("#musicbrainz_#{first_id} a", "CD") - |> LVT.render_click() - - view - |> LVT.element("#cart-items form") - |> LVT.render_change(%{"format" => "vinyl"}) - - assert LVT.render(view) =~ "In cart" + conn + |> visit(~p"/collection/import") + |> fill_in("Search for a record", with: "Marillion Marbles") + |> click_link("#musicbrainz_#{first_id} a", "CD") + |> change_cart_format("#cart-items form", %{"format" => "vinyl"}) + |> assert_has("#musicbrainz_#{first_id} span", text: "In cart") end test "rejects change_format when the resulting pair is already in the cart", %{conn: conn} do - alias Phoenix.LiveViewTest, as: LVT - stub_release_group_search() [first | _] = Map.get(release_group_search_results(), "release-groups") first_id = first["id"] - {:ok, view, _html} = LVT.live(conn, ~p"/collection/import") + session = + conn + |> visit(~p"/collection/import") + |> fill_in("Search for a record", with: "Marillion Marbles") + |> click_link("#musicbrainz_#{first_id} a", "CD") + |> click_link("#musicbrainz_#{first_id} a", "Vinyl") - view - |> LVT.form("#import_form", %{"mb_query" => "Marillion Marbles"}) - |> LVT.render_change() + assert_has(session, "#cart-items li", count: 2) - view - |> LVT.element("#musicbrainz_#{first_id} a", "CD") - |> LVT.render_click() - - view - |> LVT.element("#musicbrainz_#{first_id} a", "Vinyl") - |> LVT.render_click() - - html = LVT.render(view) - cart_item_ids = Regex.scan(~r/id="cart-item-(\d+)"/, html, capture: :all_but_first) - assert length(cart_item_ids) == 2 - - [first_id_match | _] = cart_item_ids - [item_id] = first_id_match - - view - |> LVT.element("#cart-item-#{item_id} form") - |> LVT.render_change(%{"cart_item_id" => item_id, "format" => "vinyl"}) - - html_after = LVT.render(view) - assert Regex.scan(~r/id="cart-item-/, html_after) |> length() == 2 + session + |> change_cart_format("#cart-items li:last-child form", %{"format" => "vinyl"}) + |> assert_has("#cart-items li", count: 2) end test "clears the cart", %{conn: conn} do @@ -460,36 +434,26 @@ defmodule MusicLibraryWeb.CollectionLive.IndexTest do end test "imports a single cart item synchronously and navigates", %{conn: conn} do - alias Phoenix.LiveViewTest, as: LVT - stub_full_import() [first | _] = Map.get(release_group_search_results(), "release-groups") first_id = first["id"] - {:ok, view, _html} = LVT.live(conn, ~p"/collection/import") + session = + conn + |> visit(~p"/collection/import") + |> fill_in("Search for a record", with: "Marillion Marbles") + |> click_link("#musicbrainz_#{first_id} a", "CD") + |> click_button("Import 1 record") + |> assert_path("/collection/*", timeout: 2_000) - view - |> LVT.form("#import_form", %{"mb_query" => "Marillion Marbles"}) - |> LVT.render_change() - - view - |> LVT.element("#musicbrainz_#{first_id} a", "CD") - |> LVT.render_click() - - view - |> LVT.element("button", "Import 1 record") - |> LVT.render_click() - - {path, _flash} = assert_redirect(view, 2_000) - "/collection/" <> record_id = path - - record = MusicLibrary.Records.get_record!(record_id) + [record] = MusicLibrary.Repo.all(Record) assert record.musicbrainz_id == first_id assert record.title == "Marbles" assert record.format == :cd refute is_nil(record.purchased_at) + assert_path(session, ~p"/collection/#{record}") {:ok, resized_cover_data} = Image.resize(marbles_cover_data()) assets = Assets.get(record.cover_hash) diff --git a/test/music_library_web/live/wishlist_live/index_test.exs b/test/music_library_web/live/wishlist_live/index_test.exs index 4dda5c96..49eeed35 100644 --- a/test/music_library_web/live/wishlist_live/index_test.exs +++ b/test/music_library_web/live/wishlist_live/index_test.exs @@ -5,11 +5,10 @@ defmodule MusicLibraryWeb.WishlistLive.IndexTest do import Ecto.Query, only: [from: 2] import MusicBrainz.Fixtures.ReleaseGroup import MusicLibrary.Fixtures.Records - import Phoenix.LiveViewTest, only: [assert_redirect: 2] + alias MusicLibrary.Records alias MusicLibrary.Records.Record alias MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroup - alias Phoenix.LiveViewTest alias Req.Test defp fill_wishlist(_) do @@ -19,32 +18,27 @@ defmodule MusicLibraryWeb.WishlistLive.IndexTest do describe "PubSub index_changed" do test "reloads stream when live_action is :index", %{conn: conn} do - {:ok, view, _html} = LiveViewTest.live(conn, ~p"/wishlist") - - html_before = LiveViewTest.render(view) + session = visit(conn, ~p"/wishlist") # Create a new wishlist record behind the scenes (simulating completed background import) - _new_record = record(%{purchased_at: nil}) + new_record = record(%{purchased_at: nil}) - # Send the index_changed message directly - send(view.pid, :records_index_changed) + refute_has(session, "#records-#{new_record.id}") - # The view should now include the new record - html_after = LiveViewTest.render(view) - assert html_after != html_before + Records.broadcast_index_changed() + + assert_has(session, "#records-#{new_record.id}", timeout: 200) end test "ignores message when live_action is :import (guard clause)", %{conn: conn} do - {:ok, view, _html} = LiveViewTest.live(conn, ~p"/wishlist/import") + session = visit(conn, ~p"/wishlist/import") - html_before = LiveViewTest.render(view) + new_record = record(%{purchased_at: nil}) - send(view.pid, :records_index_changed) + Records.broadcast_index_changed() - html_after = LiveViewTest.render(view) - - # Should be identical — the message is a no-op when grid is behind modal - assert html_after == html_before + # The message is a no-op when the grid is behind the import modal. + refute_has(session, "#records-#{new_record.id}", timeout: 100) end end @@ -107,36 +101,26 @@ defmodule MusicLibraryWeb.WishlistLive.IndexTest do end test "imports a single cart item synchronously and navigates to wishlist", %{conn: conn} do - alias Phoenix.LiveViewTest, as: LVT - stub_full_import() [first | _] = Map.get(release_group_search_results(), "release-groups") first_id = first["id"] - {:ok, view, _html} = LVT.live(conn, ~p"/wishlist/import") + session = + conn + |> visit(~p"/wishlist/import") + |> fill_in("Search for a record", with: "Marillion Marbles") + |> click_link("#musicbrainz_#{first_id} a", "CD") + |> click_button("Import 1 record") + |> assert_path("/wishlist/*", timeout: 2_000) - view - |> LVT.form("#import_form", %{"mb_query" => "Marillion Marbles"}) - |> LVT.render_change() - - view - |> LVT.element("#musicbrainz_#{first_id} a", "CD") - |> LVT.render_click() - - view - |> LVT.element("button", "Import 1 record") - |> LVT.render_click() - - {path, _flash} = assert_redirect(view, 2_000) - "/wishlist/" <> record_id = path - - record = MusicLibrary.Records.get_record!(record_id) + [record] = MusicLibrary.Repo.all(Record) assert record.musicbrainz_id == first_id assert record.title == "Marbles" assert record.format == :cd assert record.purchased_at == nil + assert_path(session, ~p"/wishlist/#{record}") refute_enqueued(worker: ImportFromMusicbrainzReleaseGroup) end diff --git a/test/music_library_web/live_helpers/record_actions_test.exs b/test/music_library_web/live_helpers/record_actions_test.exs index f1d1352e..afa62b1f 100644 --- a/test/music_library_web/live_helpers/record_actions_test.exs +++ b/test/music_library_web/live_helpers/record_actions_test.exs @@ -4,8 +4,6 @@ defmodule MusicLibraryWeb.LiveHelpers.RecordActionsTest do import MusicLibrary.Fixtures.Records - import Phoenix.LiveViewTest, only: [live: 2, render: 1, render_click: 3] - alias MusicBrainz.Fixtures.Release, as: ReleaseFixtures alias MusicBrainz.Fixtures.ReleaseGroup alias MusicLibrary.Chats @@ -43,15 +41,45 @@ defmodule MusicLibraryWeb.LiveHelpers.RecordActionsTest do end) end + defp visit_record(conn, record) do + visit(conn, ~p"/collection/#{record.id}") + end + + defp trigger_record_action(session, event) do + unwrap(session, fn view -> + render_click(view, event, %{}) + end) + end + + defp submit_record_chat_message(session, message) do + unwrap(session, fn view -> + view + |> form("#record-chat-form", %{"message" => message}) + |> Phoenix.LiveViewTest.render_submit() + end) + end + + defp stub_openai_stream do + body = + "data: #{JSON.encode!(%{type: "response.output_text.delta", delta: "Hello"})}\n\n" <> + "data: #{JSON.encode!(%{type: "response.completed"})}\n\n" + + Test.stub(OpenAI.API, fn conn -> + conn + |> Plug.Conn.put_resp_content_type("text/event-stream") + |> Plug.Conn.send_resp(200, body) + end) + end + describe "refresh_musicbrainz_data event" do test "success path shows confirmation toast", %{conn: conn} do record = record() stub_musicbrainz_happy_path(record.musicbrainz_id) - {:ok, view, _html} = live(conn, ~p"/collection/#{record.id}") - render_click(view, "refresh_musicbrainz_data", %{}) - - assert render(view) =~ "MusicBrainz data refreshed successfully" + conn + |> visit_record(record) + |> trigger_record_action("refresh_musicbrainz_data") + |> assert_has("#toast-group", text: "MusicBrainz data refreshed successfully") end @tag :capture_log @@ -70,11 +98,10 @@ defmodule MusicLibraryWeb.LiveHelpers.RecordActionsTest do end end) - {:ok, view, _html} = live(conn, ~p"/collection/#{record.id}") - render_click(view, "refresh_musicbrainz_data", %{}) - - html = render(view) - assert html =~ "Error refreshing MusicBrainz data" + conn + |> visit_record(record) + |> trigger_record_action("refresh_musicbrainz_data") + |> assert_has("#toast-group", text: "Error refreshing MusicBrainz data") end end @@ -83,10 +110,11 @@ defmodule MusicLibraryWeb.LiveHelpers.RecordActionsTest do record = record() stub_musicbrainz_happy_path(record.musicbrainz_id, cover_data: raven_cover_data()) - {:ok, view, _html} = live(conn, ~p"/collection/#{record.id}") - render_click(view, "refresh_cover", %{}) + conn + |> visit_record(record) + |> trigger_record_action("refresh_cover") + |> assert_has("#toast-group", text: "Cover refreshed successfully") - assert render(view) =~ "Cover refreshed successfully" refute Records.get_record!(record.id).cover_hash == record.cover_hash end @@ -109,10 +137,10 @@ defmodule MusicLibraryWeb.LiveHelpers.RecordActionsTest do end end) - {:ok, view, _html} = live(conn, ~p"/collection/#{record.id}") - render_click(view, "refresh_cover", %{}) - - assert render(view) =~ "Error refreshing cover" + conn + |> visit_record(record) + |> trigger_record_action("refresh_cover") + |> assert_has("#toast-group", text: "Error refreshing cover") end end @@ -121,10 +149,10 @@ defmodule MusicLibraryWeb.LiveHelpers.RecordActionsTest do record = record() stub_musicbrainz_happy_path(record.musicbrainz_id) - {:ok, view, _html} = live(conn, ~p"/collection/#{record.id}") - render_click(view, "populate_genres", %{}) - - assert render(view) =~ "In progress - record will update automatically" + conn + |> visit_record(record) + |> trigger_record_action("populate_genres") + |> assert_has("#toast-group", text: "In progress - record will update automatically") assert_enqueued( worker: MusicLibrary.Worker.PopulateGenres, @@ -138,10 +166,10 @@ defmodule MusicLibraryWeb.LiveHelpers.RecordActionsTest do record = record(%{dominant_colors: []}) stub_musicbrainz_happy_path(record.musicbrainz_id) - {:ok, view, _html} = live(conn, ~p"/collection/#{record.id}") - render_click(view, "extract_colors", %{}) - - assert render(view) =~ "Colors extracted" + conn + |> visit_record(record) + |> trigger_record_action("extract_colors") + |> assert_has("#toast-group", text: "Colors extracted") updated = Records.get_record!(record.id) # FakeColorExtractor returns a fixed 5-color palette @@ -157,10 +185,10 @@ defmodule MusicLibraryWeb.LiveHelpers.RecordActionsTest do {:ok, record} = Records.update_record(record, %{cover_hash: String.duplicate("00", 32)}) stub_musicbrainz_happy_path(record.musicbrainz_id) - {:ok, view, _html} = live(conn, ~p"/collection/#{record.id}") - render_click(view, "extract_colors", %{}) - - assert render(view) =~ "Error extracting colors" + conn + |> visit_record(record) + |> trigger_record_action("extract_colors") + |> assert_has("#toast-group", text: "Error extracting colors") end end @@ -168,22 +196,13 @@ defmodule MusicLibraryWeb.LiveHelpers.RecordActionsTest do test "re-counts chats when the Chat component broadcasts a change", %{conn: conn} do record = record() stub_musicbrainz_happy_path(record.musicbrainz_id) + stub_openai_stream() - {:ok, view, _html} = live(conn, ~p"/collection/#{record.id}") + conn + |> visit_record(record) + |> submit_record_chat_message("hello") + |> assert_has("span.text-xs", text: "1", timeout: 200) - {:ok, _chat} = - Chats.create_chat_with_message( - %{entity: :record, musicbrainz_id: record.musicbrainz_id}, - %{role: "user", content: "hello"} - ) - - # The Chat component would `send(self(), {Chat, :chats_changed})` after - # seeding a chat. We simulate that by sending the same message directly. - send(view.pid, {MusicLibraryWeb.Components.Chat, :chats_changed}) - - # Forcing a re-render flushes the handle_info that re-computes chat_count; - # we verify via the context function which is the authoritative count. - _ = render(view) assert Chats.count_chats(:record, record.musicbrainz_id) == 1 end end @@ -193,7 +212,7 @@ defmodule MusicLibraryWeb.LiveHelpers.RecordActionsTest do record = record() stub_musicbrainz_happy_path(record.musicbrainz_id) - {:ok, view, _html} = live(conn, ~p"/collection/#{record.id}") + session = visit_record(conn, record) updated_record = %{record | title: "Brand New Title"} @@ -203,9 +222,9 @@ defmodule MusicLibraryWeb.LiveHelpers.RecordActionsTest do {:update, updated_record} ) - html = render(view) - assert html =~ "Brand New Title" - assert html =~ "Record updated in the background" + session + |> assert_has("*", text: "Brand New Title", timeout: 200) + |> assert_has("#toast-group", text: "Record updated in the background") end end @@ -219,9 +238,9 @@ defmodule MusicLibraryWeb.LiveHelpers.RecordActionsTest do {:ok, _} = Similarity.store_embedding(record.id, embedding, embedding_text) - {:ok, view, _html} = live(conn, ~p"/collection/#{record.id}") - - assert render(view) =~ "Genres: progressive rock" + conn + |> visit_record(record) + |> assert_has("*", text: "Genres: progressive rock") end # The `{:error, :not_found}` branch is exercised implicitly by every