using Microsoft.Playwright; using Tests.E2e.Infra; using Tests.E2e.PageObjectModels; using Xunit.Abstractions; namespace Tests.E2e; public class GlobalSearchTests( PlaywrightFixture fixture, ITestOutputHelper output) : E2ETestBase(fixture, output) { private readonly PlaywrightFixture _fixture = fixture; private readonly ITestOutputHelper _output = output; [Fact] public async Task User_Can_Search_For_Resources_Of_Every_Kind_And_Navigate_To_One() { (IBrowserContext context, IPage page) = await CreatePageAsync(); // Shared random token in every seeded name so a single fragment hits // every kind, while a kind-prefixed query isolates one. var token = Guid.NewGuid().ToString("N")[..6]; var seed = new (string Kind, string Name)[] { ("server", $"srv-{token}"), ("switch", $"sw-{token}"), ("firewall", $"fw-{token}"), ("router", $"rt-{token}"), ("accesspoint", $"ap-{token}"), ("ups", $"ups-{token}"), ("desktop", $"dt-{token}"), ("laptop", $"lt-{token}"), ("system", $"sys-{token}"), ("service", $"svc-{token}"), }; try { await page.GotoAsync(_fixture.BaseUrl); await new MainLayoutPom(page).AssertLoadedAsync(); // ───── Seed: one resource of every kind ───── await page.GotoAsync($"{_fixture.BaseUrl}/servers/list"); await new ServersListPom(page).AddServerAsync($"srv-{token}"); await page.GotoAsync($"{_fixture.BaseUrl}/switches/list"); await new SwitchListPom(page).AddSwitchAsync($"sw-{token}"); await page.GotoAsync($"{_fixture.BaseUrl}/firewalls/list"); await new FirewallsListPom(page).AddFirewallAsync($"fw-{token}"); await page.GotoAsync($"{_fixture.BaseUrl}/routers/list"); await new RouterListPom(page).AddRouterAsync($"rt-{token}"); await page.GotoAsync($"{_fixture.BaseUrl}/accesspoints/list"); await new AccessPointsListPom(page).AddAccessPointAsync($"ap-{token}"); await page.GotoAsync($"{_fixture.BaseUrl}/ups/list"); await new UpsListPom(page).AddUpsAsync($"ups-{token}"); await page.GotoAsync($"{_fixture.BaseUrl}/desktops/list"); await new DesktopsListPom(page).AddDesktopAsync($"dt-{token}"); await page.GotoAsync($"{_fixture.BaseUrl}/laptops/list"); await new LaptopListPom(page).AddLaptopAsync($"lt-{token}"); await page.GotoAsync($"{_fixture.BaseUrl}/systems/list"); await new SystemsListPom(page).AddSystemAsync($"sys-{token}"); await page.GotoAsync($"{_fixture.BaseUrl}/services/list"); await new ServicesListPom(page).AddServiceAsync($"svc-{token}"); var search = new GlobalSearchPom(page); // ───── Per-kind search: each unique name surfaces its own kind ───── foreach ((string Kind, string Name) entry in seed) { await search.SearchAsync(entry.Name); await search.AssertResultExistsAsync(entry.Kind, entry.Name); } // ───── Cross-kind search: shared token surfaces results from // multiple kinds in the same dropdown. Top N is capped at 8; // within an equal-score tier ties break alphabetically by name, // so the kinds asserted here are those guaranteed to land // inside the cap given the seeded name prefixes. await search.SearchAsync(token); await search.AssertResultExistsAsync("service", $"svc-{token}"); await search.AssertResultExistsAsync("server", $"srv-{token}"); await search.AssertResultExistsAsync("switch", $"sw-{token}"); // ───── Click navigates to the resource page ───── await search.SearchAsync($"svc-{token}"); await search.ClickResultAsync("service", $"svc-{token}"); await page.WaitForURLAsync($"**/resources/services/svc-{token}"); } catch (Exception) { _output.WriteLine("TEST FAILED — Capturing diagnostics"); _output.WriteLine($"Current URL: {page.Url}"); var html = await page.ContentAsync(); _output.WriteLine("==== DOM SNAPSHOT START ===="); _output.WriteLine(html); _output.WriteLine("==== DOM SNAPSHOT END ===="); throw; } finally { await context.CloseAsync(); } } }