prop/test/microwaveprop_web/live/skewt_live_test.exs
Graham McIntire 3a81d05b4e
perf(skewt): load profile + sounding asynchronously via start_async
URL-parameter loads (`/skewt?q=…`) were blocking the initial mount
on three serial steps: geocoder/grid resolve, ProfilesFile or
HrrrProfileLookup read, and the SkewtParams/SoundingParams/SkewtSvg
derivation. The address path in particular hits an external API,
which made the page take "forever" to load on first paint.

Restructure SkewtLive so the page chrome (header, search bar) renders
synchronously on mount, and the heavy work runs in a single
`start_async(:load_skewt, …)` task that calls SkewtLocationResolver,
walks the on-disk profile store with a DB fallback, and produces the
SVG. A new `loading?` assign drives a small spinner + "Resolving
location and loading HRRR profile…" message that shows while the task
is in flight; previous data stays on screen so navigation between
forecast hours doesn't blank out the diagram.

handle_async/3 covers three landings:

  * `{:ok, {:ok, result}}` — apply assigns from the resolver
  * `{:ok, {:error, reason}}` — surface as the existing error alert
    (geocoder failure, unknown callsign, etc.)
  * `{:exit, reason}` — log a warning and show a generic retry message

start_async/3 cancels any prior task with the same name on each call,
so a rapid-fire URL patch (e.g. clicking through forecast-hour
buttons before the first response returns) never lands stale data.

Tests: extended SkewtLiveTest from 3 to 4 cases — the URL-param
initial render now asserts the chrome + spinner appear pre-async,
and a separate case `render_async/2`s the view to confirm the
location label arrives once the task completes. 4/4 green.
2026-04-25 14:36:30 -05:00

48 lines
1.6 KiB
Elixir

defmodule MicrowavepropWeb.SkewtLiveTest do
use MicrowavepropWeb.ConnCase, async: false
import Phoenix.LiveViewTest
describe "GET /skewt" do
test "renders search bar with no results when no query is supplied", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/skewt")
assert html =~ "Skew-T-Log-P"
assert html =~ ~s|placeholder="EM12kp|
refute html =~ "viewBox=\"0 0 720"
end
test "initial render with a URL query shows the page chrome + a loading indicator", %{conn: conn} do
# The URL parameter triggers an async resolve. The page must
# render the chrome (header, search bar) and a loading state
# synchronously — *before* the geocoder / HRRR fetch returns.
{:ok, _view, html} = live(conn, ~p"/skewt?q=EM12kp")
assert html =~ "Skew-T-Log-P"
assert html =~ ~s|placeholder="EM12kp|
assert html =~ "Resolving location"
# Location label has not arrived yet — that's the whole point.
refute html =~ "EM12KP"
end
test "after the async completes the resolved location label appears", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/skewt?q=EM12kp")
# Wait for the :load_skewt async to finish, then re-render.
html = render_async(view)
assert html =~ "EM12KP"
assert html =~ "32.646"
assert html =~ "-97.125"
refute html =~ "Resolving location"
end
test "submitting the form patches the URL with the query", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/skewt")
view |> form("form", q: "EM12") |> render_submit()
assert render_async(view) =~ "EM12"
end
end
end