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