Replace /scrobble/:release_id with nested route

This commit is contained in:
Claudio Ortolina
2026-04-23 14:39:26 +01:00
parent d8041dc0dd
commit 0dcd6eab83
8 changed files with 137 additions and 606 deletions
@@ -0,0 +1,46 @@
defmodule MusicLibraryWeb.ScrobbleLive.ReleaseShowTest do
@moduledoc """
Smoke tests for the scrobble release page. Full scrobble behaviour
(picker, selection bar, handlers) is covered by the Release
LiveComponent tests in `release_test.exs`.
"""
use MusicLibraryWeb.ConnCase
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_mb(_) do
Test.stub(MusicBrainz.API, fn conn ->
case conn.request_path do
"/ws/2/release/" <> _id ->
Test.json(conn, ReleaseFixtures.release_with_media(:marbles))
_ ->
Test.json(conn, %{})
end
end)
:ok
end
describe "ReleaseShow" do
setup [:stub_mb]
test "renders the scrobble UI for the release", %{conn: conn} do
conn
|> visit(~p"/scrobble/#{@rg_id}/releases/#{@release_id}")
|> unwrap(&Phoenix.LiveViewTest.render_async/1)
|> assert_has("h2", text: "Marbles")
end
test "back link targets the release-group page", %{conn: conn} do
conn
|> visit(~p"/scrobble/#{@rg_id}/releases/#{@release_id}")
|> assert_has("a[href='/scrobble/#{@rg_id}']", text: "Back to releases")
end
end
end
@@ -1,261 +0,0 @@
defmodule MusicLibraryWeb.ScrobbleLive.ShowTest do
use MusicLibraryWeb.ConnCase
# Route /scrobble/:release_id is removed in Phase 5 — skip until then
@moduletag :skip
import Phoenix.LiveViewTest, only: [element: 2, render_change: 2, render_click: 3]
alias MusicBrainz.Fixtures.Release, as: ReleaseFixtures
alias MusicLibrary.Secrets
alias Req.Test
@release_id ReleaseFixtures.release_id(:marbles)
defp stub_musicbrainz_release(_) do
Test.stub(MusicBrainz.API, fn conn ->
case conn.request_path do
"/ws/2/release/" <> _id ->
Test.json(conn, ReleaseFixtures.release_with_media(:marbles))
_ ->
Test.json(conn, %{})
end
end)
:ok
end
defp stub_musicbrainz_release_error(_) do
Test.stub(MusicBrainz.API, fn conn ->
Plug.Conn.send_resp(conn, 404, "Not Found")
end)
:ok
end
defp stub_lastfm_scrobble(_) do
Test.stub(LastFm.API, fn conn ->
Test.json(conn, %{"scrobbles" => %{"@attr" => %{"accepted" => 1}}})
end)
:ok
end
defp stub_lastfm_scrobble_error(_) do
Test.stub(LastFm.API, fn conn ->
Test.json(conn, %{"error" => 11, "message" => "Service temporarily unavailable"})
end)
:ok
end
defp store_lastfm_session_key(_) do
Secrets.store("last_fm_session_key", "test_session_key")
:ok
end
defp first_track_id do
ReleaseFixtures.release_with_media(:marbles)
|> Map.get("media")
|> List.first()
|> Map.get("tracks")
|> List.first()
|> Map.get("id")
end
describe "Show" do
setup [:stub_musicbrainz_release]
test "renders release details", %{conn: conn} do
conn
|> visit(~p"/scrobble/#{@release_id}")
|> assert_has("h2", "Marbles")
|> assert_has("a", "Back to search")
end
test "shows Last.fm not connected alert when no session key", %{conn: conn} do
conn
|> visit(~p"/scrobble/#{@release_id}")
|> assert_has("div", "You need to connect your Last.fm account")
end
test "displays tracks", %{conn: conn} do
conn
|> visit(~p"/scrobble/#{@release_id}")
|> assert_has("h3", "Tracks")
end
end
describe "Show with Last.fm connected" do
setup [:stub_musicbrainz_release, :stub_lastfm_scrobble, :store_lastfm_session_key]
test "does not show Last.fm not connected alert", %{conn: conn} do
conn
|> visit(~p"/scrobble/#{@release_id}")
|> refute_has("[data-part='title']", "Last.fm not connected")
end
test "scrobble full release", %{conn: conn} do
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
render_click(view, "scrobble_release", %{})
end)
|> assert_has("#toast-group", "Release scrobbled successfully")
end
test "scrobble single medium", %{conn: conn} do
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
render_click(view, "scrobble_medium", %{"number" => "1"})
end)
|> assert_has("#toast-group", "Disc scrobbled successfully")
end
test "scrobble single medium still works with tracks selected elsewhere", %{conn: conn} do
track_id = first_track_id()
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
# Select a track first — AC#3 regression: medium scrobble must not be
# blocked just because something is ticked.
view
|> element("#scrobble-release-form")
|> render_change(%{"release" => %{"selected_tracks" => [track_id]}})
render_click(view, "scrobble_medium", %{"number" => "1"})
end)
|> assert_has("#toast-group", "Disc scrobbled successfully")
end
test "toggle track on and off changes button label", %{conn: conn} do
track_id = first_track_id()
session = visit(conn, ~p"/scrobble/#{@release_id}")
# Toggle track on — button label changes
session
|> unwrap(fn view ->
view
|> element("#scrobble-release-form")
|> render_change(%{"release" => %{"selected_tracks" => [track_id]}})
end)
|> assert_has("button", "Scrobble selected tracks")
# Toggle track off — button label reverts
session
|> unwrap(fn view ->
view
|> element("#scrobble-release-form")
|> render_change(%{"release" => %{"selected_tracks" => []}})
end)
|> assert_has("button", "Scrobble release")
end
test "toggle medium selects and deselects all tracks in that medium", %{conn: conn} do
session = visit(conn, ~p"/scrobble/#{@release_id}")
# Toggle medium 1 on — button label changes
session
|> unwrap(fn view ->
view
|> element("#scrobble-release-form")
|> render_change(%{"release" => %{"toggle_medium" => ["1"]}})
end)
|> assert_has("button", "Scrobble selected tracks")
# Toggle medium 1 off — button label reverts
session
|> unwrap(fn view ->
view
|> element("#scrobble-release-form")
|> render_change(%{"release" => %{"toggle_medium" => []}})
end)
|> assert_has("button", "Scrobble release")
end
test "scrobble selected tracks", %{conn: conn} do
track_id = first_track_id()
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
view
|> element("#scrobble-release-form")
|> render_change(%{"release" => %{"selected_tracks" => [track_id]}})
render_click(view, "scrobble_selected_tracks", %{})
end)
|> assert_has("#toast-group", "Selected tracks scrobbled successfully")
end
test "scrobble selected tracks with no selection shows error", %{conn: conn} do
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
render_click(view, "scrobble_selected_tracks", %{})
end)
|> assert_has("#toast-group", "No tracks selected")
end
end
describe "Show with Last.fm scrobble error" do
setup [:stub_musicbrainz_release, :stub_lastfm_scrobble_error, :store_lastfm_session_key]
@tag :capture_log
test "scrobble release shows error toast on failure", %{conn: conn} do
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
render_click(view, "scrobble_release", %{})
end)
|> assert_has("#toast-group", "Error scrobbling release")
end
@tag :capture_log
test "scrobble medium shows error toast on failure", %{conn: conn} do
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
render_click(view, "scrobble_medium", %{"number" => "1"})
end)
|> assert_has("#toast-group", "Error scrobbling disc")
end
@tag :capture_log
test "scrobble selected tracks shows error toast on failure", %{conn: conn} do
track_id = first_track_id()
session = visit(conn, ~p"/scrobble/#{@release_id}")
session
|> unwrap(fn view ->
view
|> element("#scrobble-release-form")
|> render_change(%{"release" => %{"selected_tracks" => [track_id]}})
render_click(view, "scrobble_selected_tracks", %{})
end)
|> assert_has("#toast-group", "Error scrobbling selected tracks")
end
end
describe "Show with failed release fetch" do
setup [:stub_musicbrainz_release_error]
test "redirects to scrobble index with error", %{conn: conn} do
conn
|> visit(~p"/scrobble/#{@release_id}")
|> assert_path(~p"/scrobble")
end
end
end