TagAndLabelPageTests.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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 /tags/{TagName} and /labels/{LabelName} — the pages that
  8. /// aggregate resources across kinds. The tag and label editors on the cards
  9. /// were already covered; where those links lead was not.
  10. /// Both pages read the whole config and tests share a container, so every
  11. /// test uses a unique tag or label key to stay deterministic.
  12. /// </summary>
  13. public class TagAndLabelPageTests(
  14. PlaywrightFixture fixture,
  15. ITestOutputHelper output) : E2ETestBase(fixture, output) {
  16. private readonly PlaywrightFixture _fixture = fixture;
  17. private readonly ITestOutputHelper _output = output;
  18. private static string Unique(string prefix) => $"{prefix}{Guid.NewGuid():N}"[..14];
  19. // =============================================================
  20. // Tag page — aggregation across kinds
  21. // =============================================================
  22. [Fact]
  23. public async Task Tag_Page_Lists_Resources_Of_Every_Kind_Carrying_The_Tag() {
  24. (IBrowserContext context, IPage page) = await CreatePageAsync();
  25. var tag = Unique("e2etag");
  26. var serverName = Unique("e2e-tsv-");
  27. var systemName = Unique("e2e-tsy-");
  28. try {
  29. await CreateServerWithTagAsync(page, serverName, tag);
  30. await CreateSystemWithTagAsync(page, systemName, tag);
  31. var tagPage = new TagPagePom(page);
  32. await tagPage.GotoAsync(_fixture.BaseUrl, tag);
  33. await tagPage.AssertTitleContainsAsync(tag);
  34. // A hardware resource and a system, aggregated onto one page.
  35. await tagPage.AssertListsAsync(serverName);
  36. await tagPage.AssertListsAsync(systemName);
  37. }
  38. catch (Exception) {
  39. await DumpAsync(page);
  40. throw;
  41. }
  42. finally {
  43. await context.CloseAsync();
  44. }
  45. }
  46. // =============================================================
  47. // Tag page — click-through from a card, and back out again
  48. // =============================================================
  49. [Fact]
  50. public async Task User_Can_Click_A_Card_Tag_Through_To_The_Tag_Page_And_Back() {
  51. (IBrowserContext context, IPage page) = await CreatePageAsync();
  52. var tag = Unique("e2enav");
  53. var serverName = Unique("e2e-tnv-");
  54. try {
  55. await CreateServerWithTagAsync(page, serverName, tag);
  56. // Follow the tag chip on the card.
  57. var card = new ServerCardPom(page);
  58. await card.Tags.NavigateToTagAsync("server", tag);
  59. var tagPage = new TagPagePom(page);
  60. await tagPage.AssertLoadedAsync();
  61. await tagPage.AssertListsAsync(serverName);
  62. // And back out to the resource it points at.
  63. await tagPage.OpenResourceAsync(serverName);
  64. await card.AssertVisibleAsync(serverName);
  65. }
  66. catch (Exception) {
  67. await DumpAsync(page);
  68. throw;
  69. }
  70. finally {
  71. await context.CloseAsync();
  72. }
  73. }
  74. [Fact]
  75. public async Task Tag_Page_Reports_An_Unused_Tag_As_Empty() {
  76. (IBrowserContext context, IPage page) = await CreatePageAsync();
  77. try {
  78. var tagPage = new TagPagePom(page);
  79. await tagPage.GotoAsync(_fixture.BaseUrl, Unique("e2enone"));
  80. await tagPage.AssertEmptyAsync();
  81. }
  82. catch (Exception) {
  83. await DumpAsync(page);
  84. throw;
  85. }
  86. finally {
  87. await context.CloseAsync();
  88. }
  89. }
  90. // =============================================================
  91. // Label page — values and detected types
  92. // =============================================================
  93. [Fact]
  94. public async Task Label_Page_Shows_Each_Resource_Value_And_Its_Detected_Type() {
  95. (IBrowserContext context, IPage page) = await CreatePageAsync();
  96. var labelKey = Unique("e2elbl");
  97. var textServer = Unique("e2e-ltx-");
  98. var numberServer = Unique("e2e-lnm-");
  99. try {
  100. await CreateServerWithLabelAsync(page, textServer, labelKey, "production");
  101. await CreateServerWithLabelAsync(page, numberServer, labelKey, "42");
  102. var labelPage = new LabelPagePom(page);
  103. await labelPage.GotoAsync(_fixture.BaseUrl, labelKey);
  104. await labelPage.AssertTitleContainsAsync(labelKey);
  105. // The page classifies each value, not just displays it.
  106. await labelPage.AssertValueAsync(textServer, "production", "TEXT");
  107. await labelPage.AssertValueAsync(numberServer, "42", "NUMBER");
  108. }
  109. catch (Exception) {
  110. await DumpAsync(page);
  111. throw;
  112. }
  113. finally {
  114. await context.CloseAsync();
  115. }
  116. }
  117. // =============================================================
  118. // Label page — filtering
  119. // =============================================================
  120. [Fact]
  121. public async Task Label_Page_Filter_Narrows_The_List_And_Can_Match_Nothing() {
  122. (IBrowserContext context, IPage page) = await CreatePageAsync();
  123. var labelKey = Unique("e2efil");
  124. var keptServer = Unique("e2e-lkp-");
  125. var filteredServer = Unique("e2e-lfl-");
  126. try {
  127. await CreateServerWithLabelAsync(page, keptServer, labelKey, "alpha");
  128. await CreateServerWithLabelAsync(page, filteredServer, labelKey, "beta");
  129. var labelPage = new LabelPagePom(page);
  130. await labelPage.GotoAsync(_fixture.BaseUrl, labelKey);
  131. await labelPage.AssertListsAsync(keptServer);
  132. await labelPage.AssertListsAsync(filteredServer);
  133. // The filter matches on the label value as well as the name.
  134. await labelPage.FilterAsync("alpha");
  135. await labelPage.AssertListsAsync(keptServer);
  136. await labelPage.AssertDoesNotListAsync(filteredServer);
  137. // Nothing matching falls through to its own state, not an empty list.
  138. await labelPage.FilterAsync(Unique("nomatch"));
  139. await labelPage.AssertNoResultsAsync();
  140. }
  141. catch (Exception) {
  142. await DumpAsync(page);
  143. throw;
  144. }
  145. finally {
  146. await context.CloseAsync();
  147. }
  148. }
  149. // =============================================================
  150. // Label page — sort direction
  151. // =============================================================
  152. [Fact]
  153. public async Task Label_Page_Sort_Direction_Reverses_The_List() {
  154. (IBrowserContext context, IPage page) = await CreatePageAsync();
  155. var labelKey = Unique("e2esrt");
  156. var lowServer = Unique("e2e-slo-");
  157. var highServer = Unique("e2e-shi-");
  158. try {
  159. // Numeric values so the default "Value" sort is unambiguous.
  160. await CreateServerWithLabelAsync(page, lowServer, labelKey, "1");
  161. await CreateServerWithLabelAsync(page, highServer, labelKey, "2");
  162. var labelPage = new LabelPagePom(page);
  163. await labelPage.GotoAsync(_fixture.BaseUrl, labelKey);
  164. IReadOnlyList<string> ascending = await labelPage.ListedOrderAsync();
  165. Assert.Equal(new[] { lowServer, highServer }, ascending);
  166. await labelPage.ToggleSortDirectionAsync();
  167. IReadOnlyList<string> descending = await labelPage.ListedOrderAsync();
  168. Assert.Equal(new[] { highServer, lowServer }, descending);
  169. }
  170. catch (Exception) {
  171. await DumpAsync(page);
  172. throw;
  173. }
  174. finally {
  175. await context.CloseAsync();
  176. }
  177. }
  178. // =============================================================
  179. // Helpers
  180. // =============================================================
  181. private async Task CreateServerAsync(IPage page, string name) {
  182. await page.GotoAsync($"{_fixture.BaseUrl}/servers/list");
  183. var list = new ServersListPom(page);
  184. await list.AssertLoadedAsync();
  185. await list.AddServerAsync(name);
  186. if (!page.Url.Contains($"/resources/hardware/{name}", StringComparison.OrdinalIgnoreCase))
  187. await list.OpenServerAsync(name);
  188. var card = new ServerCardPom(page);
  189. await card.AssertVisibleAsync(name);
  190. }
  191. private async Task CreateServerWithTagAsync(IPage page, string name, string tag) {
  192. await CreateServerAsync(page, name);
  193. var card = new ServerCardPom(page);
  194. await card.Tags.AddTagsAsync("server", tag);
  195. await card.Tags.AssertTagVisibleAsync("server", tag);
  196. }
  197. private async Task CreateServerWithLabelAsync(IPage page, string name, string key, string value) {
  198. await CreateServerAsync(page, name);
  199. var card = new ServerCardPom(page);
  200. await card.Labels.AddLabelAsync("server", key, value);
  201. await card.Labels.AssertLabelVisibleAsync("server", key);
  202. }
  203. private async Task CreateSystemWithTagAsync(IPage page, string name, string tag) {
  204. await page.GotoAsync($"{_fixture.BaseUrl}/systems/list");
  205. var list = new SystemsListPom(page);
  206. await list.AssertLoadedAsync();
  207. await list.AddSystemAsync(name);
  208. if (!page.Url.Contains($"/resources/systems/{name}", StringComparison.OrdinalIgnoreCase))
  209. await list.OpenSystemAsync(name);
  210. var card = new SystemCardPom(page);
  211. await card.AssertVisibleAsync(name);
  212. await card.Tags.AddTagsAsync("system", tag);
  213. await card.Tags.AssertTagVisibleAsync("system", tag);
  214. }
  215. private async Task DumpAsync(IPage page) {
  216. _output.WriteLine("TEST FAILED — Capturing diagnostics");
  217. _output.WriteLine($"Current URL: {page.Url}");
  218. var html = await page.ContentAsync();
  219. _output.WriteLine("==== DOM SNAPSHOT START ====");
  220. _output.WriteLine(html);
  221. _output.WriteLine("==== DOM SNAPSHOT END ====");
  222. }
  223. }