defmodule MicrowavepropWeb.RoverLocationsLive.ShowTest do use MicrowavepropWeb.ConnCase, async: true import Phoenix.LiveViewTest alias Microwaveprop.Radio.Maidenhead alias Microwaveprop.Repo alias Microwaveprop.Rover describe "show" do test "renders the location and a map container", %{conn: conn} do user = Microwaveprop.AccountsFixtures.user_fixture() {:ok, loc} = Rover.create_location(user, %{ lat: 32.5, lon: -97.5, status: :good, notes: "great spot" }) {:ok, lv, html} = live(conn, ~p"/rover-locations/#{loc.id}") assert html =~ "Rover Location" assert html =~ "great spot" assert html =~ "EM12gm" assert has_element?(lv, "#location-map-#{loc.id}[phx-hook=LocationMap]") end test "redirects when the location does not exist", %{conn: conn} do missing_id = Ecto.UUID.generate() assert {:error, {:live_redirect, %{to: "/rover-locations"}}} = live(conn, ~p"/rover-locations/#{missing_id}") end test "redirects when the id is not a valid UUID", %{conn: conn} do assert {:error, {:live_redirect, %{to: "/rover-locations"}}} = live(conn, ~p"/rover-locations/not-a-uuid") end test "owner sees a Delete button; visitors do not", %{conn: conn} do user = Microwaveprop.AccountsFixtures.user_fixture() {:ok, loc} = Rover.create_location(user, %{lat: 32.5, lon: -97.5, status: :good}) # Anonymous: no Delete {:ok, _lv, html} = live(conn, ~p"/rover-locations/#{loc.id}") refute html =~ "Delete" # Owner: Delete present conn = log_in_user(conn, user) {:ok, _lv, html} = live(conn, ~p"/rover-locations/#{loc.id}") assert html =~ "Delete" end test "admin sees Delete on someone else's location", %{conn: conn} do owner = Microwaveprop.AccountsFixtures.user_fixture() admin = Microwaveprop.AccountsFixtures.user_fixture() {:ok, admin} = Microwaveprop.Accounts.admin_update_user(admin, %{is_admin: true}) {:ok, loc} = Rover.create_location(owner, %{lat: 32.5, lon: -97.5, status: :good}) conn = log_in_user(conn, admin) {:ok, _lv, html} = live(conn, ~p"/rover-locations/#{loc.id}") assert html =~ "Delete" end test "owner can enter edit mode and the marker is told to become draggable", %{conn: conn} do user = Microwaveprop.AccountsFixtures.user_fixture() {:ok, loc} = Rover.create_location(user, %{lat: 32.5, lon: -97.5, status: :good}) conn = log_in_user(conn, user) {:ok, lv, _html} = live(conn, ~p"/rover-locations/#{loc.id}") html = lv |> element("button", "Edit") |> render_click() assert html =~ "Editing — drag the marker" # Save + Cancel show up; Edit button hides. assert has_element?(lv, "button[phx-click='save_edit']") assert has_element?(lv, "button[phx-click='cancel_edit']") refute has_element?(lv, "button[phx-click='toggle_edit']") end test "drag updates the working coords + grid (without persisting)", %{conn: conn} do user = Microwaveprop.AccountsFixtures.user_fixture() {:ok, loc} = Rover.create_location(user, %{lat: 32.5, lon: -97.5, status: :good}) conn = log_in_user(conn, user) {:ok, lv, _html} = live(conn, ~p"/rover-locations/#{loc.id}") _ = lv |> element("button", "Edit") |> render_click() # Simulate the JS hook firing the dragend event. html = render_hook(lv, "location_dragged", %{"lat" => 33.25, "lon" => -97.75}) assert html =~ "33.25" assert html =~ "-97.75" # Grid recomputed for the new coords. assert html =~ Maidenhead.from_latlon(33.25, -97.75, 10) # DB still has the original coords until Save is clicked. assert %{lat: 32.5, lon: -97.5} = Repo.reload(loc) end test "save persists the dragged coords to the DB", %{conn: conn} do user = Microwaveprop.AccountsFixtures.user_fixture() {:ok, loc} = Rover.create_location(user, %{lat: 32.5, lon: -97.5, status: :good}) conn = log_in_user(conn, user) {:ok, lv, _html} = live(conn, ~p"/rover-locations/#{loc.id}") _ = lv |> element("button", "Edit") |> render_click() _ = render_hook(lv, "location_dragged", %{"lat" => 33.25, "lon" => -97.75}) html = lv |> element("button[phx-click='save_edit']") |> render_click() assert html =~ "Location updated" reloaded = Repo.reload(loc) assert_in_delta reloaded.lat, 33.25, 1.0e-6 assert_in_delta reloaded.lon, -97.75, 1.0e-6 end test "cancel reverts the working coords without writing to the DB", %{conn: conn} do user = Microwaveprop.AccountsFixtures.user_fixture() {:ok, loc} = Rover.create_location(user, %{lat: 32.5, lon: -97.5, status: :good}) conn = log_in_user(conn, user) {:ok, lv, _html} = live(conn, ~p"/rover-locations/#{loc.id}") _ = lv |> element("button", "Edit") |> render_click() _ = render_hook(lv, "location_dragged", %{"lat" => 99.0, "lon" => 99.0}) html = lv |> element("button[phx-click='cancel_edit']") |> render_click() # Working coords reset to the stored values. assert html =~ "32.5" refute html =~ "99.0, 99.0" reloaded = Repo.reload(loc) assert reloaded.lat == 32.5 assert reloaded.lon == -97.5 end test "save_edit fails closed when the user is signed out mid-session", %{conn: conn} do user = Microwaveprop.AccountsFixtures.user_fixture() {:ok, loc} = Rover.create_location(user, %{lat: 32.5, lon: -97.5, status: :good}) # Anonymous conn — driving the event directly through render_hook # exercises the unauthenticated branch of save_edit. {:ok, lv, _html} = live(conn, ~p"/rover-locations/#{loc.id}") html = render_hook(lv, "save_edit", %{}) assert html =~ "Sign in required" assert Repo.reload(loc).lat == 32.5 end test "edit mode exposes status + notes inputs populated with current values", %{conn: conn} do user = Microwaveprop.AccountsFixtures.user_fixture() {:ok, loc} = Rover.create_location(user, %{ lat: 32.5, lon: -97.5, status: :good, notes: "first pass" }) conn = log_in_user(conn, user) {:ok, lv, html} = live(conn, ~p"/rover-locations/#{loc.id}") # Inputs are not visible until edit mode is entered. refute has_element?(lv, "select[name='location[status]']") refute has_element?(lv, "textarea[name='location[notes]']") assert html =~ "first pass" _ = lv |> element("button", "Edit") |> render_click() assert has_element?(lv, "select[name='location[status]']") assert has_element?( lv, "textarea[name='location[notes]']", "first pass" ) end test "save persists status + notes edits", %{conn: conn} do user = Microwaveprop.AccountsFixtures.user_fixture() {:ok, loc} = Rover.create_location(user, %{ lat: 32.5, lon: -97.5, status: :good, notes: "old" }) conn = log_in_user(conn, user) {:ok, lv, _html} = live(conn, ~p"/rover-locations/#{loc.id}") _ = lv |> element("button", "Edit") |> render_click() _ = lv |> form("#location-edit-form", location: %{status: "bad", notes: "private property"} ) |> render_change() html = lv |> element("button[phx-click='save_edit']") |> render_click() assert html =~ "Location updated" reloaded = Repo.reload(loc) assert reloaded.status == :bad assert reloaded.notes == "private property" end test "cancel reverts pending status + notes changes", %{conn: conn} do user = Microwaveprop.AccountsFixtures.user_fixture() {:ok, loc} = Rover.create_location(user, %{ lat: 32.5, lon: -97.5, status: :good, notes: "keeper" }) conn = log_in_user(conn, user) {:ok, lv, _html} = live(conn, ~p"/rover-locations/#{loc.id}") _ = lv |> element("button", "Edit") |> render_click() _ = lv |> form("#location-edit-form", location: %{status: "bad", notes: "scratch this"} ) |> render_change() _ = lv |> element("button[phx-click='cancel_edit']") |> render_click() reloaded = Repo.reload(loc) assert reloaded.status == :good assert reloaded.notes == "keeper" end test "non-owner does not see the Edit button", %{conn: conn} do owner = Microwaveprop.AccountsFixtures.user_fixture() visitor = Microwaveprop.AccountsFixtures.user_fixture() {:ok, loc} = Rover.create_location(owner, %{lat: 32.5, lon: -97.5, status: :good}) conn = log_in_user(conn, visitor) {:ok, lv, _html} = live(conn, ~p"/rover-locations/#{loc.id}") refute has_element?(lv, "button[phx-click='toggle_edit']") end test "owner can delete from the show page and is redirected", %{conn: conn} do user = Microwaveprop.AccountsFixtures.user_fixture() {:ok, loc} = Rover.create_location(user, %{lat: 32.5, lon: -97.5, status: :good}) conn = log_in_user(conn, user) {:ok, lv, _html} = live(conn, ~p"/rover-locations/#{loc.id}") assert {:ok, _index_lv, _html} = lv |> element("button", "Delete") |> render_click() |> follow_redirect(conn, ~p"/rover-locations") end end end