DocsTests.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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 /docs knowledge base. The Blazor Server host reads the
  8. /// docs assets off disk via StaticWebAssetDocsContentProvider; fetching them
  9. /// over HTTP breaks behind a reverse proxy (issue #304), and the failure is
  10. /// silent — the page still renders, just with a "document not found"
  11. /// placeholder. These tests assert on resolved content for that reason.
  12. /// </summary>
  13. public class DocsTests(
  14. PlaywrightFixture fixture,
  15. ITestOutputHelper output) : E2ETestBase(fixture, output) {
  16. private readonly PlaywrightFixture _fixture = fixture;
  17. private readonly ITestOutputHelper _output = output;
  18. // =============================================================
  19. // Overview renders (issue #304 regression)
  20. // =============================================================
  21. [Fact]
  22. public async Task Docs_Overview_Renders_Content_Not_A_Not_Found_Placeholder() {
  23. (IBrowserContext context, IPage page) = await CreatePageAsync();
  24. try {
  25. await page.GotoAsync($"{_fixture.BaseUrl}/docs");
  26. var docs = new DocsPom(page);
  27. await docs.AssertLoadedAsync();
  28. // The index and the default overview document must both resolve.
  29. await docs.AssertIndexResolvedAsync();
  30. await docs.AssertContentResolvedAsync();
  31. }
  32. catch (Exception) {
  33. await DumpAsync(page);
  34. throw;
  35. }
  36. finally {
  37. await context.CloseAsync();
  38. }
  39. }
  40. // =============================================================
  41. // Deep link renders
  42. // =============================================================
  43. [Fact]
  44. public async Task User_Can_Deep_Link_Straight_To_A_Docs_Page() {
  45. (IBrowserContext context, IPage page) = await CreatePageAsync();
  46. try {
  47. // Navigating directly (rather than clicking through) is the path a
  48. // bookmark or an external link takes.
  49. await page.GotoAsync($"{_fixture.BaseUrl}/docs/ssh-config-export");
  50. var docs = new DocsPom(page);
  51. await docs.AssertLoadedAsync();
  52. await docs.AssertContentResolvedAsync();
  53. }
  54. catch (Exception) {
  55. await DumpAsync(page);
  56. throw;
  57. }
  58. finally {
  59. await context.CloseAsync();
  60. }
  61. }
  62. // =============================================================
  63. // Navigation between docs
  64. // =============================================================
  65. [Fact]
  66. public async Task User_Can_Navigate_Between_Docs_From_The_Index() {
  67. (IBrowserContext context, IPage page) = await CreatePageAsync();
  68. try {
  69. await page.GotoAsync($"{_fixture.BaseUrl}/docs");
  70. var docs = new DocsPom(page);
  71. await docs.AssertLoadedAsync();
  72. await docs.AssertIndexResolvedAsync();
  73. await docs.OpenDocAsync("install-guide.md");
  74. await docs.AssertContentResolvedAsync();
  75. // A second hop exercises OnParametersSetAsync re-fetching for an
  76. // already-initialised component, not just the first render.
  77. await docs.OpenDocAsync("versioning.md");
  78. await docs.AssertContentResolvedAsync();
  79. // Back to the overview via the header link.
  80. await docs.HomeLink.ClickAsync();
  81. await page.WaitForURLAsync("**/docs");
  82. await docs.AssertContentResolvedAsync();
  83. }
  84. catch (Exception) {
  85. await DumpAsync(page);
  86. throw;
  87. }
  88. finally {
  89. await context.CloseAsync();
  90. }
  91. }
  92. // =============================================================
  93. // Sidebar search
  94. // =============================================================
  95. [Fact]
  96. public async Task Docs_Search_Filters_The_Index() {
  97. (IBrowserContext context, IPage page) = await CreatePageAsync();
  98. try {
  99. await page.GotoAsync($"{_fixture.BaseUrl}/docs");
  100. var docs = new DocsPom(page);
  101. await docs.AssertLoadedAsync();
  102. await docs.AssertIndexResolvedAsync();
  103. var totalCount = await docs.IndexLinks.CountAsync();
  104. Assert.True(totalCount > 1, $"Expected a multi-entry docs index, found {totalCount}.");
  105. await docs.SearchAsync("ansible");
  106. // Only the ansible guide should survive the filter.
  107. await Assertions.Expect(docs.IndexLink("ansible-generator-guide.md")).ToBeVisibleAsync();
  108. await Assertions.Expect(docs.IndexLink("versioning.md")).ToHaveCountAsync(0);
  109. // Clearing restores the full index.
  110. await docs.SearchAsync("");
  111. await Assertions.Expect(docs.IndexLinks).ToHaveCountAsync(totalCount);
  112. }
  113. catch (Exception) {
  114. await DumpAsync(page);
  115. throw;
  116. }
  117. finally {
  118. await context.CloseAsync();
  119. }
  120. }
  121. private async Task DumpAsync(IPage page) {
  122. _output.WriteLine("TEST FAILED — Capturing diagnostics");
  123. _output.WriteLine($"Current URL: {page.Url}");
  124. var html = await page.ContentAsync();
  125. _output.WriteLine("==== DOM SNAPSHOT START ====");
  126. _output.WriteLine(html);
  127. _output.WriteLine("==== DOM SNAPSHOT END ====");
  128. }
  129. }