e2e: fix flaky tests by adding page readiness chaeck
Some checks failed
Release Workflow / determine-release-type (push) Has been cancelled
Release Workflow / checks (push) Has been cancelled
Release Workflow / e2e-tests (push) Has been cancelled
Release Workflow / build-images (push) Has been cancelled
Release Workflow / publish-release (push) Has been cancelled
Some checks failed
Release Workflow / determine-release-type (push) Has been cancelled
Release Workflow / checks (push) Has been cancelled
Release Workflow / e2e-tests (push) Has been cancelled
Release Workflow / build-images (push) Has been cancelled
Release Workflow / publish-release (push) Has been cancelled
This commit is contained in:
parent
85e2272a5c
commit
289832d58b
5 changed files with 38 additions and 12 deletions
|
|
@ -4,6 +4,7 @@ import { apiClientMiddleware } from "~/middleware/api-client";
|
|||
import type { QueryClient } from "@tanstack/react-query";
|
||||
import { Toaster } from "~/client/components/ui/sonner";
|
||||
import { useServerEvents } from "~/client/hooks/use-server-events";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()({
|
||||
server: {
|
||||
|
|
@ -34,6 +35,12 @@ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()(
|
|||
|
||||
function RootLayout() {
|
||||
useServerEvents();
|
||||
useEffect(() => {
|
||||
document.body.setAttribute("data-app-ready", "true");
|
||||
return () => {
|
||||
document.body.removeAttribute("data-app-ready");
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import fs from "fs";
|
|||
import { test, expect } from "@playwright/test";
|
||||
import { resetDatabase } from "./helpers/db";
|
||||
import path from "node:path";
|
||||
import { gotoAndWaitForAppReady } from "./helpers/page";
|
||||
|
||||
const authFile = path.join(process.cwd(), "./playwright/.auth/user.json");
|
||||
|
||||
|
|
@ -15,7 +16,7 @@ test.beforeAll(async () => {
|
|||
});
|
||||
|
||||
test("should redirect to onboarding", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await gotoAndWaitForAppReady(page, "/");
|
||||
|
||||
await page.waitForURL(/onboarding/);
|
||||
|
||||
|
|
@ -23,7 +24,7 @@ test("should redirect to onboarding", async ({ page }) => {
|
|||
});
|
||||
|
||||
test("user can register a new account", async ({ page }) => {
|
||||
await page.goto("/onboarding");
|
||||
await gotoAndWaitForAppReady(page, "/onboarding");
|
||||
|
||||
await page.getByRole("textbox", { name: "Email" }).click();
|
||||
await page.getByRole("textbox", { name: "Email" }).fill("test@test.com");
|
||||
|
|
@ -39,7 +40,7 @@ test("user can register a new account", async ({ page }) => {
|
|||
});
|
||||
|
||||
test("user can download recovery key", async ({ page }) => {
|
||||
await page.goto("/login");
|
||||
await gotoAndWaitForAppReady(page, "/login");
|
||||
|
||||
await page.getByRole("textbox", { name: "Username" }).fill("test");
|
||||
await page.getByRole("textbox", { name: "Password" }).fill("password");
|
||||
|
|
@ -69,7 +70,7 @@ test("user can download recovery key", async ({ page }) => {
|
|||
});
|
||||
|
||||
test("can't create another admin user after initial setup", async ({ page }) => {
|
||||
await page.goto("/onboarding");
|
||||
await gotoAndWaitForAppReady(page, "/onboarding");
|
||||
|
||||
await page.getByRole("textbox", { name: "Email" }).click();
|
||||
await page.getByRole("textbox", { name: "Email" }).fill("test@test.com");
|
||||
|
|
@ -85,7 +86,7 @@ test("can't create another admin user after initial setup", async ({ page }) =>
|
|||
});
|
||||
|
||||
test("can login after initial setup", async ({ page }) => {
|
||||
await page.goto("/login");
|
||||
await gotoAndWaitForAppReady(page, "/login");
|
||||
|
||||
await page.getByRole("textbox", { name: "Username" }).fill("test");
|
||||
await page.getByRole("textbox", { name: "Password" }).fill("password");
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
import { gotoAndWaitForAppReady } from "./helpers/page";
|
||||
|
||||
test.describe.configure({ mode: "serial" });
|
||||
|
||||
|
|
@ -16,7 +17,7 @@ test.beforeAll(() => {
|
|||
});
|
||||
|
||||
test("can backup & restore a file", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await gotoAndWaitForAppReady(page, "/");
|
||||
await expect(page).toHaveURL("/volumes");
|
||||
|
||||
// 0. Create a test file in /test-data
|
||||
|
|
@ -75,17 +76,24 @@ test("can backup & restore a file", async ({ page }) => {
|
|||
});
|
||||
|
||||
test("deleting a volume cascades and removes its backup schedule", async ({ page }) => {
|
||||
await page.goto("/backups");
|
||||
await gotoAndWaitForAppReady(page, "/backups");
|
||||
await page.getByText("Test Backup", { exact: true }).first().click();
|
||||
|
||||
await expect(page.getByRole("link", { name: "Test Volume" })).toBeVisible();
|
||||
await page.getByRole("link", { name: "Test Volume" }).click();
|
||||
const volumeLink = page.locator("main").getByRole("link", { name: "Test Volume", exact: true }).first();
|
||||
await expect(volumeLink).toBeVisible();
|
||||
await volumeLink.click();
|
||||
await expect(page).toHaveURL(/\/volumes\/[^/?#]+/);
|
||||
await expect(page.getByText("Volume Configuration", { exact: true })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Delete" })).toBeVisible();
|
||||
|
||||
await page.getByRole("button", { name: "Delete" }).click();
|
||||
await expect(async () => {
|
||||
await page.getByRole("button", { name: "Delete" }).click();
|
||||
await expect(page.getByRole("heading", { name: "Delete volume?" })).toBeVisible();
|
||||
}).toPass({ timeout: 10000 });
|
||||
await expect(page.getByText("All backup schedules associated with this volume will also be removed.")).toBeVisible();
|
||||
await page.getByRole("button", { name: "Delete volume" }).click();
|
||||
await expect(page.getByText("Volume deleted successfully")).toBeVisible();
|
||||
|
||||
await page.goto("/backups");
|
||||
await gotoAndWaitForAppReady(page, "/backups");
|
||||
await expect(page.getByText("Test Backup", { exact: true })).toHaveCount(0);
|
||||
});
|
||||
|
|
|
|||
10
e2e/helpers/page.ts
Normal file
10
e2e/helpers/page.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import type { Page } from "@playwright/test";
|
||||
|
||||
export const waitForAppReady = async (page: Page) => {
|
||||
await page.waitForFunction(() => document.body?.getAttribute("data-app-ready") === "true");
|
||||
};
|
||||
|
||||
export const gotoAndWaitForAppReady = async (page: Page, url: string) => {
|
||||
await page.goto(url, { waitUntil: "domcontentloaded" });
|
||||
await waitForAppReady(page);
|
||||
};
|
||||
|
|
@ -11,7 +11,7 @@ export default defineConfig({
|
|||
use: {
|
||||
baseURL: `http://${process.env.SERVER_IP}:4096`,
|
||||
video: "retain-on-failure",
|
||||
trace: "on-first-retry",
|
||||
trace: "retain-on-failure",
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue