SubmitLive now hands off to the async pipeline:
- confirm_csv/confirm_adif → CsvImport.enqueue/2 → push_navigate to
/imports/:id. ADIF preview shape matches CSV, so one enqueue call
serves both.
- allow_upload(auto_upload: false) so bytes only stream when the user
clicks submit. Progress bar + percentage now gated on
entry.progress > 0 and < 100 — invisible during file selection,
only rendered while the actual upload is in-flight.
- Old in-page csv_results / @csv_result / reset_csv removed; the
/imports/:id page replaces them.
New ImportLive at /imports/🆔 subscribes to 'csv_import:<id>' PubSub,
shows total/processed/imported/refined/error counters in a daisyUI
stats grid, progress bar, status badge, and a collapsed errors table
when error_count > 0. Missing or malformed run_id redirects to /submit
with a flash.
7 new frontend tests (5 ImportLive, 2 updated SubmitLive), 1902 total,
credo clean.
137 lines
3.8 KiB
Elixir
137 lines
3.8 KiB
Elixir
defmodule MicrowavepropWeb.ImportLiveTest do
|
|
use MicrowavepropWeb.ConnCase, async: true
|
|
|
|
import Phoenix.LiveViewTest
|
|
|
|
alias Microwaveprop.Radio.ImportRun
|
|
alias Microwaveprop.Repo
|
|
|
|
defp insert_run(attrs) do
|
|
defaults = %{
|
|
submitter_email: "importer@example.com",
|
|
rows: %{"rows" => [], "refinement_ids" => []},
|
|
total_rows: 10,
|
|
processed_rows: 0,
|
|
imported_count: 0,
|
|
refined_count: 0,
|
|
error_count: 0,
|
|
errors: %{},
|
|
status: "pending"
|
|
}
|
|
|
|
{:ok, run} =
|
|
%ImportRun{}
|
|
|> ImportRun.changeset(Map.merge(defaults, attrs))
|
|
|> Repo.insert()
|
|
|
|
run
|
|
end
|
|
|
|
describe "mount" do
|
|
test "renders the initial state of a pending import", %{conn: conn} do
|
|
run = insert_run(%{total_rows: 25, submitter_email: "alice@example.com"})
|
|
|
|
{:ok, _lv, html} = live(conn, ~p"/imports/#{run.id}")
|
|
|
|
assert html =~ "Contact Import"
|
|
assert html =~ "alice@example.com"
|
|
assert html =~ "25"
|
|
assert html =~ ~r/id="import-status-badge"[^>]*>\s*Pending/
|
|
assert html =~ ~s(id="import-progress-bar")
|
|
assert html =~ ~s(id="import-imported-count")
|
|
assert html =~ ~s(id="import-refined-count")
|
|
assert html =~ ~s(id="import-error-count")
|
|
end
|
|
|
|
test "redirects to /submit with a flash when the import is not found", %{conn: conn} do
|
|
missing_id = Ecto.UUID.generate()
|
|
|
|
assert {:error, {:live_redirect, %{to: "/submit", flash: flash}}} =
|
|
live(conn, ~p"/imports/#{missing_id}")
|
|
|
|
assert flash["error"] =~ "Import not found"
|
|
end
|
|
|
|
test "redirects to /submit for an obviously bad id", %{conn: conn} do
|
|
assert {:error, {:live_redirect, %{to: "/submit"}}} =
|
|
live(conn, ~p"/imports/not-a-uuid")
|
|
end
|
|
end
|
|
|
|
describe "live updates" do
|
|
test "updates counters when an import_progress message is broadcast", %{conn: conn} do
|
|
run = insert_run(%{total_rows: 100})
|
|
|
|
{:ok, lv, _html} = live(conn, ~p"/imports/#{run.id}")
|
|
|
|
updated = %{
|
|
run
|
|
| processed_rows: 50,
|
|
imported_count: 48,
|
|
refined_count: 1,
|
|
error_count: 1,
|
|
status: "running",
|
|
errors: %{"7" => ["band is invalid"]}
|
|
}
|
|
|
|
Phoenix.PubSub.broadcast(
|
|
Microwaveprop.PubSub,
|
|
"csv_import:#{run.id}",
|
|
{:import_progress, run.id, updated}
|
|
)
|
|
|
|
html = render(lv)
|
|
|
|
assert html =~ ~r/id="import-imported-count"[^>]*>\s*48/
|
|
assert html =~ ~r/id="import-refined-count"[^>]*>\s*1/
|
|
assert html =~ ~r/id="import-error-count"[^>]*>\s*1/
|
|
assert html =~ ~r/id="import-status-badge"[^>]*>\s*Running/
|
|
assert html =~ "band is invalid"
|
|
end
|
|
|
|
test "shows the complete UI once status flips to complete", %{conn: conn} do
|
|
run = insert_run(%{total_rows: 3})
|
|
|
|
{:ok, lv, _html} = live(conn, ~p"/imports/#{run.id}")
|
|
|
|
done = %{
|
|
run
|
|
| processed_rows: 3,
|
|
imported_count: 3,
|
|
status: "complete",
|
|
completed_at: DateTime.utc_now()
|
|
}
|
|
|
|
Phoenix.PubSub.broadcast(
|
|
Microwaveprop.PubSub,
|
|
"csv_import:#{run.id}",
|
|
{:import_progress, run.id, done}
|
|
)
|
|
|
|
html = render(lv)
|
|
|
|
assert html =~ "Import complete"
|
|
assert html =~ ~s(id="import-complete-panel")
|
|
assert html =~ "View contacts"
|
|
assert html =~ ~r/id="import-status-badge"[^>]*>\s*Complete/
|
|
end
|
|
|
|
test "shows the errors section when error_count > 0", %{conn: conn} do
|
|
run =
|
|
insert_run(%{
|
|
total_rows: 2,
|
|
processed_rows: 2,
|
|
imported_count: 1,
|
|
error_count: 1,
|
|
status: "complete",
|
|
errors: %{"2" => ["band is invalid"]}
|
|
})
|
|
|
|
{:ok, _lv, html} = live(conn, ~p"/imports/#{run.id}")
|
|
|
|
assert html =~ ~s(id="import-errors-section")
|
|
assert html =~ "Row 2"
|
|
assert html =~ "band is invalid"
|
|
end
|
|
end
|
|
end
|