prop/test/microwaveprop_web/live/submit_live_test.exs
Graham McIntire 18a291555c
Add CSV bulk upload to /submit page
Adds a tabbed UI with single-contact form and CSV upload. Users can
download a sample CSV, upload their file, and get partial import with
per-row error reporting. Enrichment jobs enqueue for each imported contact.
2026-04-01 15:05:51 -05:00

215 lines
5.8 KiB
Elixir

defmodule MicrowavepropWeb.SubmitLiveTest do
use MicrowavepropWeb.ConnCase, async: true
import Phoenix.LiveViewTest
alias Microwaveprop.Radio.Contact
alias Microwaveprop.Repo
alias Microwaveprop.Terrain.ElevationClient
alias Microwaveprop.Weather.HrrrClient
alias Microwaveprop.Weather.IemClient
setup do
Req.Test.stub(IemClient, fn conn ->
case conn.request_path do
"/cgi-bin/request/asos.py" -> Req.Test.text(conn, "#DEBUG,\nstation,valid,tmpf\n")
_ -> Req.Test.json(conn, %{"profiles" => []})
end
end)
Req.Test.stub(HrrrClient, fn conn ->
Plug.Conn.send_resp(conn, 404, "not found")
end)
Req.Test.stub(ElevationClient, fn conn ->
params = Plug.Conn.fetch_query_params(conn).query_params
lat_count = params["latitude"] |> String.split(",") |> length()
Req.Test.json(conn, %{"elevation" => List.duplicate(200.0, lat_count)})
end)
:ok
end
describe "mount" do
test "renders the submission form", %{conn: conn} do
{:ok, _lv, html} = live(conn, ~p"/submit")
assert html =~ "Submit Contact"
assert html =~ "Station 1"
assert html =~ "Station 2"
assert html =~ "Grid 1"
assert html =~ "Grid 2"
assert html =~ "Band"
assert html =~ "Mode"
end
end
describe "validate" do
test "shows validation errors on change", %{conn: conn} do
{:ok, lv, _html} = live(conn, ~p"/submit")
html =
lv
|> form("#contact-form", contact: %{station1: ""})
|> render_change()
assert html =~ "can't be blank"
end
end
describe "csv upload" do
test "renders CSV upload tab and sample download link", %{conn: conn} do
{:ok, lv, _html} = live(conn, ~p"/submit")
html =
lv
|> element("[phx-value-tab=csv]")
|> render_click()
assert html =~ "Upload CSV"
assert html =~ "/downloads/sample_contacts.csv"
assert html =~ "Download sample CSV"
end
test "uploads valid CSV and shows import count", %{conn: conn} do
{:ok, lv, _html} = live(conn, ~p"/submit")
lv
|> element("[phx-value-tab=csv]")
|> render_click()
csv_content =
"station1,station2,grid1,grid2,band,mode,qso_timestamp\nW5XD,K5TR,EM12,EM00,10000,CW,2026-03-28T18:00:00Z\n"
csv_input =
file_input(lv, "#csv-upload-form", :csv, [
%{name: "contacts.csv", content: csv_content, type: "text/csv"}
])
render_upload(csv_input, "contacts.csv")
html =
lv
|> form("#csv-upload-form", %{submitter_email: "test@example.com"})
|> render_submit()
assert html =~ "1 contact imported"
end
test "shows errors for invalid rows alongside successful imports", %{conn: conn} do
{:ok, lv, _html} = live(conn, ~p"/submit")
lv
|> element("[phx-value-tab=csv]")
|> render_click()
csv_content =
"station1,station2,grid1,grid2,band,mode,qso_timestamp\n" <>
"W5XD,K5TR,EM12,EM00,10000,CW,2026-03-28T18:00:00Z\n" <>
",,,,,,\n"
csv_input =
file_input(lv, "#csv-upload-form", :csv, [
%{name: "contacts.csv", content: csv_content, type: "text/csv"}
])
render_upload(csv_input, "contacts.csv")
html =
lv
|> form("#csv-upload-form", %{submitter_email: "test@example.com"})
|> render_submit()
assert html =~ "1 contact imported"
assert html =~ "1 row had errors"
assert html =~ "Row 3"
end
test "requires email for CSV upload", %{conn: conn} do
{:ok, lv, _html} = live(conn, ~p"/submit")
lv
|> element("[phx-value-tab=csv]")
|> render_click()
csv_content =
"station1,station2,grid1,grid2,band,mode,qso_timestamp\nW5XD,K5TR,EM12,EM00,10000,CW,2026-03-28T18:00:00Z\n"
csv_input =
file_input(lv, "#csv-upload-form", :csv, [
%{name: "contacts.csv", content: csv_content, type: "text/csv"}
])
render_upload(csv_input, "contacts.csv")
html =
lv
|> form("#csv-upload-form", %{submitter_email: ""})
|> render_submit()
assert html =~ "Email is required for CSV upload"
end
test "handles CSV with no data rows", %{conn: conn} do
{:ok, lv, _html} = live(conn, ~p"/submit")
lv
|> element("[phx-value-tab=csv]")
|> render_click()
csv_content = "station1,station2,grid1,grid2,band,mode,qso_timestamp\n"
csv_input =
file_input(lv, "#csv-upload-form", :csv, [
%{name: "header_only.csv", content: csv_content, type: "text/csv"}
])
render_upload(csv_input, "header_only.csv")
html =
lv
|> form("#csv-upload-form", %{submitter_email: "test@example.com"})
|> render_submit()
assert html =~ "CSV file has no data rows"
end
end
describe "save" do
test "creates QSO and redirects on valid submit", %{conn: conn} do
{:ok, lv, _html} = live(conn, ~p"/submit")
lv
|> form("#contact-form",
contact: %{
station1: "W5XD",
station2: "K5TR",
qso_timestamp: "2026-03-28T18:00",
mode: "CW",
band: "1296",
grid1: "EM12",
grid2: "EM00",
submitter_email: "test@example.com"
}
)
|> render_submit()
contact = Repo.one!(Contact)
assert contact.station1 == "W5XD"
assert contact.user_submitted == true
assert contact.pos1["lat"]
assert_redirect(lv, ~p"/contacts/#{contact.id}")
end
test "shows errors on invalid submit", %{conn: conn} do
{:ok, lv, _html} = live(conn, ~p"/submit")
html =
lv
|> form("#contact-form", contact: %{station1: ""})
|> render_submit()
assert html =~ "can&#39;t be blank"
end
end
end