ConnectionsPageTests.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. using Microsoft.Playwright;
  2. using Tests.E2e.Infra;
  3. using Tests.E2e.PageObjectModels;
  4. using Xunit.Abstractions;
  5. namespace Tests.E2e;
  6. /// <summary>
  7. /// Coverage for the global /connections page — previously the only page in
  8. /// the app with no test ids at all, and the only route from which a
  9. /// connection can be made without going through a resource card.
  10. /// Fixtures are seeded through the YAML import rather than by clicking port
  11. /// groups into existence, which keeps the setup to one step.
  12. /// </summary>
  13. public class ConnectionsPageTests(
  14. PlaywrightFixture fixture,
  15. ITestOutputHelper output) : E2ETestBase(fixture, output) {
  16. private readonly PlaywrightFixture _fixture = fixture;
  17. private readonly ITestOutputHelper _output = output;
  18. private const string _portType = "rj45";
  19. private const string _portSpeed = "1";
  20. private const int _portCount = 4;
  21. private static string TwoSwitchesNoConnection(string switchA, string switchB) =>
  22. $"""
  23. version: 3
  24. resources:
  25. - kind: Switch
  26. name: {switchA}
  27. ports:
  28. - type: {_portType}
  29. speed: {_portSpeed}
  30. count: {_portCount}
  31. - kind: Switch
  32. name: {switchB}
  33. ports:
  34. - type: {_portType}
  35. speed: {_portSpeed}
  36. count: {_portCount}
  37. """;
  38. // =============================================================
  39. // Creating a connection from the global page
  40. // =============================================================
  41. [Fact]
  42. public async Task User_Can_Connect_Two_Resources_From_The_Connections_Page() {
  43. (IBrowserContext context, IPage page) = await CreatePageAsync();
  44. var switchA = $"e2e-cxa-{Guid.NewGuid():N}"[..14];
  45. var switchB = $"e2e-cxb-{Guid.NewGuid():N}"[..14];
  46. try {
  47. await SeedSwitchesAsync(page, switchA, switchB);
  48. var connections = new ConnectionsPagePom(page);
  49. await connections.GotoAsync(_fixture.BaseUrl);
  50. await connections.OpenModalAsync();
  51. await connections.Modal.CreateConnectionAsync(
  52. switchA,
  53. ConnectionModalPom.GroupLabel(_portType, _portSpeed, _portCount),
  54. ConnectionModalPom.PortLabel(1),
  55. switchB,
  56. ConnectionModalPom.GroupLabel(_portType, _portSpeed, _portCount),
  57. ConnectionModalPom.PortLabel(1));
  58. // Submitting closes the modal.
  59. await connections.Modal.AssertClosedAsync();
  60. // The saved config is the source of truth.
  61. await page.GotoAsync($"{_fixture.BaseUrl}/yaml");
  62. ILocator yaml = page.GetByTestId("yaml-file-content");
  63. await Assertions.Expect(yaml).ToBeVisibleAsync();
  64. await Assertions.Expect(yaml).ToContainTextAsync("connections:");
  65. await Assertions.Expect(yaml).ToContainTextAsync(switchA);
  66. await Assertions.Expect(yaml).ToContainTextAsync(switchB);
  67. }
  68. catch (Exception) {
  69. await DumpAsync(page);
  70. throw;
  71. }
  72. finally {
  73. await context.CloseAsync();
  74. }
  75. }
  76. // =============================================================
  77. // Connection labels survive the round trip
  78. // =============================================================
  79. [Fact]
  80. public async Task A_Connection_Label_Is_Persisted_With_The_Connection() {
  81. (IBrowserContext context, IPage page) = await CreatePageAsync();
  82. var switchA = $"e2e-cla-{Guid.NewGuid():N}"[..14];
  83. var switchB = $"e2e-clb-{Guid.NewGuid():N}"[..14];
  84. var label = $"uplink-{Guid.NewGuid():N}"[..14];
  85. try {
  86. await SeedSwitchesAsync(page, switchA, switchB);
  87. var connections = new ConnectionsPagePom(page);
  88. await connections.GotoAsync(_fixture.BaseUrl);
  89. await connections.OpenModalAsync();
  90. await connections.Modal.CreateConnectionAsync(
  91. switchA,
  92. ConnectionModalPom.GroupLabel(_portType, _portSpeed, _portCount),
  93. ConnectionModalPom.PortLabel(2),
  94. switchB,
  95. ConnectionModalPom.GroupLabel(_portType, _portSpeed, _portCount),
  96. ConnectionModalPom.PortLabel(2),
  97. label);
  98. await connections.Modal.AssertClosedAsync();
  99. await page.GotoAsync($"{_fixture.BaseUrl}/yaml");
  100. ILocator yaml = page.GetByTestId("yaml-file-content");
  101. await Assertions.Expect(yaml).ToBeVisibleAsync();
  102. await Assertions.Expect(yaml).ToContainTextAsync(label);
  103. }
  104. catch (Exception) {
  105. await DumpAsync(page);
  106. throw;
  107. }
  108. finally {
  109. await context.CloseAsync();
  110. }
  111. }
  112. // =============================================================
  113. // Opening and dismissing the modal
  114. // =============================================================
  115. [Fact]
  116. public async Task Connections_Page_Opens_The_Connection_Modal() {
  117. (IBrowserContext context, IPage page) = await CreatePageAsync();
  118. try {
  119. var connections = new ConnectionsPagePom(page);
  120. await connections.GotoAsync(_fixture.BaseUrl);
  121. // The modal is not mounted until asked for.
  122. await connections.Modal.AssertClosedAsync();
  123. await connections.OpenModalAsync();
  124. }
  125. catch (Exception) {
  126. await DumpAsync(page);
  127. throw;
  128. }
  129. finally {
  130. await context.CloseAsync();
  131. }
  132. }
  133. // =============================================================
  134. // Helpers
  135. // =============================================================
  136. private async Task SeedSwitchesAsync(IPage page, string switchA, string switchB) {
  137. var import = new YamlImportPom(page);
  138. await import.GotoAsync(_fixture.BaseUrl);
  139. await import.PasteAsync(TwoSwitchesNoConnection(switchA, switchB));
  140. await import.AssertNoErrorAsync();
  141. await import.ApplyAsync();
  142. }
  143. private async Task DumpAsync(IPage page) {
  144. _output.WriteLine("TEST FAILED — Capturing diagnostics");
  145. _output.WriteLine($"Current URL: {page.Url}");
  146. var html = await page.ContentAsync();
  147. _output.WriteLine("==== DOM SNAPSHOT START ====");
  148. _output.WriteLine(html);
  149. _output.WriteLine("==== DOM SNAPSHOT END ====");
  150. }
  151. }