defmodule ToweropsWeb.Live.Components.GlobalSearchComponentTest do use ToweropsWeb.ConnCase, async: true import Phoenix.LiveViewTest setup :register_and_log_in_user setup %{conn: conn, user: user} do {:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test ISP"}, user.id) {:ok, site} = Towerops.Sites.create_site(%{ name: "Tower Alpha", organization_id: organization.id }) {:ok, _device} = Towerops.Devices.create_device(%{ name: "Core Router", ip_address: "10.0.0.1", site_id: site.id, organization_id: organization.id, snmp_enabled: true }) conn = Plug.Conn.put_session(conn, :current_organization_id, organization.id) %{conn: conn, organization: organization} end describe "global search" do test "search event with value param finds results", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/dashboard") html = view |> element("#global-search") |> render_hook("open", %{}) assert html =~ "Search devices" html = view |> element("#global-search") |> render_hook("search", %{"value" => "Core Router"}) assert html =~ "Core Router" assert html =~ "Devices" end test "search event with value param finds sites", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/dashboard") view |> element("#global-search") |> render_hook("open", %{}) html = view |> element("#global-search") |> render_hook("search", %{"value" => "Tower"}) assert html =~ "Tower Alpha" assert html =~ "Sites" end test "short query returns no results", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/dashboard") view |> element("#global-search") |> render_hook("open", %{}) html = view |> element("#global-search") |> render_hook("search", %{"value" => "a"}) assert html =~ "No results found" end test "close hides the overlay", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/dashboard") view |> element("#global-search") |> render_hook("open", %{}) html = view |> element("#global-search") |> render_hook("close", %{}) refute html =~ "Search devices" end test "ArrowDown / ArrowUp keyboard navigation cycles selected index", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/dashboard") view |> element("#global-search") |> render_hook("open", %{}) view |> element("#global-search") |> render_hook("search", %{"value" => "Core"}) # Arrows shouldn't crash even at boundaries view |> element("#global-search") |> render_hook("keydown", %{"key" => "ArrowDown"}) view |> element("#global-search") |> render_hook("keydown", %{"key" => "ArrowDown"}) view |> element("#global-search") |> render_hook("keydown", %{"key" => "ArrowUp"}) # An unrelated key is a no-op html = view |> element("#global-search") |> render_hook("keydown", %{"key" => "Tab"}) assert html =~ "Core" end test "Enter on an empty result list is a no-op", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/dashboard") view |> element("#global-search") |> render_hook("open", %{}) html = view |> element("#global-search") |> render_hook("keydown", %{"key" => "Enter"}) # No navigation, view stays alive assert is_binary(html) and byte_size(html) > 0 assert Process.alive?(view.pid) end end end