prop/test/microwaveprop_web/live/contact_live_test.exs
Graham McIntire a40ea0d62d Show submitter callsign on the contacts index
When a contact is linked to a logged-in user account, display that
user's callsign in the Submitted column instead of a generic "Yes".
Anonymous submissions still show "Yes"; scraped contacts show an em
dash.
2026-04-09 14:02:30 -05:00

194 lines
6 KiB
Elixir

defmodule MicrowavepropWeb.ContactLiveTest do
use MicrowavepropWeb.ConnCase, async: true
import Microwaveprop.AccountsFixtures
import Phoenix.LiveViewTest
alias Microwaveprop.Radio.Contact
alias Microwaveprop.Repo
setup do
Req.Test.stub(Microwaveprop.Terrain.ElevationClient, fn conn ->
Req.Test.json(conn, [])
end)
Req.Test.stub(Microwaveprop.Weather.HrrrClient, fn conn ->
Plug.Conn.send_resp(conn, 404, "not found")
end)
Req.Test.stub(Microwaveprop.Weather.SolarClient, fn conn ->
Req.Test.text(conn, "")
end)
:ok
end
defp create_contact(attrs \\ %{}) do
default = %{
station1: "W5XD",
station2: "K5TR",
qso_timestamp: ~U[2026-03-28 18:00:00Z],
mode: "CW",
band: Decimal.new("1296"),
grid1: "EM12",
grid2: "EM00",
pos1: %{"lat" => 32.9, "lon" => -97.0},
pos2: %{"lat" => 30.3, "lon" => -97.7},
distance_km: Decimal.new("295")
}
{changeset_attrs, direct_attrs} = Map.split(Map.merge(default, attrs), [:user_submitted])
{:ok, contact} =
%Contact{}
|> Contact.changeset(direct_attrs)
|> Ecto.Changeset.change(changeset_attrs)
|> Repo.insert()
contact
end
describe "Index" do
test "renders page with Contacts heading", %{conn: conn} do
{:ok, _lv, html} = live(conn, ~p"/contacts")
assert html =~ "Contacts"
end
test "table shows contact data", %{conn: conn} do
create_contact()
{:ok, _lv, html} = live(conn, ~p"/contacts")
assert html =~ "W5XD"
assert html =~ "K5TR"
assert html =~ "CW"
assert html =~ "1296"
end
test "row links to detail page", %{conn: conn} do
contact = create_contact()
{:ok, _lv, html} = live(conn, ~p"/contacts")
assert html =~ ~p"/contacts/#{contact.id}"
end
test "sort params order the table", %{conn: conn} do
create_contact(%{station1: "ZZ9ZZ"})
create_contact(%{station1: "AA1AA"})
{:ok, _lv, html} = live(conn, ~p"/contacts?sort_by=station1&sort_order=asc")
# AA1AA should appear before ZZ9ZZ
aa_pos = html |> :binary.match("AA1AA") |> elem(0)
zz_pos = html |> :binary.match("ZZ9ZZ") |> elem(0)
assert aa_pos < zz_pos
end
test "search by single callsign matches either station", %{conn: conn} do
create_contact(%{station1: "W5LUA", station2: "W5HN"})
create_contact(%{station1: "K5TR", station2: "N5AC"})
{:ok, _lv, html} = live(conn, ~p"/contacts?search=W5LUA")
assert html =~ "W5LUA"
refute html =~ "K5TR"
end
test "search by two callsigns finds contacts between that pair", %{conn: conn} do
create_contact(%{station1: "W5LUA", station2: "W5HN"})
create_contact(%{station1: "W5HN", station2: "W5LUA"})
create_contact(%{station1: "W5LUA", station2: "K5TR"})
{:ok, _lv, html} = live(conn, ~p"/contacts?search=W5LUA+W5HN")
assert html =~ "W5LUA"
assert html =~ "W5HN"
# The W5LUA-K5TR contact should not appear
refute html =~ "K5TR"
end
test "pagination works", %{conn: conn} do
for i <- 1..25 do
ts = DateTime.add(~U[2026-01-01 00:00:00Z], i * 3600, :second)
create_contact(%{qso_timestamp: ts})
end
{:ok, _lv, html} = live(conn, ~p"/contacts")
assert html =~ "Page 1 of 2"
{:ok, _lv, html2} = live(conn, ~p"/contacts?page=2")
assert html2 =~ "Page 2 of 2"
end
test "Submitted column shows the user's callsign when linked to an account", %{conn: conn} do
user = user_fixture(%{callsign: "W5UNIQ"})
create_contact(%{station1: "K5ONE", user_submitted: true, user_id: user.id})
{:ok, _lv, html} = live(conn, ~p"/contacts?search=K5ONE")
assert html =~ "W5UNIQ"
end
test "Submitted column shows 'Yes' when user_submitted but no user linked", %{conn: conn} do
create_contact(%{station1: "K5ANON", user_submitted: true, user_id: nil})
{:ok, _lv, html} = live(conn, ~p"/contacts?search=K5ANON")
# Locate the row containing our station and assert "Yes" appears in it
[row] = Regex.run(~r/<tr[^>]*>(?:(?!<\/tr>).)*K5ANON(?:(?!<\/tr>).)*<\/tr>/s, html) || [""]
assert row =~ ">Yes<"
end
test "Submitted column shows dash for scraped contacts", %{conn: conn} do
create_contact(%{station1: "K5SCRAPE", user_submitted: false})
{:ok, _lv, html} = live(conn, ~p"/contacts?search=K5SCRAPE")
[row] = Regex.run(~r/<tr[^>]*>(?:(?!<\/tr>).)*K5SCRAPE(?:(?!<\/tr>).)*<\/tr>/s, html) || [""]
refute row =~ ">Yes<"
end
end
describe "Show" do
test "renders contact detail fields", %{conn: conn} do
contact = create_contact()
{:ok, _lv, html} = live(conn, ~p"/contacts/#{contact.id}")
assert html =~ "W5XD"
assert html =~ "K5TR"
# Mode, band, grids are shown in the elevation profile section (requires SRTM)
# or in the header subtitle
assert html =~ "2026-03-28"
end
test "shows surface observations section", %{conn: conn} do
contact = create_contact()
{:ok, _lv, html} = live(conn, ~p"/contacts/#{contact.id}")
assert html =~ "Surface Observations"
end
test "shows sounding section", %{conn: conn} do
contact = create_contact()
{:ok, _lv, html} = live(conn, ~p"/contacts/#{contact.id}")
assert html =~ "Soundings"
end
test "shows solar index section", %{conn: conn} do
contact = create_contact()
{:ok, _lv, html} = live(conn, ~p"/contacts/#{contact.id}")
assert html =~ "Solar Conditions"
end
test "renders with sort assigns", %{conn: conn} do
contact = create_contact()
{:ok, lv, html} = live(conn, ~p"/contacts/#{contact.id}")
# Page renders without error
assert html =~ "Surface Observations"
assert html =~ "Soundings"
# Sort assigns are initialized (no crash on render)
assert render(lv) =~ contact.station1
end
test "raises for bad UUID", %{conn: conn} do
assert_raise Ecto.NoResultsError, fn ->
live(conn, ~p"/contacts/#{Ecto.UUID.generate()}")
end
end
end
end