prop/test/microwaveprop_web/live/beacon_live_test.exs
Graham McIntire 92ef6c67fc
Defer beacon coverage estimate until the toggle is flipped on
Previously RangeEstimate.estimate/1 ran on every beacon page mount,
even though the cells data was only rendered after the user flipped
the coverage toggle — a wasted bbox grid pass per page load. Start
with estimate: nil, compute lazily in handle_event("toggle_coverage")
when flipping on, and push the cells payload to the Leaflet hook via
a new load_coverage event. The JS hook no longer reads data-cells on
mount and only builds the cellLayer when the server sends it. Cached
in the socket so re-toggling is instant.
2026-04-14 17:13:05 -05:00

213 lines
7.2 KiB
Elixir

defmodule MicrowavepropWeb.BeaconLiveTest do
use MicrowavepropWeb.ConnCase
import Microwaveprop.AccountsFixtures
import Microwaveprop.BeaconsFixtures
import Phoenix.LiveViewTest
alias Microwaveprop.Accounts
defp promote_to_admin(user) do
{:ok, user} = Accounts.admin_update_user(user, %{is_admin: true})
user
end
defp admin_user_fixture do
promote_to_admin(user_fixture())
end
defp approved_beacon_fixture(user, attrs \\ %{}) do
beacon = beacon_fixture(user, attrs)
{:ok, beacon} = Microwaveprop.Beacons.approve_beacon(beacon)
beacon
end
describe "Index (public)" do
test "lists approved beacons without logging in", %{conn: conn} do
beacon = approved_beacon_fixture(user_fixture())
{:ok, _view, html} = live(conn, ~p"/beacons")
assert html =~ "Beacons"
assert html =~ beacon.callsign
end
test "hides unapproved beacons from the public list", %{conn: conn} do
_pending = beacon_fixture(user_fixture(), callsign: "W5PEN")
{:ok, _view, html} = live(conn, ~p"/beacons")
refute html =~ "W5PEN"
end
test "anonymous sees the Submit Beacon button", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/beacons")
assert html =~ "Submit Beacon"
end
test "authenticated user sees the Submit Beacon button", %{conn: conn} do
user = user_fixture()
conn = log_in_user(conn, user)
{:ok, _view, html} = live(conn, ~p"/beacons")
assert html =~ "Submit Beacon"
end
test "admin sees pending beacons section", %{conn: conn} do
pending = beacon_fixture(user_fixture(), callsign: "W5PEN")
conn = log_in_user(conn, admin_user_fixture())
{:ok, _view, html} = live(conn, ~p"/beacons")
assert html =~ "Pending approval"
assert html =~ pending.callsign
end
test "non-admin does not see pending beacons section", %{conn: conn} do
_pending = beacon_fixture(user_fixture(), callsign: "W5PEN")
conn = log_in_user(conn, user_fixture())
{:ok, _view, html} = live(conn, ~p"/beacons")
refute html =~ "Pending approval"
refute html =~ "W5PEN"
end
test "admin can approve a pending beacon", %{conn: conn} do
pending = beacon_fixture(user_fixture(), callsign: "W5PEN")
conn = log_in_user(conn, admin_user_fixture())
{:ok, view, _html} = live(conn, ~p"/beacons")
html =
view
|> element("a[phx-click][data-confirm='Approve this beacon?']")
|> render_click()
assert html =~ "Approved #{pending.callsign}"
assert Microwaveprop.Beacons.get_beacon!(pending.id).approved == true
end
end
describe "Show (public)" do
test "renders beacon details without logging in", %{conn: conn} do
beacon = approved_beacon_fixture(user_fixture())
{:ok, _view, html} = live(conn, ~p"/beacons/#{beacon}")
assert html =~ beacon.callsign
assert html =~ "10,368.1"
end
test "shows pending badge on unapproved beacons", %{conn: conn} do
beacon = beacon_fixture(user_fixture())
{:ok, _view, html} = live(conn, ~p"/beacons/#{beacon}")
assert html =~ "Pending approval"
end
end
describe "Form" do
test "authenticated non-admin can access /beacons/new", %{conn: conn} do
user = user_fixture()
conn = log_in_user(conn, user)
{:ok, _view, html} = live(conn, ~p"/beacons/new")
assert html =~ "New beacon"
end
test "anonymous user can access /beacons/new", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/beacons/new")
assert html =~ "New beacon"
end
test "anonymous submission creates an unapproved beacon with no user_id", %{conn: conn} do
{:ok, form_live, _html} = live(conn, ~p"/beacons/new")
attrs = valid_beacon_attrs(callsign: "W5ANON")
assert {:ok, _view, html} =
form_live
|> form("#beacon-form", beacon: attrs)
|> render_submit()
|> follow_redirect(conn, ~p"/beacons")
assert html =~ "awaiting admin approval"
beacon =
Enum.find(Microwaveprop.Beacons.list_pending_beacons(), fn b -> b.callsign == "W5ANON" end)
assert beacon
assert beacon.user_id == nil
assert beacon.approved == false
end
test "non-admin submission creates an unapproved beacon", %{conn: conn} do
user = user_fixture()
conn = log_in_user(conn, user)
{:ok, form_live, _html} = live(conn, ~p"/beacons/new")
attrs = valid_beacon_attrs()
assert {:ok, _view, _html} =
form_live
|> form("#beacon-form", beacon: attrs)
|> render_submit()
|> follow_redirect(conn, ~p"/beacons")
beacon = List.first(Microwaveprop.Beacons.list_pending_beacons())
assert beacon.callsign == attrs.callsign
assert beacon.approved == false
end
test "admin edits a beacon", %{conn: conn} do
admin = admin_user_fixture()
beacon = beacon_fixture(admin)
conn = log_in_user(conn, admin)
{:ok, form_live, _} = live(conn, ~p"/beacons/#{beacon}/edit")
assert {:ok, _view, html} =
form_live
|> form("#beacon-form", beacon: %{power_mw: 25_000.0})
|> render_submit()
|> follow_redirect(conn, ~p"/beacons")
assert html =~ "Beacon updated"
end
test "non-admin cannot access /beacons/:id/edit", %{conn: conn} do
beacon = beacon_fixture(user_fixture())
conn = log_in_user(conn, user_fixture())
assert {:error, {:redirect, %{to: "/"}}} = live(conn, ~p"/beacons/#{beacon}/edit")
end
end
describe "Show — lazy coverage" do
test "does not compute the estimate or embed cells on mount", %{conn: conn} do
beacon = approved_beacon_fixture(user_fixture(), frequency_mhz: 10_368.1)
{:ok, view, html} = live(conn, ~p"/beacons/#{beacon}")
# Map div exists but data-cells is absent or empty until the user
# flips the toggle — we don't want to pay the RangeEstimate cost
# on every page load.
refute html =~ "data-cells"
assert has_element?(view, "input[phx-click='toggle_coverage']")
end
test "flipping the toggle computes the estimate and pushes coverage data", %{conn: conn} do
beacon = approved_beacon_fixture(user_fixture(), frequency_mhz: 10_368.1)
{:ok, view, html} = live(conn, ~p"/beacons/#{beacon}")
# Before the click the coverage info bar is absent.
refute html =~ "cells rendered"
render_click(element(view, "input[phx-click='toggle_coverage']"))
html = render(view)
# After toggling on, the coverage info bar (which only renders
# when @estimate is populated and @show_coverage is true) shows.
assert html =~ "cells rendered"
assert html =~ "Atm loss"
end
test "toggle_coverage is a no-op for beacons below 5.76 GHz", %{conn: conn} do
beacon = approved_beacon_fixture(user_fixture(), frequency_mhz: 1296.0)
{:ok, view, html} = live(conn, ~p"/beacons/#{beacon}")
assert html =~ "5.76 GHz and up only"
refute html =~ "data-cells"
refute html =~ "cells rendered"
# Even if a client-side hack forces the click, the server refuses.
render_click(view, "toggle_coverage")
refute render(view) =~ "cells rendered"
end
end
end