From f4db0632985bcdabf0e83f2a3afae6a281f118e1 Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Sun, 19 Apr 2026 09:00:06 +0100 Subject: [PATCH] Test artist edit form and record set picker flows Closes #180 --- .../live/artist_live/show_test.exs | 130 ++++++++++++++++++ .../live/record_set_live/show_test.exs | 83 +++++++++++ 2 files changed, 213 insertions(+) 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 3413568f..1b8b6d9b 100644 --- a/test/music_library_web/live/artist_live/show_test.exs +++ b/test/music_library_web/live/artist_live/show_test.exs @@ -2,8 +2,10 @@ defmodule MusicLibraryWeb.ArtistLive.ShowTest do use MusicLibraryWeb.ConnCase import MusicLibrary.Fixtures.Records + import Phoenix.LiveViewTest alias LastFm.Fixtures + alias MusicLibrary.Artists defp fill_collection(_config) do collection_record = @@ -151,4 +153,132 @@ defmodule MusicLibraryWeb.ArtistLive.ShowTest do |> refute_has("#wishlist p", escape(other_collection_record.title)) end end + + describe "Edit artist image" do + setup :fill_collection + setup :stub_last_fm + + test "opens the edit modal with the form", %{ + conn: conn, + artist_musicbrainz_id: musicbrainz_id + } do + conn + |> visit(~p"/artists/#{musicbrainz_id}/edit") + |> unwrap(&render_async/1) + |> assert_has("#artist-info-form") + |> assert_has("label", text: "Search for artist image online") + end + + test "Brave Search displays image results", %{ + conn: conn, + artist_musicbrainz_id: musicbrainz_id + } do + Req.Test.stub(BraveSearch.API, fn conn -> + assert conn.request_path == "/res/v1/images/search" + Req.Test.json(conn, BraveSearch.Fixtures.search_images_response()) + end) + + {:ok, view, _html} = live(conn, ~p"/artists/#{musicbrainz_id}/edit") + + view + |> element("#image-search-button") + |> render_click() + + html = render_async(view) + + assert html =~ "https://thumbnails.example.com/raven-thumb.jpg" + end + + @tag :capture_log + test "Brave Search transport error surfaces a friendly message", %{ + conn: conn, + artist_musicbrainz_id: musicbrainz_id + } do + Req.Test.stub(BraveSearch.API, fn conn -> + Req.Test.transport_error(conn, :timeout) + end) + + {:ok, view, _html} = live(conn, ~p"/artists/#{musicbrainz_id}/edit") + + view + |> element("#image-search-button") + |> render_click() + + html = render_async(view) + + assert html =~ "Search failed" + end + + test "uploading an image saves and updates the artist info", %{ + conn: conn, + artist_musicbrainz_id: musicbrainz_id, + artist_info: artist_info + } do + {:ok, view, _html} = live(conn, ~p"/artists/#{musicbrainz_id}/edit") + + image = + file_input(view, "#artist-info-form", :image_data, [ + %{name: "raven.jpg", content: raven_cover_data(), type: "image/jpeg"} + ]) + + render_upload(image, "raven.jpg") + + view + |> form("#artist-info-form") + |> render_submit() + + updated = Artists.get_artist_info!(artist_info.id) + assert updated.image_data_hash != artist_info.image_data_hash + assert updated.image_data_hash != nil + end + + test "selecting a Brave Search result downloads and saves the image", %{ + conn: conn, + artist_musicbrainz_id: musicbrainz_id, + artist_info: artist_info + } do + raven_binary = raven_cover_data() + + Req.Test.stub(BraveSearch.API, fn conn -> + case conn.request_path do + "/res/v1/images/search" -> + Req.Test.json(conn, BraveSearch.Fixtures.search_images_response()) + + _ -> + Plug.Conn.send_resp(conn, 200, raven_binary) + end + end) + + {:ok, view, _html} = live(conn, ~p"/artists/#{musicbrainz_id}/edit") + + view |> element("#image-search-button") |> render_click() + render_async(view) + + view + |> element( + "button[phx-click='select_image'][phx-value-url='https://images.example.com/raven-cover.jpg']" + ) + |> render_click() + + render_async(view) + + updated = Artists.get_artist_info!(artist_info.id) + assert updated.image_data_hash != artist_info.image_data_hash + assert updated.image_data_hash != nil + end + end + + defp stub_last_fm(_config) do + Req.Test.stub(LastFm.API, fn conn -> + case Map.get(conn.params, "method") do + "artist.getInfo" -> + Req.Test.json(conn, Fixtures.Artist.get_info()) + + "artist.getSimilar" -> + Req.Test.json(conn, Fixtures.Artist.get_similar_artists()) + end + end) + + :ok + end end diff --git a/test/music_library_web/live/record_set_live/show_test.exs b/test/music_library_web/live/record_set_live/show_test.exs index 5512a6ec..f00b768e 100644 --- a/test/music_library_web/live/record_set_live/show_test.exs +++ b/test/music_library_web/live/record_set_live/show_test.exs @@ -2,6 +2,7 @@ defmodule MusicLibraryWeb.RecordSetLive.ShowTest do use MusicLibraryWeb.ConnCase import MusicLibrary.Fixtures.RecordSets + import MusicLibrary.Fixtures.Records, only: [record: 1] import Phoenix.LiveViewTest alias MusicLibrary.RecordSets @@ -105,4 +106,86 @@ defmodule MusicLibraryWeb.RecordSetLive.ShowTest do |> assert_path(~p"/record-sets/#{set}") end end + + describe "Add record" do + test "opens the picker modal", %{conn: conn} do + set = record_set() + + conn + |> visit(~p"/record-sets/#{set}/show/add-record") + |> assert_has("h1", text: "Add Record") + |> assert_has("#record-picker-search-input") + end + + test "shows collected records matching the query", %{conn: conn} do + set = record_set() + collected = record(%{title: "Collected Unique Xyzzy", purchased_at: DateTime.utc_now()}) + + {:ok, view, _html} = live(conn, ~p"/record-sets/#{set}/show/add-record") + + html = search_picker(view, "Xyzzy") + + assert html =~ "Collected" + assert html =~ escape(collected.title) + end + + test "shows wishlisted records matching the query", %{conn: conn} do + set = record_set() + wishlisted = record(%{title: "Wishlisted Unique Xyzzy", purchased_at: nil}) + + {:ok, view, _html} = live(conn, ~p"/record-sets/#{set}/show/add-record") + + html = search_picker(view, "Xyzzy") + + assert html =~ "Wishlisted" + assert html =~ escape(wishlisted.title) + end + + test "shows 'No records found' for non-matching queries", %{conn: conn} do + set = record_set() + + {:ok, view, _html} = live(conn, ~p"/record-sets/#{set}/show/add-record") + + html = search_picker(view, "NonexistentTitleZzzzzzzz") + + assert html =~ "No records found" + end + + test "adds a record to the set", %{conn: conn} do + set = record_set() + picked = record(%{title: "Pickable Unique Xyzzy", purchased_at: DateTime.utc_now()}) + + {:ok, view, _html} = live(conn, ~p"/record-sets/#{set}/show/add-record") + + search_picker(view, "Xyzzy") + + view + |> element("li[phx-click='add_record'][phx-value-record-id='#{picked.id}']") + |> render_click() + + updated = RecordSets.get_record_set!(set.id) + assert Enum.any?(updated.items, fn item -> item.record.id == picked.id end) + end + + test "excludes records already in the set from results", %{conn: conn} do + set = record_set() + existing = record(%{title: "Already In Set Xyzzy", purchased_at: DateTime.utc_now()}) + {:ok, _} = RecordSets.add_record_to_set(set, existing.id) + + {:ok, view, _html} = live(conn, ~p"/record-sets/#{set}/show/add-record") + + search_picker(view, "Xyzzy") + + refute has_element?( + view, + "li[phx-click='add_record'][phx-value-record-id='#{existing.id}']" + ) + end + end + + defp search_picker(view, query) do + view + |> form("#record-picker-navigation form", %{query: query}) + |> render_submit() + end end