ML-193: add secondary parser, search, and API coverage tests
Add ~80 focused tests across 10 files covering:
- Record set search by name, description, contained record title, artist name
- Navigation events for records, artists, record sets, links, and view-all actions
- MusicBrainz.ReleaseGroup parser (artist credits, included RGs, release IDs, types)
- MusicBrainz.ReleaseSearchResult edge cases (missing RG, unknown formats)
- LastFm.Session XML edge cases (non-subscriber, missing nodes)
- MusicLibrary.HttpError default status-kind mapping
- Discogs.API.ErrorResponse fallback message and retry-delay behavior
- ArtistRefreshWikipediaData {:cancel, :no_english_wikipedia} worker path
- StatsComponents grouped on-this-day records and anniversary labels
This commit is contained in:
@@ -143,6 +143,132 @@ defmodule MusicLibraryWeb.StatsLive.IndexTest do
|
||||
|
||||
assert_has(session, "##{record_yesterday.id} h2", escape(record_yesterday.title))
|
||||
end
|
||||
|
||||
test "groups records with the same musicbrainz_id in the 'On This Day' section", %{
|
||||
conn: conn,
|
||||
collection: collection
|
||||
} do
|
||||
today = Date.utc_today()
|
||||
|
||||
# Create a record and a duplicate with the same musicbrainz_id
|
||||
base_record =
|
||||
List.first(collection)
|
||||
|> Records.change_record(%{
|
||||
release_date: Date.to_iso8601(today),
|
||||
purchased_at: DateTime.utc_now()
|
||||
})
|
||||
|> Repo.update!()
|
||||
|
||||
# Create another record with the SAME musicbrainz_id (different purchase)
|
||||
# Convert Artist structs to plain maps for create_record
|
||||
artist_maps = Enum.map(base_record.artists, &Map.from_struct/1)
|
||||
|
||||
dup_attrs = %{
|
||||
title: base_record.title,
|
||||
musicbrainz_id: base_record.musicbrainz_id,
|
||||
release_date: Date.to_iso8601(today),
|
||||
purchased_at: DateTime.utc_now() |> DateTime.add(1, :second),
|
||||
artists: artist_maps,
|
||||
genres: base_record.genres,
|
||||
format: base_record.format,
|
||||
type: base_record.type,
|
||||
cover_url: base_record.cover_url,
|
||||
cover_hash: base_record.cover_hash,
|
||||
musicbrainz_data: base_record.musicbrainz_data
|
||||
}
|
||||
|
||||
{:ok, _dup} = MusicLibrary.Records.create_record(dup_attrs)
|
||||
|
||||
session = conn |> visit("/")
|
||||
|
||||
# The grouped record should appear with the group ID
|
||||
assert_has(session, "#group-#{base_record.musicbrainz_id}")
|
||||
# It should show the release count
|
||||
assert_has(session, "#group-#{base_record.musicbrainz_id}", "2 releases")
|
||||
end
|
||||
|
||||
test "shows 'Today' label for records released on the current date", %{
|
||||
conn: conn,
|
||||
collection: collection
|
||||
} do
|
||||
today = Date.utc_today()
|
||||
|
||||
_record =
|
||||
List.first(collection)
|
||||
|> Records.change_record(%{
|
||||
release_date: Date.to_iso8601(today),
|
||||
purchased_at: DateTime.utc_now()
|
||||
})
|
||||
|> Repo.update!()
|
||||
|
||||
session = conn |> visit("/")
|
||||
|
||||
assert_has(session, "span", "Today")
|
||||
end
|
||||
|
||||
test "shows anniversary label for records released exactly 5 years ago", %{
|
||||
conn: conn,
|
||||
collection: collection
|
||||
} do
|
||||
today = Date.utc_today()
|
||||
# Use year-based adjustment so month-day stays the same
|
||||
five_years_ago = %{today | year: today.year - 5}
|
||||
|
||||
_record =
|
||||
List.first(collection)
|
||||
|> Records.change_record(%{
|
||||
release_date: Date.to_iso8601(five_years_ago),
|
||||
purchased_at: DateTime.utc_now()
|
||||
})
|
||||
|> Repo.update!()
|
||||
|
||||
session = conn |> visit("/")
|
||||
|
||||
assert_has(session, "span", "5 years ago")
|
||||
end
|
||||
|
||||
test "shows anniversary label for records released exactly 10 years ago", %{
|
||||
conn: conn,
|
||||
collection: collection
|
||||
} do
|
||||
today = Date.utc_today()
|
||||
# Use year-based adjustment so month-day stays the same
|
||||
ten_years_ago = %{today | year: today.year - 10}
|
||||
|
||||
_record =
|
||||
List.first(collection)
|
||||
|> Records.change_record(%{
|
||||
release_date: Date.to_iso8601(ten_years_ago),
|
||||
purchased_at: DateTime.utc_now()
|
||||
})
|
||||
|> Repo.update!()
|
||||
|
||||
session = conn |> visit("/")
|
||||
|
||||
assert_has(session, "span", "10 years ago")
|
||||
end
|
||||
|
||||
test "shows normal year label for records not on milestone anniversary", %{
|
||||
conn: conn,
|
||||
collection: collection
|
||||
} do
|
||||
today = Date.utc_today()
|
||||
# Use year-based adjustment so month-day stays the same
|
||||
three_years_ago = %{today | year: today.year - 3}
|
||||
|
||||
_record =
|
||||
List.first(collection)
|
||||
|> Records.change_record(%{
|
||||
release_date: Date.to_iso8601(three_years_ago),
|
||||
purchased_at: DateTime.utc_now()
|
||||
})
|
||||
|> Repo.update!()
|
||||
|
||||
session = conn |> visit("/")
|
||||
|
||||
# 3 years is not a milestone (not divisible by 5 or 10)
|
||||
assert_has(session, "span", "3 years ago")
|
||||
end
|
||||
end
|
||||
|
||||
describe "Scrobble activity" do
|
||||
|
||||
@@ -2,6 +2,13 @@ defmodule MusicLibraryWeb.UniversalSearchLive.IndexTest do
|
||||
use MusicLibraryWeb.ConnCase
|
||||
|
||||
import MusicLibrary.Fixtures.Records
|
||||
import MusicLibrary.Fixtures.RecordSets
|
||||
|
||||
import Phoenix.LiveViewTest,
|
||||
only: [
|
||||
render_click: 1,
|
||||
element: 2
|
||||
]
|
||||
|
||||
setup do
|
||||
collection_record = record(%{title: "Dark Side of the Moon"})
|
||||
@@ -12,10 +19,18 @@ defmodule MusicLibraryWeb.UniversalSearchLive.IndexTest do
|
||||
|
||||
artist_info(artist_record.artists |> List.first() |> Map.get(:musicbrainz_id), %{})
|
||||
|
||||
# Create a record set for navigation testing
|
||||
{:ok, record_set} =
|
||||
MusicLibrary.RecordSets.create_record_set(%{
|
||||
name: "Prog Favorites",
|
||||
description: "Best prog albums"
|
||||
})
|
||||
|
||||
%{
|
||||
collection_record: collection_record,
|
||||
wishlist_record: wishlist_record,
|
||||
artist_record: artist_record
|
||||
artist_record: artist_record,
|
||||
record_set: record_set
|
||||
}
|
||||
end
|
||||
|
||||
@@ -125,6 +140,173 @@ defmodule MusicLibraryWeb.UniversalSearchLive.IndexTest do
|
||||
end
|
||||
end
|
||||
|
||||
describe "Navigation events" do
|
||||
setup do
|
||||
Req.Test.set_req_test_to_shared()
|
||||
|
||||
# Return valid-enough shapes so async task parsers don't crash
|
||||
Req.Test.stub(LastFm.API, fn conn ->
|
||||
Req.Test.json(conn, %{
|
||||
"artist" => %{
|
||||
"name" => "test",
|
||||
"mbid" => "",
|
||||
"bio" => %{"summary" => "", "content" => ""},
|
||||
"image" => [%{"#text" => "", "size" => "large"}],
|
||||
"url" => ""
|
||||
},
|
||||
"similarartists" => %{"artist" => []}
|
||||
})
|
||||
end)
|
||||
|
||||
Req.Test.stub(MusicBrainz.API, fn conn ->
|
||||
Req.Test.json(conn, %{
|
||||
"media" => [],
|
||||
"track-count" => 0
|
||||
})
|
||||
end)
|
||||
|
||||
on_exit(fn ->
|
||||
Req.Test.stub(MusicBrainz.API, nil)
|
||||
Req.Test.stub(LastFm.API, nil)
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
test "navigates to collection record on click", %{
|
||||
conn: conn,
|
||||
collection_record: collection_record
|
||||
} do
|
||||
conn
|
||||
|> visit(~p"/collection")
|
||||
|> click_button("Search (Cmd/Ctrl+K)")
|
||||
|> fill_in("Universal Search", with: collection_record.title)
|
||||
|> unwrap(fn view ->
|
||||
view
|
||||
|> element("div[phx-click='navigate_to_record'][phx-value-type='collection']")
|
||||
|> render_click()
|
||||
end)
|
||||
|> assert_path(~p"/collection/#{collection_record.id}")
|
||||
end
|
||||
|
||||
test "navigates to wishlist record on click", %{
|
||||
conn: conn,
|
||||
wishlist_record: wishlist_record
|
||||
} do
|
||||
conn
|
||||
|> visit(~p"/collection")
|
||||
|> click_button("Search (Cmd/Ctrl+K)")
|
||||
|> fill_in("Universal Search", with: wishlist_record.title)
|
||||
|> unwrap(fn view ->
|
||||
view
|
||||
|> element("div[phx-click='navigate_to_record'][phx-value-type='wishlist']")
|
||||
|> render_click()
|
||||
end)
|
||||
|> assert_path(~p"/wishlist/#{wishlist_record.id}")
|
||||
end
|
||||
|
||||
test "navigates to artist on click", %{
|
||||
conn: conn,
|
||||
artist_record: artist_record
|
||||
} do
|
||||
artist = List.first(artist_record.artists)
|
||||
|
||||
conn
|
||||
|> visit(~p"/collection")
|
||||
|> click_button("Search (Cmd/Ctrl+K)")
|
||||
|> fill_in("Universal Search", with: artist.name)
|
||||
|> unwrap(fn view ->
|
||||
view
|
||||
|> element("div[phx-click='navigate_to_artist']")
|
||||
|> render_click()
|
||||
end)
|
||||
|> assert_path(~p"/artists/#{artist.musicbrainz_id}")
|
||||
end
|
||||
|
||||
test "navigates to record set on click", %{
|
||||
conn: conn,
|
||||
record_set: record_set
|
||||
} do
|
||||
conn
|
||||
|> visit(~p"/collection")
|
||||
|> click_button("Search (Cmd/Ctrl+K)")
|
||||
|> fill_in("Universal Search", with: record_set.name)
|
||||
|> unwrap(fn view ->
|
||||
view
|
||||
|> element("div[phx-click='navigate_to_record_set']")
|
||||
|> render_click()
|
||||
end)
|
||||
|> assert_path(~p"/record-sets/#{record_set.id}")
|
||||
end
|
||||
|
||||
test "navigates via navigation link to collection chat", %{conn: conn} do
|
||||
conn
|
||||
|> visit(~p"/collection")
|
||||
|> click_button("Search (Cmd/Ctrl+K)")
|
||||
|> fill_in("Universal Search", with: "chat")
|
||||
|> unwrap(fn view ->
|
||||
view
|
||||
|> element("div[phx-click='navigate_to_link']")
|
||||
|> render_click()
|
||||
end)
|
||||
|> assert_path(~p"/collection", query_params: %{"chat" => "open"})
|
||||
end
|
||||
|
||||
test "record sets appear in search results", %{
|
||||
conn: conn,
|
||||
record_set: record_set
|
||||
} do
|
||||
conn
|
||||
|> visit(~p"/collection")
|
||||
|> click_button("Search (Cmd/Ctrl+K)")
|
||||
|> fill_in("Universal Search", with: record_set.name)
|
||||
|> assert_has("p", record_set.name)
|
||||
|> assert_has("h3", "Record Sets")
|
||||
end
|
||||
|
||||
test "view-all-collection navigates to collection with query param", %{conn: conn} do
|
||||
# Create 7 records sharing a common prefix so > 5 results exist
|
||||
Enum.each(1..7, fn i ->
|
||||
record(%{title: "ViewAllTest #{i}"})
|
||||
end)
|
||||
|
||||
conn
|
||||
|> visit(~p"/collection")
|
||||
|> click_button("Search (Cmd/Ctrl+K)")
|
||||
|> fill_in("Universal Search", with: "ViewAllTest")
|
||||
|> click_button("button[phx-click='navigate_to_collection']", "View all")
|
||||
|> assert_path(~p"/collection", query_params: %{"query" => "ViewAllTest"})
|
||||
end
|
||||
|
||||
test "view-all-wishlist navigates to wishlist with query param", %{conn: conn} do
|
||||
# Create 7 wishlist records with a common prefix
|
||||
Enum.each(1..7, fn i ->
|
||||
record(%{title: "WishlistAll #{i}", purchased_at: nil})
|
||||
end)
|
||||
|
||||
conn
|
||||
|> visit(~p"/collection")
|
||||
|> click_button("Search (Cmd/Ctrl+K)")
|
||||
|> fill_in("Universal Search", with: "WishlistAll")
|
||||
|> click_button("button[phx-click='navigate_to_wishlist']", "View all")
|
||||
|> assert_path(~p"/wishlist", query_params: %{"query" => "WishlistAll"})
|
||||
end
|
||||
|
||||
test "view-all-record-sets navigates to record sets with query param", %{conn: conn} do
|
||||
# Create 7 record sets with a common prefix
|
||||
Enum.each(1..7, fn i ->
|
||||
record_set(%{name: "SetAll #{i}"})
|
||||
end)
|
||||
|
||||
conn
|
||||
|> visit(~p"/collection")
|
||||
|> click_button("Search (Cmd/Ctrl+K)")
|
||||
|> fill_in("Universal Search", with: "SetAll")
|
||||
|> click_button("button[phx-click='navigate_to_record_sets']", "View all")
|
||||
|> assert_path(~p"/record-sets", query_params: %{"query" => "SetAll"})
|
||||
end
|
||||
end
|
||||
|
||||
describe "Keyboard navigation" do
|
||||
test "displays keyboard navigation hints when results are present", %{
|
||||
conn: conn,
|
||||
|
||||
Reference in New Issue
Block a user