ML-168: broadcast index_changed event after background import
Import workers now broadcast :records_index_changed on "records:index_changed" after successful import via Records.broadcast_index_changed/0. CollectionLive.Index and WishlistLive.Index subscribe to the topic in mount/3 and reload their record streams on receipt, with a live_action guard to skip reloads when the grid is hidden behind a modal (:import, :barcode_scan). IndexActions.handle_index_changed/1 refreshes total_entries before reloading to keep the pagination bar accurate.
This commit is contained in:
+32
-8
@@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
id: ML-168
|
id: ML-168
|
||||||
title: Update wishlist index and collection index when background import finishes
|
title: Update wishlist index and collection index when background import finishes
|
||||||
status: To Do
|
status: Done
|
||||||
assignee: []
|
assignee: []
|
||||||
created_date: "2026-05-08 05:40"
|
created_date: "2026-05-08 05:40"
|
||||||
updated_date: "2026-05-09 06:04"
|
updated_date: "2026-05-14 15:54"
|
||||||
labels:
|
labels:
|
||||||
- ready
|
- ready
|
||||||
dependencies: []
|
dependencies: []
|
||||||
@@ -27,12 +27,12 @@ When importing multiple records, the application performs the import in the back
|
|||||||
|
|
||||||
<!-- AC:BEGIN -->
|
<!-- AC:BEGIN -->
|
||||||
|
|
||||||
- [ ] #1 Importing 2+ records via the AddRecord cart automatically updates the collection index without manual refresh
|
- [x] #1 Importing 2+ records via the AddRecord cart automatically updates the collection index without manual refresh
|
||||||
- [ ] #2 Importing 2+ records via the AddRecord cart automatically updates the wishlist index without manual refresh
|
- [x] #2 Importing 2+ records via the AddRecord cart automatically updates the wishlist index without manual refresh
|
||||||
- [ ] #3 Importing 2+ records via barcode scan automatically updates the collection index without manual refresh
|
- [x] #3 Importing 2+ records via barcode scan automatically updates the collection index without manual refresh
|
||||||
- [ ] #4 Existing import worker tests pass
|
- [x] #4 Existing import worker tests pass
|
||||||
- [ ] #5 New tests verify PubSub broadcast from import workers after success
|
- [x] #5 New tests verify PubSub broadcast from import workers after success
|
||||||
- [ ] #6 No regressions in CollectionLive.Index or WishlistLive.Index behavior
|
- [x] #6 No regressions in CollectionLive.Index or WishlistLive.Index behavior
|
||||||
<!-- AC:END -->
|
<!-- AC:END -->
|
||||||
|
|
||||||
## Implementation Plan
|
## Implementation Plan
|
||||||
@@ -415,4 +415,28 @@ The project uses offset-based pagination (`LIMIT ? OFFSET ?`). When `handle_inde
|
|||||||
## Documentation Updates
|
## Documentation Updates
|
||||||
|
|
||||||
- `docs/architecture.md`: Add `"records:index_changed"` to PubSub Topics table.
|
- `docs/architecture.md`: Add `"records:index_changed"` to PubSub Topics table.
|
||||||
|
|
||||||
|
**2026-05-14 — Implementation complete**
|
||||||
|
|
||||||
|
All 7 steps implemented:
|
||||||
|
|
||||||
|
1. ✅ `broadcast_index_changed/0` added to `lib/music_library/records.ex`
|
||||||
|
2. ✅ `subscribe_to_index/0` added to `lib/music_library/records.ex`
|
||||||
|
3. ✅ `handle_index_changed/1` added to `lib/music_library_web/live_helpers/index_actions.ex` (refreshes `total_entries` before reload)
|
||||||
|
4. ✅ Subscription + guarded `handle_info` in `CollectionLive.Index` (guard: `[:index, :edit]`)
|
||||||
|
5. ✅ Subscription + guarded `handle_info` in `WishlistLive.Index` (guard: `[:index, :edit]`)
|
||||||
|
6. ✅ Both import workers call `Records.broadcast_index_changed()` after `{:ok, _record}`
|
||||||
|
7. ✅ Tests added:
|
||||||
|
- `records_test.exs`: PubSub broadcast/receive round-trip
|
||||||
|
- `import_from_musicbrainz_release_test.exs`: asserts broadcast after success
|
||||||
|
- `import_from_musicbrainz_release_group_test.exs`: asserts broadcast after success
|
||||||
|
- `collection_live/index_test.exs`: reloads stream on `:index`, no-op on `:import`
|
||||||
|
- `wishlist_live/index_test.exs`: reloads stream on `:index`, no-op on `:import`
|
||||||
|
|
||||||
|
Full test suite: **981 passed, 0 failures**
|
||||||
|
|
||||||
|
`docs/architecture.md`: Added `"records:index_changed"` row to PubSub Topics table.
|
||||||
|
|
||||||
|
**Remaining for acceptance criteria #1-#3**: Manual browser verification required — import 2+ records via cart and barcode scan on both collection and wishlist, confirm auto-update and no modal title flicker.
|
||||||
|
|
||||||
<!-- SECTION:NOTES:END -->
|
<!-- SECTION:NOTES:END -->
|
||||||
@@ -250,10 +250,11 @@ HTTP 429 into `:rate_limit` vs `:auth_error` by reading the body `code`
|
|||||||
|
|
||||||
## PubSub Topics
|
## PubSub Topics
|
||||||
|
|
||||||
| PubSub | Topic Pattern | Message | Used By |
|
| PubSub | Topic Pattern | Message | Used By |
|
||||||
| ---------------- | -------------------------- | ------------------- | ---------------------------------------------------------------------------------------------- |
|
| ---------------- | -------------------------- | ------------------------ | ---------------------------------------------------------------------------------------------- |
|
||||||
| `:music_library` | `"records:#{id}"` | `{:update, record}` | CollectionLive.Show, WishlistLive.Show — subscribe in handle_params, unsubscribe on navigation |
|
| `:music_library` | `"records:#{id}"` | `{:update, record}` | CollectionLive.Show, WishlistLive.Show — subscribe in handle_params, unsubscribe on navigation |
|
||||||
| `:music_library` | `"listening_stats:update"` | `%{track_count: n}` | StatsLive.Index, ScrobbledTracksLive.Index — new scrobbles arrived |
|
| `:music_library` | `"records:index_changed"` | `:records_index_changed` | CollectionLive.Index, WishlistLive.Index — auto-refresh when background import completes |
|
||||||
|
| `:music_library` | `"listening_stats:update"` | `%{track_count: n}` | StatsLive.Index, ScrobbledTracksLive.Index — new scrobbles arrived |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -126,4 +126,21 @@ defmodule MusicLibrary.Records do
|
|||||||
{:update, record}
|
{:update, record}
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Broadcasts that the records index has changed (new record imported, deleted, etc.).
|
||||||
|
Index LiveViews subscribe to this topic to auto-refresh.
|
||||||
|
"""
|
||||||
|
@spec broadcast_index_changed() :: :ok
|
||||||
|
def broadcast_index_changed do
|
||||||
|
Phoenix.PubSub.broadcast(MusicLibrary.PubSub, "records:index_changed", :records_index_changed)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Subscribes the calling process to records index change notifications.
|
||||||
|
"""
|
||||||
|
@spec subscribe_to_index() :: :ok | {:error, term()}
|
||||||
|
def subscribe_to_index do
|
||||||
|
Phoenix.PubSub.subscribe(MusicLibrary.PubSub, "records:index_changed")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ defmodule MusicLibrary.Worker.ImportFromMusicbrainzRelease do
|
|||||||
|
|
||||||
use Oban.Worker, queue: :music_brainz, max_attempts: 3
|
use Oban.Worker, queue: :music_brainz, max_attempts: 3
|
||||||
|
|
||||||
|
alias MusicLibrary.Records
|
||||||
alias MusicLibrary.Records.Record
|
alias MusicLibrary.Records.Record
|
||||||
alias MusicLibrary.Worker.ErrorHandler
|
alias MusicLibrary.Worker.ErrorHandler
|
||||||
|
|
||||||
@@ -19,8 +20,12 @@ defmodule MusicLibrary.Worker.ImportFromMusicbrainzRelease do
|
|||||||
]
|
]
|
||||||
|
|
||||||
case MusicLibrary.Records.import_from_musicbrainz_release(release_id, opts) do
|
case MusicLibrary.Records.import_from_musicbrainz_release(release_id, opts) do
|
||||||
{:ok, _record} -> :ok
|
{:ok, _record} ->
|
||||||
other -> ErrorHandler.to_oban_result(other)
|
Records.broadcast_index_changed()
|
||||||
|
:ok
|
||||||
|
|
||||||
|
other ->
|
||||||
|
ErrorHandler.to_oban_result(other)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -23,8 +23,12 @@ defmodule MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroup do
|
|||||||
]
|
]
|
||||||
|
|
||||||
case Records.import_from_musicbrainz_release_group(release_group_id, opts) do
|
case Records.import_from_musicbrainz_release_group(release_group_id, opts) do
|
||||||
{:ok, _record} -> :ok
|
{:ok, _record} ->
|
||||||
other -> ErrorHandler.to_oban_result(other)
|
Records.broadcast_index_changed()
|
||||||
|
:ok
|
||||||
|
|
||||||
|
other ->
|
||||||
|
ErrorHandler.to_oban_result(other)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ defmodule MusicLibraryWeb.CollectionLive.Index do
|
|||||||
|
|
||||||
alias MusicLibrary.Chats
|
alias MusicLibrary.Chats
|
||||||
alias MusicLibrary.Collection
|
alias MusicLibrary.Collection
|
||||||
|
alias MusicLibrary.Records
|
||||||
alias MusicLibraryWeb.Components.AddRecord
|
alias MusicLibraryWeb.Components.AddRecord
|
||||||
alias MusicLibraryWeb.LiveHelpers.IndexActions
|
alias MusicLibraryWeb.LiveHelpers.IndexActions
|
||||||
|
|
||||||
@@ -242,6 +243,10 @@ defmodule MusicLibraryWeb.CollectionLive.Index do
|
|||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def mount(_params, _session, socket) do
|
def mount(_params, _session, socket) do
|
||||||
|
if connected?(socket) do
|
||||||
|
Records.subscribe_to_index()
|
||||||
|
end
|
||||||
|
|
||||||
{:ok,
|
{:ok,
|
||||||
socket
|
socket
|
||||||
|> assign(:current_section, :collection)
|
|> assign(:current_section, :collection)
|
||||||
@@ -300,6 +305,15 @@ defmodule MusicLibraryWeb.CollectionLive.Index do
|
|||||||
{:noreply, assign(socket, :chat_count, chat_count)}
|
{:noreply, assign(socket, :chat_count, chat_count)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_info(:records_index_changed, socket)
|
||||||
|
when socket.assigns.live_action in [:index, :edit] do
|
||||||
|
{:noreply, IndexActions.handle_index_changed(socket)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_info(:records_index_changed, socket) do
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_async(:collection_summary, {:ok, summary}, socket) do
|
def handle_async(:collection_summary, {:ok, summary}, socket) do
|
||||||
{:noreply, assign(socket, :collection_summary, summary)}
|
{:noreply, assign(socket, :collection_summary, summary)}
|
||||||
|
|||||||
@@ -176,6 +176,10 @@ defmodule MusicLibraryWeb.WishlistLive.Index do
|
|||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def mount(_params, _session, socket) do
|
def mount(_params, _session, socket) do
|
||||||
|
if connected?(socket) do
|
||||||
|
Records.subscribe_to_index()
|
||||||
|
end
|
||||||
|
|
||||||
current_date = Date.utc_today()
|
current_date = Date.utc_today()
|
||||||
|
|
||||||
{:ok,
|
{:ok,
|
||||||
@@ -217,6 +221,15 @@ defmodule MusicLibraryWeb.WishlistLive.Index do
|
|||||||
IndexActions.handle_cart_imported_async(socket, count)
|
IndexActions.handle_cart_imported_async(socket, count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_info(:records_index_changed, socket)
|
||||||
|
when socket.assigns.live_action in [:index, :edit] do
|
||||||
|
{:noreply, IndexActions.handle_index_changed(socket)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_info(:records_index_changed, socket) do
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("delete", %{"id" => id}, socket) do
|
def handle_event("delete", %{"id" => id}, socket) do
|
||||||
IndexActions.handle_delete(socket, id)
|
IndexActions.handle_delete(socket, id)
|
||||||
|
|||||||
@@ -146,6 +146,18 @@ defmodule MusicLibraryWeb.LiveHelpers.IndexActions do
|
|||||||
{:noreply, load_and_assign_records(socket, socket.assigns.record_list_params)}
|
{:noreply, load_and_assign_records(socket, socket.assigns.record_list_params)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Handles a PubSub notification that records have changed.
|
||||||
|
Refreshes total_entries and reloads the record stream using the current parameters.
|
||||||
|
"""
|
||||||
|
def handle_index_changed(socket) do
|
||||||
|
config = socket.assigns.index_config
|
||||||
|
params = socket.assigns.record_list_params
|
||||||
|
total_records = config.context_module.search_records_count(params.query)
|
||||||
|
updated_params = %{params | total_entries: total_records}
|
||||||
|
load_and_assign_records(socket, updated_params)
|
||||||
|
end
|
||||||
|
|
||||||
defp record_page_title(record, config) do
|
defp record_page_title(record, config) do
|
||||||
Enum.join(
|
Enum.join(
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -83,4 +83,13 @@ defmodule MusicLibrary.RecordsTest do
|
|||||||
assert expected == Records.get_record!(expected.id)
|
assert expected == Records.get_record!(expected.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "broadcast_index_changed/0 and subscribe_to_index/0" do
|
||||||
|
test "broadcasts :records_index_changed to subscribers" do
|
||||||
|
Records.subscribe_to_index()
|
||||||
|
Records.broadcast_index_changed()
|
||||||
|
|
||||||
|
assert_received :records_index_changed
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ defmodule MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroupTest do
|
|||||||
import MusicBrainz.Fixtures.ReleaseGroup
|
import MusicBrainz.Fixtures.ReleaseGroup
|
||||||
import MusicLibrary.Fixtures.Records
|
import MusicLibrary.Fixtures.Records
|
||||||
|
|
||||||
|
alias MusicLibrary.Records
|
||||||
alias MusicLibrary.Records.Record
|
alias MusicLibrary.Records.Record
|
||||||
alias MusicLibrary.Worker.FetchArtistInfo
|
alias MusicLibrary.Worker.FetchArtistInfo
|
||||||
alias MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroup
|
alias MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroup
|
||||||
@@ -93,5 +94,37 @@ defmodule MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroupTest do
|
|||||||
"purchased_at" => DateTime.to_iso8601(DateTime.utc_now())
|
"purchased_at" => DateTime.to_iso8601(DateTime.utc_now())
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "broadcasts index_changed after successful import" do
|
||||||
|
release_group_data = release_group(:marbles)
|
||||||
|
release_group_id = release_group_id(:marbles)
|
||||||
|
release_group_releases_data = release_group_releases(:marbles)
|
||||||
|
|
||||||
|
cover_data = marbles_cover_data()
|
||||||
|
|
||||||
|
Req.Test.stub(MusicBrainz.API, fn conn ->
|
||||||
|
case conn.path_info do
|
||||||
|
[_ws, _version, "release-group", ^release_group_id] ->
|
||||||
|
Req.Test.json(conn, release_group_data)
|
||||||
|
|
||||||
|
[_ws, _version, "release"] ->
|
||||||
|
Req.Test.json(conn, release_group_releases_data)
|
||||||
|
|
||||||
|
[_release_group, ^release_group_id, "front"] ->
|
||||||
|
Plug.Conn.send_resp(conn, 200, cover_data)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
Records.subscribe_to_index()
|
||||||
|
|
||||||
|
assert :ok =
|
||||||
|
perform_job(ImportFromMusicbrainzReleaseGroup, %{
|
||||||
|
"release_group_id" => release_group_id,
|
||||||
|
"format" => "cd",
|
||||||
|
"purchased_at" => DateTime.to_iso8601(DateTime.utc_now())
|
||||||
|
})
|
||||||
|
|
||||||
|
assert_received :records_index_changed
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ defmodule MusicLibrary.Worker.ImportFromMusicbrainzReleaseTest do
|
|||||||
import MusicBrainz.Fixtures.ReleaseGroup
|
import MusicBrainz.Fixtures.ReleaseGroup
|
||||||
import MusicLibrary.Fixtures.Records
|
import MusicLibrary.Fixtures.Records
|
||||||
|
|
||||||
|
alias MusicLibrary.Records
|
||||||
alias MusicLibrary.Records.Record
|
alias MusicLibrary.Records.Record
|
||||||
alias MusicLibrary.Worker.ImportFromMusicbrainzRelease
|
alias MusicLibrary.Worker.ImportFromMusicbrainzRelease
|
||||||
|
|
||||||
@@ -63,5 +64,44 @@ defmodule MusicLibrary.Worker.ImportFromMusicbrainzReleaseTest do
|
|||||||
"selected_release_id" => "nonexistent-release-id"
|
"selected_release_id" => "nonexistent-release-id"
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "broadcasts index_changed after successful import" do
|
||||||
|
release_data = release(:marbles)
|
||||||
|
release_id = release_id(:marbles)
|
||||||
|
|
||||||
|
release_group_data = release_group(:marbles)
|
||||||
|
release_group_id = release_group_id(:marbles)
|
||||||
|
release_group_releases_data = release_group_releases(:marbles)
|
||||||
|
|
||||||
|
cover_data = marbles_cover_data()
|
||||||
|
|
||||||
|
Req.Test.stub(MusicBrainz.API, fn conn ->
|
||||||
|
case conn.path_info do
|
||||||
|
[_ws, _version, "release-group", ^release_group_id] ->
|
||||||
|
Req.Test.json(conn, release_group_data)
|
||||||
|
|
||||||
|
[_ws, _version, "release", ^release_id] ->
|
||||||
|
Req.Test.json(conn, release_data)
|
||||||
|
|
||||||
|
[_ws, _version, "release"] ->
|
||||||
|
Req.Test.json(conn, release_group_releases_data)
|
||||||
|
|
||||||
|
[_release_group, ^release_group_id, "front"] ->
|
||||||
|
Plug.Conn.send_resp(conn, 200, cover_data)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
Records.subscribe_to_index()
|
||||||
|
|
||||||
|
assert :ok =
|
||||||
|
perform_job(ImportFromMusicbrainzRelease, %{
|
||||||
|
"release_id" => release_id,
|
||||||
|
"format" => "cd",
|
||||||
|
"purchased_at" => DateTime.to_iso8601(DateTime.utc_now()),
|
||||||
|
"selected_release_id" => release_id
|
||||||
|
})
|
||||||
|
|
||||||
|
assert_received :records_index_changed
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ defmodule MusicLibraryWeb.CollectionLive.IndexTest do
|
|||||||
alias MusicLibrary.Assets.{Image, Transform}
|
alias MusicLibrary.Assets.{Image, Transform}
|
||||||
alias MusicLibrary.Records.Record
|
alias MusicLibrary.Records.Record
|
||||||
alias MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroup
|
alias MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroup
|
||||||
|
alias Phoenix.LiveViewTest
|
||||||
alias Req.Test
|
alias Req.Test
|
||||||
|
|
||||||
# make it a multiple of 4 for easier calculations
|
# make it a multiple of 4 for easier calculations
|
||||||
@@ -93,6 +94,37 @@ defmodule MusicLibraryWeb.CollectionLive.IndexTest do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# Create a new record behind the scenes (simulating completed background import)
|
||||||
|
_new_record = record()
|
||||||
|
|
||||||
|
# Send the index_changed message directly
|
||||||
|
send(view.pid, :records_index_changed)
|
||||||
|
|
||||||
|
# The view should now include the new record
|
||||||
|
html_after = LiveViewTest.render(view)
|
||||||
|
assert html_after != html_before
|
||||||
|
end
|
||||||
|
|
||||||
|
test "ignores message when live_action is :import (guard clause)", %{conn: conn} do
|
||||||
|
{:ok, view, _html} = LiveViewTest.live(conn, ~p"/collection/import")
|
||||||
|
|
||||||
|
html_before = LiveViewTest.render(view)
|
||||||
|
|
||||||
|
send(view.pid, :records_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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "Search and pagination" do
|
describe "Search and pagination" do
|
||||||
setup [:fill_collection]
|
setup [:fill_collection]
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ defmodule MusicLibraryWeb.WishlistLive.IndexTest do
|
|||||||
|
|
||||||
alias MusicLibrary.Records.Record
|
alias MusicLibrary.Records.Record
|
||||||
alias MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroup
|
alias MusicLibrary.Worker.ImportFromMusicbrainzReleaseGroup
|
||||||
|
alias Phoenix.LiveViewTest
|
||||||
alias Req.Test
|
alias Req.Test
|
||||||
|
|
||||||
defp fill_wishlist(_) do
|
defp fill_wishlist(_) do
|
||||||
@@ -16,6 +17,37 @@ defmodule MusicLibraryWeb.WishlistLive.IndexTest do
|
|||||||
%{wishlist: records}
|
%{wishlist: records}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# Create a new wishlist record behind the scenes (simulating completed background import)
|
||||||
|
_new_record = record(%{purchased_at: nil})
|
||||||
|
|
||||||
|
# Send the index_changed message directly
|
||||||
|
send(view.pid, :records_index_changed)
|
||||||
|
|
||||||
|
# The view should now include the new record
|
||||||
|
html_after = LiveViewTest.render(view)
|
||||||
|
assert html_after != html_before
|
||||||
|
end
|
||||||
|
|
||||||
|
test "ignores message when live_action is :import (guard clause)", %{conn: conn} do
|
||||||
|
{:ok, view, _html} = LiveViewTest.live(conn, ~p"/wishlist/import")
|
||||||
|
|
||||||
|
html_before = LiveViewTest.render(view)
|
||||||
|
|
||||||
|
send(view.pid, :records_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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "Wishlist" do
|
describe "Wishlist" do
|
||||||
setup [:fill_wishlist]
|
setup [:fill_wishlist]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user