towerops/test/towerops_web/live/network_map_live_test.exs
Graham McIntire aa7fc830e5 test: refactor LiveView tests to use HTML-aware element/form helpers
Apply patterns from testingliveview.com — replace render_click/submit/change
event-name calls with element/form-targeted equivalents that validate the
actual DOM wiring, not just handler logic. Adds stable test selectors
(id="...") to interactive elements across 10 LiveView templates.

Surfaces (and fixes) several real issues that the bypass-style tests masked:
- Removes dead regenerate_token handler in agent_live/index.ex (no UI ever
  triggered it) and its corresponding test.
- Removes dead refresh_topology test in network_map_live_test (event was
  referenced only in the test — no handler, no button, no JS anywhere).
- Corrects "shows notification tab with add device modal" — modal lives on
  the security tab; original test mounted ?tab=notifications and only
  worked because direct event calls bypassed tab gating.
- Corrects form input shape mismatches in agent_live/index_test that the
  handler's defensive case clauses had been masking.
- Expands setup data for org/settings_live_test (snmp_community,
  default_agent_token_id, multi-org membership) so the gated buttons
  actually render.

11 direct render_click/submit/change calls remain — all intentional,
documented server-side guard tests (tampered POSTs, race conditions,
defensive idempotent handlers).

Full LiveView test suite: 1300 tests, 0 failures.
2026-04-29 11:14:20 -05:00

226 lines
6.6 KiB
Elixir

defmodule ToweropsWeb.NetworkMapLiveTest do
use ToweropsWeb.ConnCase, async: true
import Phoenix.LiveViewTest
@moduletag :skip
setup :register_and_log_in_user
setup %{user: user} do
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
%{organization: organization}
end
describe "mount and render" do
test "mounts and renders the network map page", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/network-map")
assert html =~ "Network Map"
assert html =~ "Experimental"
assert html =~ "Topology Visualization"
end
test "shows stats bar with default zeros", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/network-map")
assert html =~ "Total Devices"
assert html =~ "Added Devices"
assert html =~ "Discovered"
assert html =~ "Connections"
end
test "shows tab navigation with added and all tabs", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/network-map")
assert html =~ "Added Devices"
assert html =~ "All Devices"
end
test "shows empty state when no devices exist", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/network-map")
assert html =~ "No network topology available"
assert html =~ "Add devices"
end
test "shows refresh button", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/network-map")
assert html =~ "Refresh"
end
test "shows legend with role and confidence indicators", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/network-map")
assert html =~ "Roles:"
assert html =~ "Router"
assert html =~ "Switch"
assert html =~ "Firewall"
assert html =~ "Discovered"
assert html =~ "Links:"
assert html =~ "High confidence"
end
test "shows cytoscape container with network map hook", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/network-map")
assert html =~ "cy-container"
assert html =~ "NetworkMap"
end
end
describe "tab navigation" do
test "defaults to added tab", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/network-map")
# The "Added Devices" tab should have active styling
assert html =~ "Added Devices"
end
test "switches to all tab via params", %{conn: conn} do
{:ok, _view, html} = live(conn, ~p"/network-map?tab=all")
assert html =~ "All Devices"
end
end
describe "topology update events" do
test "pushes update_topology event on mount", %{conn: conn, organization: organization} do
{:ok, site} = Towerops.Sites.create_site(%{name: "Test Site", organization_id: organization.id})
{:ok, _device} =
Towerops.Devices.create_device(
%{
name: "Test-Router",
ip_address: "10.0.0.1",
snmp_enabled: true,
snmp_version: "2c",
snmp_community: "public",
snmp_port: 161,
site_id: site.id,
organization_id: organization.id
},
bypass_limits: true
)
{:ok, view, _html} = live(conn, ~p"/network-map")
# Verify the topology data is loaded (not empty)
topology = view |> element("#cy-container") |> render()
assert topology =~ "data-topology"
end
test "refreshes topology on PubSub message", %{conn: conn, organization: organization} do
{:ok, view, _html} = live(conn, ~p"/network-map")
# Broadcast a topology update
Phoenix.PubSub.broadcast(
Towerops.PubSub,
"topology:#{organization.id}",
{:topology_updated, organization.id}
)
# Give LiveView time to process
_ = render(view)
# Verify it still renders without error
html = render(view)
assert html =~ "Topology Visualization"
end
end
describe "node detail panel" do
setup %{organization: organization} do
{:ok, site} = Towerops.Sites.create_site(%{name: "Test Site", organization_id: organization.id})
{:ok, device} =
Towerops.Devices.create_device(
%{
name: "Test-Router",
ip_address: "10.0.0.1",
snmp_enabled: true,
snmp_version: "2c",
snmp_community: "public",
snmp_port: 161,
site_id: site.id,
organization_id: organization.id
},
bypass_limits: true
)
%{site: site, device: device}
end
test "node_clicked event assigns selected node detail", %{conn: conn, device: device} do
{:ok, view, _html} = live(conn, ~p"/network-map")
html =
view
|> element("#cy-container")
|> render_hook("node_clicked", %{"node_id" => device.id, "node_type" => "managed"})
assert has_element?(view, "#node-detail-panel", "Test-Router")
assert html =~ "10.0.0.1"
end
test "close button clears selection", %{conn: conn, device: device} do
{:ok, view, _html} = live(conn, ~p"/network-map")
# Open panel via the JS hook (cytoscape graph node click)
view
|> element("#cy-container")
|> render_hook("node_clicked", %{"node_id" => device.id, "node_type" => "managed"})
assert has_element?(view, "#node-detail-panel")
view |> element("#close-detail-panel") |> render_click()
refute has_element?(view, "#node-detail-panel")
end
test "?node=<id> param opens detail panel on mount", %{conn: conn, device: device} do
{:ok, _view, html} = live(conn, ~p"/network-map?node=#{device.id}")
assert html =~ "Test-Router"
assert html =~ "10.0.0.1"
end
end
describe "site grouping" do
test "network map renders site groupings in topology data", %{conn: conn, organization: organization} do
{:ok, site} = Towerops.Sites.create_site(%{name: "Main Office", organization_id: organization.id})
{:ok, _device} =
Towerops.Devices.create_device(
%{
name: "Office-Router",
ip_address: "10.0.0.1",
snmp_enabled: true,
snmp_version: "2c",
snmp_community: "public",
snmp_port: 161,
site_id: site.id,
organization_id: organization.id
},
bypass_limits: true
)
{:ok, _view, html} = live(conn, ~p"/network-map")
# The topology data should include site info in JSON
assert html =~ "Main Office"
end
end
describe "unauthenticated access" do
test "requires authentication" do
conn = build_conn()
{:error, redirect} = live(conn, ~p"/network-map")
assert {:redirect, %{to: path}} = redirect
assert path == ~p"/users/log-in"
end
end
end