using Microsoft.Playwright; using Tests.E2e.Infra; using Tests.E2e.PageObjectModels; using Xunit.Abstractions; namespace Tests.E2e; public class ServerCardTests( PlaywrightFixture fixture, ITestOutputHelper output) : E2ETestBase(fixture, output) { private readonly PlaywrightFixture _fixture = fixture; private readonly ITestOutputHelper _output = output; [Fact] public async Task User_Can_Rename_Clone_And_Delete_Server_From_Details_Page() { (IBrowserContext context, IPage page) = await CreatePageAsync(); var originalName = $"e2e-srv-{Guid.NewGuid():N}"[..16]; var renamedName = $"e2e-srv-rn-{Guid.NewGuid():N}"[..16]; var cloneName = $"e2e-srv-cl-{Guid.NewGuid():N}"[..16]; try { // ------------------------------------ // Navigate to Servers list // ------------------------------------ await page.GotoAsync(_fixture.BaseUrl); var layout = new MainLayoutPom(page); await layout.AssertLoadedAsync(); await layout.GotoHardwareAsync(); var hardwareTree = new HardwareTreePom(page); await hardwareTree.AssertLoadedAsync(); await hardwareTree.GotoServersListAsync(); var listPage = new ServersListPom(page); await listPage.AssertLoadedAsync(); // ------------------------------------ // Create server // ------------------------------------ await listPage.AddServerAsync(originalName); // If list does not auto-navigate, open it if (!page.Url.Contains($"/resources/hardware/{originalName}", StringComparison.OrdinalIgnoreCase)) await listPage.OpenServerAsync(originalName); var card = new ServerCardPom(page); await card.AssertVisibleAsync(originalName); // ==================================== // RENAME // ==================================== await card.RenameAsync(originalName, renamedName); await card.AssertVisibleAsync(renamedName); // ==================================== // CLONE // ==================================== await card.CloneAsync(renamedName, cloneName); await card.AssertVisibleAsync(cloneName); // ==================================== // DELETE CLONE // ==================================== await card.DeleteAsync(cloneName); // Details page delete navigates to tree await page.WaitForURLAsync("**/hardware/tree"); // ==================================== // DELETE RENAMED ORIGINAL // ==================================== await page.GotoAsync($"{_fixture.BaseUrl}/resources/hardware/{renamedName}"); await card.AssertVisibleAsync(renamedName); await card.DeleteAsync(renamedName); await page.WaitForURLAsync("**/hardware/tree"); } 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(); } } [Fact] public async Task User_Can_Add_And_Remove_Tags_From_Server_Card() { (IBrowserContext context, IPage page) = await CreatePageAsync(); var name = $"e2e-ap-{Guid.NewGuid():N}"[..16]; try { await page.GotoAsync(_fixture.BaseUrl); var layout = new MainLayoutPom(page); await layout.AssertLoadedAsync(); await layout.GotoHardwareAsync(); var hardwareTree = new HardwareTreePom(page); await hardwareTree.AssertLoadedAsync(); await hardwareTree.GotoServersListAsync(); var list = new ServersListPom(page); await list.AssertLoadedAsync(); await list.AddServerAsync(name); await page.WaitForURLAsync($"**/resources/hardware/{name}"); var card = new ServerCardPom(page); await card.AssertVisibleAsync(name); TagsPom tags = card.Tags; // ------------------------------------------------- // Add multiple tags in one modal interaction // ------------------------------------------------- await tags.AddTagsAsync("server", "Foo", "Bar", "Baz"); await tags.AssertTagVisibleAsync("server", "Foo"); await tags.AssertTagVisibleAsync("server", "Bar"); await tags.AssertTagVisibleAsync("server", "Baz"); // ------------------------------------------------- // Remove a single tag // ------------------------------------------------- await tags.RemoveTagAsync("server", "Bar"); await tags.AssertTagNotVisibleAsync("server", "Bar"); await tags.AssertTagVisibleAsync("server", "Foo"); await tags.AssertTagVisibleAsync("server", "Baz"); // ------------------------------------------------- // Reload to verify persistence // ------------------------------------------------- await page.ReloadAsync(); await tags.AssertTagVisibleAsync("server", "Foo"); await tags.AssertTagVisibleAsync("server", "Baz"); await tags.AssertTagNotVisibleAsync("server", "Bar"); await context.CloseAsync(); } finally { await context.CloseAsync(); } } [Fact] public async Task User_Can_Add_And_Remove_Labels_From_Server_Card() { (IBrowserContext context, IPage page) = await CreatePageAsync(); var name = $"e2e-srv-lbl-{Guid.NewGuid():N}"[..16]; try { await page.GotoAsync(_fixture.BaseUrl); var layout = new MainLayoutPom(page); await layout.AssertLoadedAsync(); await layout.GotoHardwareAsync(); var hardwareTree = new HardwareTreePom(page); await hardwareTree.AssertLoadedAsync(); await hardwareTree.GotoServersListAsync(); var list = new ServersListPom(page); await list.AssertLoadedAsync(); await list.AddServerAsync(name); await page.WaitForURLAsync($"**/resources/hardware/{name}"); var card = new ServerCardPom(page); await card.AssertVisibleAsync(name); LabelsPom labels = card.Labels; await labels.AddLabelAsync("server", "env", "production"); await labels.AssertLabelVisibleAsync("server", "env"); await labels.AddLabelAsync("server", "owner", "team-a"); await labels.AssertLabelVisibleAsync("server", "owner"); await labels.RemoveLabelAsync("server", "owner"); await labels.AssertLabelNotVisibleAsync("server", "owner"); await labels.AssertLabelVisibleAsync("server", "env"); await page.ReloadAsync(); await labels.AssertLabelVisibleAsync("server", "env"); await labels.AssertLabelNotVisibleAsync("server", "owner"); await context.CloseAsync(); } finally { await context.CloseAsync(); } } [Fact] public async Task User_Can_Edit_Label_From_Server_Card() { (IBrowserContext context, IPage page) = await CreatePageAsync(); var name = $"e2e-srv-edit-{Guid.NewGuid():N}"[..16]; try { await page.GotoAsync(_fixture.BaseUrl); var layout = new MainLayoutPom(page); await layout.AssertLoadedAsync(); await layout.GotoHardwareAsync(); var hardwareTree = new HardwareTreePom(page); await hardwareTree.AssertLoadedAsync(); await hardwareTree.GotoServersListAsync(); var list = new ServersListPom(page); await list.AssertLoadedAsync(); await list.AddServerAsync(name); await page.WaitForURLAsync($"**/resources/hardware/{name}"); var card = new ServerCardPom(page); await card.AssertVisibleAsync(name); LabelsPom labels = card.Labels; await labels.AddLabelAsync("server", "env", "production"); await labels.AssertLabelDisplaysAsync("server", "env", "production"); await labels.EditLabelAsync("server", "env", "env", "staging"); await labels.AssertLabelDisplaysAsync("server", "env", "staging"); await page.ReloadAsync(); await labels.AssertLabelDisplaysAsync("server", "env", "staging"); await labels.EditLabelAsync("server", "env", "environment", "staging"); await labels.AssertLabelNotVisibleAsync("server", "env"); await labels.AssertLabelDisplaysAsync("server", "environment", "staging"); await page.ReloadAsync(); await labels.AssertLabelDisplaysAsync("server", "environment", "staging"); await context.CloseAsync(); } finally { await context.CloseAsync(); } } }