test(e2e): playwright smoke — login/create/edit/save happy path
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -19,6 +19,7 @@ frontend/dist/
|
||||
frontend/.vite/
|
||||
frontend/playwright-report/
|
||||
frontend/test-results/
|
||||
frontend/.pw-data/
|
||||
|
||||
# SPA build output — .gitkeep tracks the folder, everything inside is generated
|
||||
src/VolcanicCore.Api/wwwroot/*
|
||||
|
||||
13
frontend/e2e/fixtures.ts
Normal file
13
frontend/e2e/fixtures.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
export default async function globalSetup() {
|
||||
const dataDir = path.resolve(__dirname, "../.pw-data");
|
||||
// Shell out to `rm -rf` — Node's fs.rm can trip ENOTEMPTY on overlay
|
||||
// filesystems (rootless containers, docker layered volumes) where the
|
||||
// dir looks empty via readdir but rmdir still refuses.
|
||||
spawnSync("rm", ["-rf", dataDir], { stdio: "ignore" });
|
||||
}
|
||||
27
frontend/e2e/smoke.spec.ts
Normal file
27
frontend/e2e/smoke.spec.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test("login → create workflow → drop node → save", async ({ page }) => {
|
||||
await page.goto("/login");
|
||||
await page.getByLabel(/password/i).fill("hunter2");
|
||||
await page.getByRole("button", { name: /sign in/i }).click();
|
||||
|
||||
await expect(page).toHaveURL(/\/workflows$/);
|
||||
await page.getByRole("button", { name: /new workflow/i }).click();
|
||||
await page.getByPlaceholder(/name/i).fill("Smoke Test");
|
||||
await page.getByRole("button", { name: /^create$/i }).click();
|
||||
|
||||
await expect(page.getByText("Smoke Test")).toBeVisible();
|
||||
await page.getByText("Smoke Test").click();
|
||||
|
||||
// On editor. Drag the Webhook tile onto the canvas.
|
||||
const tile = page.locator("aside").filter({ hasText: /Nodes/i }).getByText("Webhook");
|
||||
const canvas = page.locator(".react-flow__pane");
|
||||
|
||||
await tile.dragTo(canvas);
|
||||
await expect(page.locator(".react-flow__node")).toHaveCount(1);
|
||||
|
||||
await page.getByRole("button", { name: /save/i }).click();
|
||||
// No visible confirmation — reload and assert the node persisted.
|
||||
await page.reload();
|
||||
await expect(page.locator(".react-flow__node")).toHaveCount(1);
|
||||
});
|
||||
43
frontend/playwright.config.ts
Normal file
43
frontend/playwright.config.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const dataDir = path.resolve(__dirname, "./.pw-data");
|
||||
const apiPort = 5099;
|
||||
const uiPort = 5173;
|
||||
|
||||
export default defineConfig({
|
||||
testDir: "./e2e",
|
||||
timeout: 60_000,
|
||||
fullyParallel: false,
|
||||
workers: 1,
|
||||
reporter: "list",
|
||||
use: {
|
||||
baseURL: `http://127.0.0.1:${uiPort}`,
|
||||
trace: "off",
|
||||
...devices["Desktop Chrome"],
|
||||
},
|
||||
webServer: [
|
||||
{
|
||||
// Fresh DB per run — the data dir is wiped in globalSetup.
|
||||
command: `dotnet run --project ../src/VolcanicCore.Api --urls http://127.0.0.1:${apiPort}`,
|
||||
cwd: __dirname,
|
||||
env: {
|
||||
ADMIN_PASSWORD: "hunter2",
|
||||
DATA_DIRECTORY: dataDir,
|
||||
},
|
||||
url: `http://127.0.0.1:${apiPort}/api/health`,
|
||||
timeout: 120_000,
|
||||
reuseExistingServer: false,
|
||||
},
|
||||
{
|
||||
command: `pnpm dev --host 127.0.0.1 --port ${uiPort}`,
|
||||
cwd: __dirname,
|
||||
url: `http://127.0.0.1:${uiPort}`,
|
||||
timeout: 60_000,
|
||||
reuseExistingServer: false,
|
||||
},
|
||||
],
|
||||
globalSetup: "./e2e/fixtures.ts",
|
||||
});
|
||||
Reference in New Issue
Block a user