DocsPom.cs 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. using Microsoft.Playwright;
  2. namespace Tests.E2e.PageObjectModels;
  3. public class DocsPom(IPage page) {
  4. // -------------------------------------------------
  5. // Root
  6. // -------------------------------------------------
  7. public ILocator Viewer
  8. => page.GetByTestId("docs-viewer");
  9. public ILocator HomeLink
  10. => page.GetByTestId("docs-home-link");
  11. public ILocator SearchInput
  12. => page.GetByTestId("docs-search-input");
  13. // -------------------------------------------------
  14. // Index
  15. // -------------------------------------------------
  16. /// <summary>
  17. /// Every sidebar entry. Matched on the test-id prefix so the assertions
  18. /// survive docs being added to or removed from docs-index.json.
  19. /// </summary>
  20. public ILocator IndexLinks
  21. => page.Locator("[data-testid^='docs-index-link-']");
  22. /// <summary>
  23. /// A single sidebar entry, addressed by its source file name
  24. /// (e.g. "overview.md" -> docs-index-link-overview-md).
  25. /// </summary>
  26. public ILocator IndexLink(string docFileName)
  27. => page.GetByTestId($"docs-index-link-{Sanitize(docFileName)}");
  28. private static string Sanitize(string value)
  29. => value.Replace(" ", "-")
  30. .Replace("/", "-")
  31. .Replace("\\", "-")
  32. .Replace(".", "-");
  33. // -------------------------------------------------
  34. // High-Level Actions
  35. // -------------------------------------------------
  36. public async Task AssertLoadedAsync() {
  37. await Assertions.Expect(Viewer).ToBeVisibleAsync();
  38. await Assertions.Expect(SearchInput).ToBeVisibleAsync();
  39. }
  40. public async Task OpenDocAsync(string docFileName) {
  41. await IndexLink(docFileName).ClickAsync();
  42. await page.WaitForURLAsync($"**/docs/{docFileName.Replace(".md", "")}");
  43. }
  44. /// <summary>
  45. /// The search box is a plain Blazor @bind, which updates on the change
  46. /// event rather than on input. Playwright's FillAsync only raises input,
  47. /// so the value must be committed with an explicit blur — elsewhere in
  48. /// the app a Generate button happens to do that as a side effect.
  49. /// </summary>
  50. public async Task SearchAsync(string filter) {
  51. await SearchInput.FillAsync(filter);
  52. await SearchInput.BlurAsync();
  53. }
  54. public async Task AssertContentContainsAsync(string text)
  55. => await Assertions.Expect(Viewer).ToContainTextAsync(text);
  56. /// <summary>
  57. /// Guards issue #304: when the Blazor Server host cannot read the docs
  58. /// assets it renders the "document not found" placeholder rather than
  59. /// failing outright, so a passing page load is not on its own proof the
  60. /// content resolved.
  61. /// </summary>
  62. public async Task AssertContentResolvedAsync() {
  63. await Assertions.Expect(Viewer).Not.ToContainTextAsync("document not found");
  64. await Assertions.Expect(Viewer).Not.ToContainTextAsync("loading documentation…");
  65. }
  66. public async Task AssertIndexResolvedAsync() {
  67. await Assertions.Expect(Viewer).Not.ToContainTextAsync("docs index not found");
  68. await Assertions.Expect(IndexLinks.First).ToBeVisibleAsync();
  69. }
  70. }