Fix toasts not appearing for notes and barcode scan
This commit is contained in:
@@ -468,11 +468,12 @@ defmodule MusicLibraryWeb.Components.BarcodeScanner do
|
|||||||
"Failed to search release for barcode #{number}: #{inspect(reason)}"
|
"Failed to search release for barcode #{number}: #{inspect(reason)}"
|
||||||
end)
|
end)
|
||||||
|
|
||||||
put_toast(
|
put_toast!(
|
||||||
socket,
|
|
||||||
:error,
|
:error,
|
||||||
gettext("Failed to search release for barcode %{number}", number: number)
|
gettext("Failed to search release for barcode %{number}", number: number)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
socket
|
||||||
end
|
end
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
@@ -509,7 +510,8 @@ defmodule MusicLibraryWeb.Components.BarcodeScanner do
|
|||||||
|
|
||||||
socket
|
socket
|
||||||
|> maybe_toast_errors(sync_errors)
|
|> maybe_toast_errors(sync_errors)
|
||||||
|> put_toast(
|
|
||||||
|
put_toast!(
|
||||||
:info,
|
:info,
|
||||||
ngettext(
|
ngettext(
|
||||||
"Importing %{count} record in the background...",
|
"Importing %{count} record in the background...",
|
||||||
@@ -518,10 +520,13 @@ defmodule MusicLibraryWeb.Components.BarcodeScanner do
|
|||||||
count: async_count
|
count: async_count
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
socket
|
||||||
else
|
else
|
||||||
case BarcodeScan.import_results(scan_results, current_time) do
|
case BarcodeScan.import_results(scan_results, current_time) do
|
||||||
[] ->
|
[] ->
|
||||||
put_toast(socket, :info, gettext("Records imported successfully"))
|
put_toast!(:info, gettext("Records imported successfully"))
|
||||||
|
socket
|
||||||
|
|
||||||
errors ->
|
errors ->
|
||||||
maybe_toast_errors(socket, errors)
|
maybe_toast_errors(socket, errors)
|
||||||
@@ -544,11 +549,12 @@ defmodule MusicLibraryWeb.Components.BarcodeScanner do
|
|||||||
"#{number}: #{ErrorMessages.friendly_message(reason)}"
|
"#{number}: #{ErrorMessages.friendly_message(reason)}"
|
||||||
end)
|
end)
|
||||||
|
|
||||||
put_toast(
|
put_toast!(
|
||||||
socket,
|
|
||||||
:error,
|
:error,
|
||||||
gettext("Some records could not be imported: %{summary}", summary: errors_summary)
|
gettext("Some records could not be imported: %{summary}", summary: errors_summary)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
socket
|
||||||
end
|
end
|
||||||
|
|
||||||
defp release_format_label(release) do
|
defp release_format_label(release) do
|
||||||
|
|||||||
@@ -117,14 +117,15 @@ defmodule MusicLibraryWeb.Components.Notes do
|
|||||||
defp update_note(note_params, socket) do
|
defp update_note(note_params, socket) do
|
||||||
case Notes.update_note(socket.assigns.note, note_params) do
|
case Notes.update_note(socket.assigns.note, note_params) do
|
||||||
{:ok, note} ->
|
{:ok, note} ->
|
||||||
|
put_toast!(:info, gettext("Note updated successfully"))
|
||||||
|
|
||||||
changeset =
|
changeset =
|
||||||
Notes.change_note(note, %{})
|
Notes.change_note(note, %{})
|
||||||
|
|
||||||
{:noreply,
|
{:noreply,
|
||||||
socket
|
socket
|
||||||
|> assign(:note, note)
|
|> assign(:note, note)
|
||||||
|> assign(:form, to_form(changeset))
|
|> assign(:form, to_form(changeset))}
|
||||||
|> put_toast(:info, gettext("Note updated successfully"))}
|
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{} = changeset} ->
|
{:error, %Ecto.Changeset{} = changeset} ->
|
||||||
{:noreply, assign(socket, form: to_form(changeset))}
|
{:noreply, assign(socket, form: to_form(changeset))}
|
||||||
@@ -134,14 +135,15 @@ defmodule MusicLibraryWeb.Components.Notes do
|
|||||||
defp create_note(note_params, socket) do
|
defp create_note(note_params, socket) do
|
||||||
case Notes.create_note(socket.assigns.note, note_params) do
|
case Notes.create_note(socket.assigns.note, note_params) do
|
||||||
{:ok, note} ->
|
{:ok, note} ->
|
||||||
|
put_toast!(:info, gettext("Note created successfully"))
|
||||||
|
|
||||||
changeset =
|
changeset =
|
||||||
Notes.change_note(note, %{})
|
Notes.change_note(note, %{})
|
||||||
|
|
||||||
{:noreply,
|
{:noreply,
|
||||||
socket
|
socket
|
||||||
|> assign(:note, note)
|
|> assign(:note, note)
|
||||||
|> assign(:form, to_form(changeset))
|
|> assign(:form, to_form(changeset))}
|
||||||
|> put_toast(:info, gettext("Note created successfully"))}
|
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{} = changeset} ->
|
{:error, %Ecto.Changeset{} = changeset} ->
|
||||||
{:noreply, assign(socket, form: to_form(changeset))}
|
{:noreply, assign(socket, form: to_form(changeset))}
|
||||||
|
|||||||
@@ -571,11 +571,13 @@ defmodule MusicLibraryWeb.CollectionLive.IndexTest do
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
# With a transport_error stub, the scan should fail gracefully.
|
# With a transport_error stub, the scan should fail gracefully.
|
||||||
# The cart should remain empty (no results added).
|
# The cart should remain empty (no results added) and an error
|
||||||
|
# toast should be displayed.
|
||||||
conn
|
conn
|
||||||
|> visit(~p"/collection/scan")
|
|> visit(~p"/collection/scan")
|
||||||
|> trigger_hook("#barcode-scanner", "barcode_scanned", %{"number" => barcode})
|
|> trigger_hook("#barcode-scanner", "barcode_scanned", %{"number" => barcode})
|
||||||
|> assert_has("#cart-empty")
|
|> assert_has("#cart-empty")
|
||||||
|
|> assert_has("#toast-group", text: "Failed to search release for barcode 123")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "removes one scanned result from the cart", %{conn: conn} do
|
test "removes one scanned result from the cart", %{conn: conn} do
|
||||||
@@ -646,13 +648,12 @@ defmodule MusicLibraryWeb.CollectionLive.IndexTest do
|
|||||||
assert_has(session, "#cart-empty")
|
assert_has(session, "#cart-empty")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "enqueues import jobs for 2+ new releases via context function" do
|
test "enqueues import jobs for 2+ new releases via UI", %{conn: conn} do
|
||||||
barcode1 = "5037300650128"
|
barcode1 = "5037300650128"
|
||||||
barcode2 = "1234567890123"
|
barcode2 = "1234567890123"
|
||||||
releases = releases(:marbles)
|
releases = releases(:marbles)
|
||||||
|
|
||||||
# Ensure clean MusicBrainz stub state
|
# Stub MusicBrainz for both barcode scans
|
||||||
# The barcode search query includes "barcode:NUMBER AND NOT format:digitalmedia"
|
|
||||||
Test.stub(MusicBrainz.API, fn conn ->
|
Test.stub(MusicBrainz.API, fn conn ->
|
||||||
query = conn.params["query"] || ""
|
query = conn.params["query"] || ""
|
||||||
|
|
||||||
@@ -663,26 +664,23 @@ defmodule MusicLibraryWeb.CollectionLive.IndexTest do
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
# Scan both barcodes to get scan results
|
# Trigger both barcode scans through the UI hook
|
||||||
{:ok, result1} = MusicLibrary.BarcodeScan.scan(barcode1)
|
session =
|
||||||
{:ok, result2} = MusicLibrary.BarcodeScan.scan(barcode2)
|
conn
|
||||||
|
|> visit(~p"/collection/scan")
|
||||||
|
|> trigger_hook("#barcode-scanner", "barcode_scanned", %{"number" => barcode1})
|
||||||
|
|> trigger_hook("#barcode-scanner", "barcode_scanned", %{"number" => barcode2})
|
||||||
|
|
||||||
results = [result1, result2]
|
assert_has(session, "#cart-items li", count: 2)
|
||||||
|
|
||||||
# Both should be new results (no existing records)
|
# Click the "Add 2 releases" button (the button is in the cart sidebar)
|
||||||
assert MusicLibrary.BarcodeScan.should_import_async?(results)
|
session
|
||||||
|
|> click_button("Add 2 releases")
|
||||||
|
|
||||||
# Import async should succeed and enqueue jobs
|
# Verify exactly two distinct import jobs were enqueued
|
||||||
current_time = DateTime.utc_now()
|
enqueued = all_enqueued(worker: ImportFromMusicbrainzRelease)
|
||||||
|
assert length(enqueued) == 2
|
||||||
{:ok, [], async_count} =
|
assert Enum.all?(enqueued, & &1.args["release_id"])
|
||||||
MusicLibrary.BarcodeScan.import_results_async(results, current_time)
|
|
||||||
|
|
||||||
assert async_count == 2
|
|
||||||
|
|
||||||
# Verify jobs were enqueued
|
|
||||||
assert_enqueued(worker: ImportFromMusicbrainzRelease)
|
|
||||||
assert_enqueued(worker: ImportFromMusicbrainzRelease)
|
|
||||||
|
|
||||||
# No records should have been inserted synchronously
|
# No records should have been inserted synchronously
|
||||||
assert MusicLibrary.Repo.all(Record) == []
|
assert MusicLibrary.Repo.all(Record) == []
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ defmodule MusicLibraryWeb.CollectionLive.ShowTest do
|
|||||||
only: [
|
only: [
|
||||||
render_click: 1,
|
render_click: 1,
|
||||||
render_hook: 3,
|
render_hook: 3,
|
||||||
|
render_submit: 1,
|
||||||
|
form: 3,
|
||||||
element: 2
|
element: 2
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -453,19 +455,25 @@ defmodule MusicLibraryWeb.CollectionLive.ShowTest do
|
|||||||
%{record: record}
|
%{record: record}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "creates a new note through the Notes context", %{record: record} do
|
test "creates a new note through the Notes component form", %{conn: conn, record: record} do
|
||||||
# No note exists yet
|
# No note exists yet
|
||||||
assert Notes.get_note(:record, record.musicbrainz_id) == nil
|
assert Notes.get_note(:record, record.musicbrainz_id) == nil
|
||||||
|
|
||||||
{:ok, note} =
|
session =
|
||||||
Notes.create_note(
|
conn
|
||||||
%MusicLibrary.Notes.Note{entity: :record, musicbrainz_id: record.musicbrainz_id},
|
|> visit(~p"/collection/#{record.id}")
|
||||||
%{"content" => "My test note content"}
|
|> render_async()
|
||||||
)
|
|
||||||
|
|
||||||
assert note.content == "My test note content"
|
# The Notes component starts in "edit" mode when no note exists.
|
||||||
assert note.entity == :record
|
# Submit the notes form (always in the DOM, even when the Fluxon sheet is hidden).
|
||||||
assert note.musicbrainz_id == record.musicbrainz_id
|
session =
|
||||||
|
unwrap(session, fn view ->
|
||||||
|
view
|
||||||
|
|> form("#notes-form", %{"note" => %{"content" => "My test note content"}})
|
||||||
|
|> render_submit()
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert_has(session, "#toast-group", text: "Note created successfully")
|
||||||
|
|
||||||
# Verify persistence
|
# Verify persistence
|
||||||
fetched = Notes.get_note(:record, record.musicbrainz_id)
|
fetched = Notes.get_note(:record, record.musicbrainz_id)
|
||||||
@@ -489,16 +497,38 @@ defmodule MusicLibraryWeb.CollectionLive.ShowTest do
|
|||||||
assert_has(session, "#read-panel article", text: "Existing note content")
|
assert_has(session, "#read-panel article", text: "Existing note content")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "updates existing note content through the Notes context", %{record: record} do
|
test "updates existing note content through the Notes component form", %{
|
||||||
|
conn: conn,
|
||||||
|
record: record
|
||||||
|
} do
|
||||||
# Pre-create a note
|
# Pre-create a note
|
||||||
{:ok, note} =
|
{:ok, _note} =
|
||||||
Notes.create_note(
|
Notes.create_note(
|
||||||
%MusicLibrary.Notes.Note{entity: :record, musicbrainz_id: record.musicbrainz_id},
|
%MusicLibrary.Notes.Note{entity: :record, musicbrainz_id: record.musicbrainz_id},
|
||||||
%{"content" => "Original content"}
|
%{"content" => "Original content"}
|
||||||
)
|
)
|
||||||
|
|
||||||
{:ok, updated} = Notes.update_note(note, %{"content" => "Updated content"})
|
session =
|
||||||
assert updated.content == "Updated content"
|
conn
|
||||||
|
|> visit(~p"/collection/#{record.id}")
|
||||||
|
|> render_async()
|
||||||
|
|
||||||
|
# Switch to "edit" tab inside the Notes component
|
||||||
|
session =
|
||||||
|
unwrap(session, fn view ->
|
||||||
|
view
|
||||||
|
|> element("button[aria-controls='edit-panel']")
|
||||||
|
|> render_click()
|
||||||
|
end)
|
||||||
|
|
||||||
|
# Submit updated content through the form
|
||||||
|
session
|
||||||
|
|> unwrap(fn view ->
|
||||||
|
view
|
||||||
|
|> form("#notes-form", %{"note" => %{"content" => "Updated content"}})
|
||||||
|
|> render_submit()
|
||||||
|
end)
|
||||||
|
|> assert_has("#toast-group", text: "Note updated successfully")
|
||||||
|
|
||||||
# Verify persistence
|
# Verify persistence
|
||||||
fetched = Notes.get_note(:record, record.musicbrainz_id)
|
fetched = Notes.get_note(:record, record.musicbrainz_id)
|
||||||
|
|||||||
Reference in New Issue
Block a user