stability.spec.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. const fs = require("node:fs");
  2. const { test, expect } = require("@playwright/test");
  3. const { baseUrls } = require("./helpers/env");
  4. const { stabilityStartButton } = require("./helpers/ui");
  5. async function setShortDuration(page) {
  6. await page.evaluate(() => {
  7. const select = document.querySelector("#durationSelect");
  8. const option = document.createElement("option");
  9. option.value = "1";
  10. option.textContent = "1 Second";
  11. select.insertBefore(option, select.firstChild);
  12. select.value = "1";
  13. select.dispatchEvent(new Event("change", { bubbles: true }));
  14. });
  15. }
  16. async function setAlertThreshold(page, value) {
  17. await page.evaluate(threshold => {
  18. const input = document.querySelector("#alertThreshold");
  19. input.value = String(threshold);
  20. input.dispatchEvent(new Event("input", { bubbles: true }));
  21. }, value);
  22. }
  23. async function waitForSamples(page) {
  24. await expect.poll(() => page.evaluate(() => window.allPingData.length), { timeout: 10_000 }).toBeGreaterThan(0);
  25. }
  26. async function waitForLocalServer(page, serverName) {
  27. await expect(page.locator("#serverArea")).toBeVisible({ timeout: 10_000 });
  28. await expect(page.locator("#server option")).toContainText(serverName, { timeout: 10_000 });
  29. }
  30. test.describe("Stability test", () => {
  31. test("keeps the local start control disabled until server discovery completes", async ({ page }) => {
  32. let releaseServerProbe;
  33. const serverProbe = new Promise(resolve => {
  34. releaseServerProbe = resolve;
  35. });
  36. await page.route(/\/backend\/empty\.php\?cors=true/, async route => {
  37. await serverProbe;
  38. await route.fulfill({ status: 200, body: "" });
  39. });
  40. await page.goto(`${baseUrls.standalone}/stability.html`);
  41. await expect(stabilityStartButton(page)).toHaveClass(/disabled/);
  42. await expect(stabilityStartButton(page)).toHaveClass(/finding/);
  43. await expect(stabilityStartButton(page)).toHaveAttribute("aria-disabled", "true");
  44. await expect(stabilityStartButton(page)).toHaveAttribute("title", "Finding best server...");
  45. await expect(page.locator("#server")).toBeDisabled();
  46. releaseServerProbe();
  47. await expect(page.locator("#server option")).toContainText("local", { timeout: 10_000 });
  48. await expect(stabilityStartButton(page)).not.toHaveClass(/disabled/);
  49. await expect(stabilityStartButton(page)).toHaveAttribute("aria-disabled", "false");
  50. await expect(stabilityStartButton(page)).toHaveAttribute("title", "");
  51. await expect(page.locator("#server")).toBeEnabled();
  52. });
  53. test("runs a short local measurement and exports CSV data", async ({ page }) => {
  54. await page.goto(`${baseUrls.standalone}/stability.html`);
  55. await expect(page).toHaveTitle("LibreSpeed - Stability Test");
  56. await waitForLocalServer(page, "local");
  57. await setShortDuration(page);
  58. await stabilityStartButton(page).click();
  59. await expect(stabilityStartButton(page)).toHaveClass(/running/);
  60. await expect(page.locator("#durationSelect")).toBeDisabled();
  61. await expect(page.locator("#targetSelect")).toBeDisabled();
  62. await expect(page.locator("#server")).toBeDisabled();
  63. await waitForSamples(page);
  64. await expect(page.locator("#statAvg")).not.toHaveText("", { timeout: 5_000 });
  65. await expect(page.locator("#rating")).not.toHaveText("--", { timeout: 5_000 });
  66. await expect(stabilityStartButton(page)).not.toHaveClass(/running/, { timeout: 10_000 });
  67. await expect(page.locator("#durationSelect")).toBeEnabled();
  68. const [download] = await Promise.all([page.waitForEvent("download"), page.locator("#downloadCsvBtn").click()]);
  69. expect(download.suggestedFilename()).toMatch(/^stability_test_.*\.csv$/);
  70. const csvPath = await download.path();
  71. const csv = fs.readFileSync(csvPath, "utf8");
  72. expect(csv).toContain("elapsed_s,ping_ms,failed\n");
  73. expect(csv.trim().split("\n").length).toBeGreaterThan(1);
  74. });
  75. test("supports threshold display, abort, and reset controls", async ({ page }) => {
  76. await page.goto(`${baseUrls.standalone}/stability.html`);
  77. await waitForLocalServer(page, "local");
  78. await setAlertThreshold(page, 40);
  79. await expect(page.locator("#thresholdValue")).toHaveText("40 ms");
  80. await stabilityStartButton(page).click();
  81. await expect(stabilityStartButton(page)).toHaveClass(/running/);
  82. await waitForSamples(page);
  83. await stabilityStartButton(page).click();
  84. await expect(stabilityStartButton(page)).not.toHaveClass(/running/);
  85. await expect(page.locator("#durationSelect")).toBeEnabled();
  86. await expect(page.locator("#targetSelect")).toBeEnabled();
  87. await page.waitForTimeout(700);
  88. await page.locator("#resetBtn").click();
  89. await expect(page.locator("#rating")).toHaveText("--");
  90. await expect(page.locator("#statAvg")).toHaveText("");
  91. await expect.poll(() => page.evaluate(() => window.allPingData.length)).toBe(0);
  92. await expect.poll(() => page.evaluate(() => window.latestData)).toBeNull();
  93. });
  94. test("loads the configured dual-mode server list", async ({ page }) => {
  95. await page.goto(`${baseUrls.dual}/stability.html`);
  96. await expect(page.locator("#serverArea")).toBeVisible({ timeout: 10_000 });
  97. await expect(page.locator("#server option")).toContainText("Local dual backend", { timeout: 10_000 });
  98. });
  99. });