refactor(e2e): implement gotoLogin function for improved login navigation

Introduced a new utility function, gotoLogin, to handle login navigation with retry logic for network changes. Updated existing tests to utilize this function, enhancing stability during login processes. Removed direct page.goto calls in favor of the new method.
This commit is contained in:
Kang committed 2026-10-05 23:13:39 +08:00
1 parent 308da46245
commit 05b3df7373
3 files changed
+51 -6

No files matched your search

+8 -4
View File
@@ -1,5 +1,6 @@
import { test, expect } from "@playwright/test";
import { readFileSync } from "node:fs";
import { gotoLogin } from "./loginNavigation.mjs";
const en = JSON.parse(
readFileSync(new URL("../src/i18n/locales/en.json", import.meta.url), "utf8"),
@@ -19,7 +20,6 @@ test("single administrator: login, SSH, refresh, editor, conflicts, download, ch
const base = process.env.NYATERM_E2E_URL;
const errors = [],
output = [];
page.on("pageerror", (error) => errors.push(String(error)));
page.on("websocket", (socket) => {
if (socket.url().endsWith("/terminal"))
socket.on("framereceived", ({ payload }) => {
@@ -28,7 +28,8 @@ test("single administrator: login, SSH, refresh, editor, conflicts, download, ch
});
const terminalOutput = () => Buffer.concat(output).toString("utf8");
try {
await page.goto(base);
await gotoLogin(page, base);
page.on("pageerror", (error) => errors.push(String(error)));
await page
.getByLabel(t("web.password"))
.fill(process.env.NYATERM_E2E_PASSWORD);
@@ -210,14 +211,17 @@ test("single administrator: login, SSH, refresh, editor, conflicts, download, ch
has: page.getByText("readme.txt", { exact: true }),
});
await expect(readmeRow).toHaveCount(1);
await readmeRow.click();
// Click the visible filename, not the center of an overflowing grid row.
await readmeRow.getByText("readme.txt", { exact: true }).click();
await page
.getByRole("button", {
name: t("fileExplorer.downloadSelected"),
exact: true,
})
.click();
expect(readFileSync(await (await downloaded).path()).toString()).toBe(
const textDownload = await downloaded;
expect(textDownload.suggestedFilename()).toBe("readme.txt");
expect(readFileSync(await textDownload.path()).toString()).toBe(
"browser upload\n",
);
+40
View File
@@ -0,0 +1,40 @@
// Docker network changes can invalidate Chromium's first module requests.
// Retry only that transport failure, before any login or application actions.
export async function gotoLogin(page, url) {
for (let attempt = 0; attempt < 3; attempt++) {
let networkChanged = false;
const pageErrors = [];
const onRequestFailed = (request) => {
if (request.failure()?.errorText === "net::ERR_NETWORK_CHANGED")
networkChanged = true;
};
const onPageError = (error) => pageErrors.push(error);
page.on("requestfailed", onRequestFailed);
page.on("pageerror", onPageError);
try {
await page.goto(url);
await page.locator("#web-password").waitFor({
state: "visible",
timeout: 10_000,
});
if (pageErrors.length)
throw new AggregateError(pageErrors, "Errors during login navigation");
return;
} catch (error) {
networkChanged ||= String(error).includes("net::ERR_NETWORK_CHANGED");
const unexpectedError = pageErrors.some(
(entry) =>
!String(entry).includes(
"Failed to fetch dynamically imported module",
),
);
if (!networkChanged || unexpectedError || attempt === 2) throw error;
console.warn(
`Retrying login navigation after ERR_NETWORK_CHANGED (${attempt + 1}/2)`,
);
} finally {
page.off("requestfailed", onRequestFailed);
page.off("pageerror", onPageError);
}
}
}
+3 -2
View File
@@ -1,6 +1,7 @@
import { test, expect } from "@playwright/test";
import { readFileSync } from "node:fs";
import { randomUUID } from "node:crypto";
import { gotoLogin } from "./loginNavigation.mjs";
const locale = (language) =>
JSON.parse(
@@ -30,7 +31,7 @@ test("login layouts in four languages, light/dark themes and narrow screens", as
{ language, theme },
);
const page = await context.newPage();
await page.goto(process.env.NYATERM_E2E_URL);
await gotoLogin(page, process.env.NYATERM_E2E_URL);
const password = page.getByLabel(t("web.password", locale(language)), {
exact: true,
});
@@ -66,8 +67,8 @@ test("password errors, keyboard login, SSE retry, portable backup and diagnostic
throw new Error("Run scripts/web-local-e2e.mjs or pnpm test:web:e2e");
const base = process.env.NYATERM_E2E_URL;
const errors = [];
await gotoLogin(page, base);
page.on("pageerror", (error) => errors.push(String(error)));
await page.goto(base);
const password = page.getByLabel(t("web.password"), { exact: true });
await expect(password).toBeEnabled();
await password.fill("incorrect-password");