From 90ac4fa23a15b56004bce4dbad1726e7cc2795db Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Sat, 16 May 2026 22:27:46 +0100 Subject: [PATCH] Extract LiveView async test helper --- .../components/chat_test.exs | 4 +- .../components/release_test.exs | 25 ++++++------ .../live/artist_live/show_test.exs | 38 +++++++++++-------- .../live/collection_live/show_test.exs | 6 +-- .../live/maintenance_live/index_test.exs | 4 +- .../live/scrobble_live/release_show_test.exs | 4 +- .../live/stats_live/top_albums_test.exs | 20 +++++----- .../live_helpers/record_actions_test.exs | 3 +- test/support/conn_case.ex | 1 - test/support/live_test_helpers.ex | 34 ++++++++++++++++- 10 files changed, 90 insertions(+), 49 deletions(-) diff --git a/test/music_library_web/components/chat_test.exs b/test/music_library_web/components/chat_test.exs index 649105cc..572cfc55 100644 --- a/test/music_library_web/components/chat_test.exs +++ b/test/music_library_web/components/chat_test.exs @@ -2,7 +2,9 @@ defmodule MusicLibraryWeb.Components.ChatTest do use MusicLibraryWeb.ConnCase, async: false import MusicLibrary.Fixtures.Records - import Phoenix.LiveViewTest + + import Phoenix.LiveViewTest, + only: [element: 2, form: 3, live: 2, render: 1, render_click: 1, render_submit: 1] alias MusicBrainz.Fixtures.Release, as: ReleaseFixtures alias MusicBrainz.Fixtures.ReleaseGroup diff --git a/test/music_library_web/components/release_test.exs b/test/music_library_web/components/release_test.exs index e89f7e35..3f6267cd 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_async: 1, render_change: 2, render_click: 1] + only: [element: 2, render: 1, render_change: 2, render_click: 1] alias MusicBrainz.Fixtures.Release, as: ReleaseFixtures alias MusicLibrary.Secrets @@ -83,7 +83,7 @@ defmodule MusicLibraryWeb.Components.ReleaseTest do conn |> visit(~p"/collection/#{record.id}") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("div[data-part=inner-prefix]", text: "Finished at") end @@ -93,7 +93,7 @@ defmodule MusicLibraryWeb.Components.ReleaseTest do conn |> visit(~p"/collection/#{record.id}") - |> unwrap(&render_async/1) + |> render_async() |> unwrap(fn view -> view |> element("button[phx-click=scrobble_release][phx-target]") @@ -116,7 +116,7 @@ defmodule MusicLibraryWeb.Components.ReleaseTest do conn |> visit(~p"/collection/#{record.id}") - |> unwrap(&render_async/1) + |> render_async() |> unwrap(fn view -> view |> element(@sheet_form) @@ -136,7 +136,7 @@ defmodule MusicLibraryWeb.Components.ReleaseTest do conn |> visit(~p"/collection/#{record.id}") - |> unwrap(&render_async/1) + |> render_async() |> unwrap(fn view -> view |> element(@sheet_form) @@ -172,7 +172,7 @@ defmodule MusicLibraryWeb.Components.ReleaseTest do conn |> visit(~p"/collection/#{record.id}") - |> unwrap(&render_async/1) + |> render_async() |> unwrap(fn view -> view |> element(@sheet_form) @@ -197,7 +197,7 @@ defmodule MusicLibraryWeb.Components.ReleaseTest do conn |> visit(~p"/collection/#{record.id}") - |> unwrap(&render_async/1) + |> render_async() |> unwrap(fn view -> view |> element(@sheet_form) @@ -221,7 +221,7 @@ defmodule MusicLibraryWeb.Components.ReleaseTest do conn |> visit(~p"/collection/#{record.id}") - |> unwrap(&render_async/1) + |> render_async() |> refute_has("button", text: "Scrobble selected") end @@ -232,7 +232,7 @@ defmodule MusicLibraryWeb.Components.ReleaseTest do conn |> visit(~p"/collection/#{record.id}") - |> unwrap(&render_async/1) + |> render_async() |> unwrap(fn view -> view |> element(@sheet_form) @@ -250,7 +250,7 @@ defmodule MusicLibraryWeb.Components.ReleaseTest do conn |> visit(~p"/collection/#{record.id}") - |> unwrap(&render_async/1) + |> render_async() |> unwrap(fn view -> view |> element(@sheet_form) @@ -267,7 +267,7 @@ defmodule MusicLibraryWeb.Components.ReleaseTest do conn |> visit(~p"/collection/#{record.id}") - |> unwrap(&render_async/1) + |> render_async() |> unwrap(fn view -> view |> element(@sheet_form) @@ -323,8 +323,7 @@ end defmodule MusicLibraryWeb.Components.ReleaseTest.ShowPrintTest do use MusicLibraryWeb.ConnCase, async: false - import Phoenix.LiveViewTest, - only: [render: 1, render_async: 1] + import Phoenix.LiveViewTest, only: [render: 1] alias MusicBrainz.Fixtures.Release, as: ReleaseFixtures alias Req.Test diff --git a/test/music_library_web/live/artist_live/show_test.exs b/test/music_library_web/live/artist_live/show_test.exs index 1c7508ba..d25c01c9 100644 --- a/test/music_library_web/live/artist_live/show_test.exs +++ b/test/music_library_web/live/artist_live/show_test.exs @@ -3,7 +3,15 @@ defmodule MusicLibraryWeb.ArtistLive.ShowTest do import MusicLibrary.Fixtures.Records - import Phoenix.LiveViewTest + import Phoenix.LiveViewTest, + only: [ + element: 2, + file_input: 4, + form: 2, + render_click: 1, + render_submit: 1, + render_upload: 2 + ] alias LastFm.Fixtures alias MusicLibrary.Artists @@ -48,7 +56,7 @@ defmodule MusicLibraryWeb.ArtistLive.ShowTest do conn |> visit(~p"/artists/#{artist_musicbrainz_id}") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("span", "No scrobbles") |> assert_has("dt", "Biography") end @@ -74,7 +82,7 @@ defmodule MusicLibraryWeb.ArtistLive.ShowTest do conn |> visit(~p"/artists/#{artist_musicbrainz_id}") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("dt", "Biography") |> assert_has("span", "Wikipedia") |> assert_has("p", text: "English musician") @@ -96,7 +104,7 @@ defmodule MusicLibraryWeb.ArtistLive.ShowTest do conn |> visit(~p"/artists/#{artist_musicbrainz_id}") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("span", "No scrobbles") |> refute_has("summary", "Biography") |> assert_has("div", "Error loading biography") @@ -118,7 +126,7 @@ defmodule MusicLibraryWeb.ArtistLive.ShowTest do conn |> visit(~p"/artists/#{artist_musicbrainz_id}") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("span", "United Kingdom") |> assert_has("span", "🇬🇧") |> assert_has("code", artist_musicbrainz_id) @@ -150,7 +158,7 @@ defmodule MusicLibraryWeb.ArtistLive.ShowTest do conn |> visit(~p"/artists/#{artist_musicbrainz_id}") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("#collection p", escape(collection_record.title)) |> assert_has("#wishlist p", escape(wishlist_record.title)) |> refute_has("#collection p", escape(other_collection_record.title)) @@ -168,7 +176,7 @@ defmodule MusicLibraryWeb.ArtistLive.ShowTest do } do conn |> visit(~p"/artists/#{musicbrainz_id}/edit") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("#artist-info-form") |> assert_has("label", text: "Search for artist image online") end @@ -185,9 +193,9 @@ defmodule MusicLibraryWeb.ArtistLive.ShowTest do session = conn |> visit(~p"/artists/#{musicbrainz_id}/edit") - |> unwrap(&render_async/1) + |> render_async() |> click_button("#image-search-button", "Search") - |> unwrap(&render_async/1) + |> render_async() html = Phoenix.LiveViewTest.render(session.view) assert html =~ "https://thumbnails.example.com/raven-thumb.jpg" @@ -205,9 +213,9 @@ defmodule MusicLibraryWeb.ArtistLive.ShowTest do session = conn |> visit(~p"/artists/#{musicbrainz_id}/edit") - |> unwrap(&render_async/1) + |> render_async() |> click_button("#image-search-button", "Search") - |> unwrap(&render_async/1) + |> render_async() html = Phoenix.LiveViewTest.render(session.view) assert html =~ "Search failed" @@ -220,7 +228,7 @@ defmodule MusicLibraryWeb.ArtistLive.ShowTest do } do conn |> visit(~p"/artists/#{musicbrainz_id}/edit") - |> unwrap(&render_async/1) + |> render_async() |> unwrap(fn view -> image = file_input(view, "#artist-info-form", :image_data, [ @@ -260,9 +268,9 @@ defmodule MusicLibraryWeb.ArtistLive.ShowTest do conn |> visit(~p"/artists/#{musicbrainz_id}/edit") - |> unwrap(fn view -> render_async(view, 500) end) + |> render_async(500) |> click_button("#image-search-button", "Search") - |> unwrap(fn view -> render_async(view, 500) end) + |> render_async(500) |> unwrap(fn view -> view |> element( @@ -270,7 +278,7 @@ defmodule MusicLibraryWeb.ArtistLive.ShowTest do ) |> render_click() end) - |> unwrap(&render_async/1) + |> render_async() updated = Artists.get_artist_info!(artist_info.id) assert updated.image_data_hash != artist_info.image_data_hash diff --git a/test/music_library_web/live/collection_live/show_test.exs b/test/music_library_web/live/collection_live/show_test.exs index 8f30f951..b11dd88e 100644 --- a/test/music_library_web/live/collection_live/show_test.exs +++ b/test/music_library_web/live/collection_live/show_test.exs @@ -20,7 +20,7 @@ defmodule MusicLibraryWeb.CollectionLive.ShowTest do conn |> visit(~p"/collection/#{record.id}") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("a", "Edit") |> click_link("Edit") |> assert_path(~p"/collection/#{record}/show/edit") @@ -43,7 +43,7 @@ defmodule MusicLibraryWeb.CollectionLive.ShowTest do session = conn |> visit(~p"/collection/#{record.id}") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("h2", escape(record.title)) |> assert_has("p", record.release_date) |> assert_has("p", format_label(record.format)) @@ -82,7 +82,7 @@ defmodule MusicLibraryWeb.CollectionLive.ShowTest do conn |> visit(~p"/collection/#{record.id}") |> assert_has("button", "Show Tracks") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("a", "Connect Last.fm") release = diff --git a/test/music_library_web/live/maintenance_live/index_test.exs b/test/music_library_web/live/maintenance_live/index_test.exs index d0ba410b..edce835a 100644 --- a/test/music_library_web/live/maintenance_live/index_test.exs +++ b/test/music_library_web/live/maintenance_live/index_test.exs @@ -22,7 +22,7 @@ defmodule MusicLibraryWeb.MaintenanceLive.IndexTest do test "async status resolves to :not_connected when no session key is stored", %{conn: conn} do conn |> visit(~p"/maintenance") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("span", "Not connected") end @@ -35,7 +35,7 @@ defmodule MusicLibraryWeb.MaintenanceLive.IndexTest do conn |> visit(~p"/maintenance") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("span", "Connected as alice") end end diff --git a/test/music_library_web/live/scrobble_live/release_show_test.exs b/test/music_library_web/live/scrobble_live/release_show_test.exs index 64c5076a..3404de39 100644 --- a/test/music_library_web/live/scrobble_live/release_show_test.exs +++ b/test/music_library_web/live/scrobble_live/release_show_test.exs @@ -33,7 +33,7 @@ defmodule MusicLibraryWeb.ScrobbleLive.ReleaseShowTest do test "renders the scrobble UI for the release", %{conn: conn} do conn |> visit(~p"/scrobble/#{@rg_id}/releases/#{@release_id}") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("h2", text: "Marbles") end @@ -46,7 +46,7 @@ defmodule MusicLibraryWeb.ScrobbleLive.ReleaseShowTest do test "sets the page title once the release loads", %{conn: conn} do conn |> visit(~p"/scrobble/#{@rg_id}/releases/#{@release_id}") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("title", text: "Marillion - Marbles") end end diff --git a/test/music_library_web/live/stats_live/top_albums_test.exs b/test/music_library_web/live/stats_live/top_albums_test.exs index da8b7e5c..418afe33 100644 --- a/test/music_library_web/live/stats_live/top_albums_test.exs +++ b/test/music_library_web/live/stats_live/top_albums_test.exs @@ -45,7 +45,7 @@ defmodule MusicLibraryWeb.StatsLive.TopAlbumsTest do test "renders no album rows when no scrobbles exist", %{conn: conn} do conn |> visit("/") - |> unwrap(&render_async/1) + |> render_async() |> refute_has("#top-albums img") end end @@ -56,7 +56,7 @@ defmodule MusicLibraryWeb.StatsLive.TopAlbumsTest do test "shows the album title, linked artist, and play count", %{conn: conn} do conn |> visit("/") - |> unwrap(&render_async/1) + |> render_async() |> assert_has("#top-albums p", text: "Marbles") |> assert_has(~s|#top-albums a[href="/artists/#{@marillion_mbid}"]|, text: "Marillion") |> assert_has("#top-albums span", text: "2") @@ -65,7 +65,7 @@ defmodule MusicLibraryWeb.StatsLive.TopAlbumsTest do test "uses the Last.fm cover URL when no matching record exists", %{conn: conn} do conn |> visit("/") - |> unwrap(&render_async/1) + |> render_async() |> assert_has(~s|#top-albums img[src="https://example.com/cover.jpg"]|) end @@ -76,7 +76,7 @@ defmodule MusicLibraryWeb.StatsLive.TopAlbumsTest do session = conn |> visit("/") - |> unwrap(&render_async/1) + |> render_async() html = Phoenix.LiveViewTest.render(session.view) @@ -94,7 +94,7 @@ defmodule MusicLibraryWeb.StatsLive.TopAlbumsTest do session = conn |> visit("/") - |> unwrap(&render_async/1) + |> render_async() html = Phoenix.LiveViewTest.render(session.view) @@ -113,7 +113,7 @@ defmodule MusicLibraryWeb.StatsLive.TopAlbumsTest do session = conn |> visit("/") - |> unwrap(&render_async/1) + |> render_async() html = Phoenix.LiveViewTest.render(session.view) @@ -146,7 +146,7 @@ defmodule MusicLibraryWeb.StatsLive.TopAlbumsTest do session = conn |> visit("/") - |> unwrap(&render_async/1) + |> render_async() session |> assert_has("##{"top-album-#{@marbles_release_id}"}") @@ -176,7 +176,7 @@ defmodule MusicLibraryWeb.StatsLive.TopAlbumsTest do session = conn |> visit("/") - |> unwrap(&render_async/1) + |> render_async() html = Phoenix.LiveViewTest.render(session.view) @@ -206,7 +206,7 @@ defmodule MusicLibraryWeb.StatsLive.TopAlbumsTest do session = conn |> visit("/") - |> unwrap(&render_async/1) + |> render_async() refute_has(session, "#top-albums", text: "Older Album") @@ -214,7 +214,7 @@ defmodule MusicLibraryWeb.StatsLive.TopAlbumsTest do |> within("#top-albums", fn s -> s |> click_button("30d") - |> unwrap(&render_async/1) + |> render_async() end) assert_has(session, "#top-albums", text: "Older Album") 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 d24ad8da..f1d1352e 100644 --- a/test/music_library_web/live_helpers/record_actions_test.exs +++ b/test/music_library_web/live_helpers/record_actions_test.exs @@ -3,7 +3,8 @@ defmodule MusicLibraryWeb.LiveHelpers.RecordActionsTest do use Oban.Testing, repo: MusicLibrary.BackgroundRepo import MusicLibrary.Fixtures.Records - import Phoenix.LiveViewTest + + import Phoenix.LiveViewTest, only: [live: 2, render: 1, render_click: 3] alias MusicBrainz.Fixtures.Release, as: ReleaseFixtures alias MusicBrainz.Fixtures.ReleaseGroup diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index c67d7ee1..0bef6e7f 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -14,7 +14,6 @@ defmodule MusicLibraryWeb.ConnCase do import Phoenix.LiveViewTest, # The default endpoint for testing only: [ - render_async: 1, render_change: 1, render_click: 3, render_hook: 2, diff --git a/test/support/live_test_helpers.ex b/test/support/live_test_helpers.ex index 5d8e6324..5320cd98 100644 --- a/test/support/live_test_helpers.ex +++ b/test/support/live_test_helpers.ex @@ -1,10 +1,42 @@ defmodule MusicLibraryWeb.LiveTestHelpers do - @moduledoc false + @moduledoc """ + Shared helpers for LiveView tests. + These helpers sit alongside PhoenixTest and Phoenix.LiveViewTest so tests can + keep PhoenixTest sessions in the pipeline while still reaching for lower-level + LiveView interactions when PhoenixTest does not cover them directly. + """ + + @doc """ + Escapes text for assertions against rendered HTML. + """ def escape(string) do LazyHTML.html_escape(string) end + @doc """ + Waits for LiveView async work to finish. + + Accepts either a PhoenixTest session, returning the session for continued + piping, or a raw LiveView view/element, returning the rendered HTML from + `Phoenix.LiveViewTest.render_async/2`. + """ + def render_async( + session_or_view, + timeout \\ Application.fetch_env!(:ex_unit, :assert_receive_timeout) + ) + + def render_async(%{view: %Phoenix.LiveViewTest.View{}} = session, timeout) do + PhoenixTest.unwrap(session, &Phoenix.LiveViewTest.render_async(&1, timeout)) + end + + def render_async(view_or_element, timeout) do + Phoenix.LiveViewTest.render_async(view_or_element, timeout) + end + + @doc """ + Dispatches a hook event to an element inside a PhoenixTest session. + """ def trigger_hook(session, selector, hook, params \\ %{}) do session |> PhoenixTest.unwrap(fn view ->