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:
1 parent
308da46245
commit
05b3df7373
3 files changed
+51
-6
No files matched your search
@@ -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",
|
||||
);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
Reference in new issue
Block a user