Add basic art upload E2E test (#2708)

This commit is contained in:
Kalle
2026-01-08 20:56:04 +02:00
committed by GitHub
parent b370a9ba1b
commit 995ee2ecab
14 changed files with 148 additions and 0 deletions

View File

@@ -22,6 +22,22 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Start MinIO
run: docker compose up -d minio
- name: Wait for MinIO to be ready
run: |
for i in {1..30}; do
if curl -sf http://127.0.0.1:9000/minio/health/live; then
echo "MinIO is ready"
exit 0
fi
echo "Waiting for MinIO... ($i/30)"
sleep 2
done
echo "MinIO failed to start"
exit 1
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
@@ -36,6 +52,10 @@ jobs:
- name: Run E2E tests
run: npm run test:e2e
- name: Stop MinIO
if: always()
run: docker compose down
- uses: actions/upload-artifact@v4
if: failure()
with:

1
.gitignore vendored
View File

@@ -25,5 +25,6 @@ dump
/test-results/
/playwright-report/
/playwright/.cache/
.e2e-minio-started
notepad.txt

View File

@@ -176,6 +176,7 @@ const basicSeeds = (variation?: SeedVariation | null) => [
nzapUser,
users,
fixAdminId,
makeArtists,
adminUserWeaponPool,
userProfiles,
userMapModePreferences,
@@ -264,6 +265,8 @@ function wipeDB() {
"GroupMatchMap",
"GroupMatch",
"Group",
"TaggedArt",
"ArtTag",
"ArtUserMetadata",
"Art",
"UnvalidatedUserSubmittedImage",
@@ -348,6 +351,14 @@ function makeAdminTournamentOrganizer() {
.run();
}
function makeArtists() {
sql
.prepare(
`update "User" set "isArtist" = 1 where id in (${ADMIN_ID}, ${NZAP_TEST_ID})`,
)
.run();
}
function adminUserWeaponPool() {
for (const [i, weaponSplId] of [200, 1100, 2000, 4000].entries()) {
sql

46
e2e/art.spec.ts Normal file
View File

@@ -0,0 +1,46 @@
import path from "node:path";
import { fileURLToPath } from "node:url";
import { NZAP_TEST_ID } from "~/db/seed/constants";
import { expect, impersonate, navigate, seed, test } from "~/utils/playwright";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
test.describe("Art", () => {
test("uploads art as NZAP, admin approves, art displays on user page", async ({
page,
}) => {
await seed(page);
await impersonate(page, NZAP_TEST_ID);
await navigate({ page, url: "/art/new" });
const testImagePath = path.join(__dirname, "fixtures/test-image.png");
await page.locator('input[type="file"]').setInputFiles(testImagePath);
await expect(page.locator("form img")).toBeVisible();
await page.getByRole("button", { name: "Save" }).click();
await expect(page).toHaveURL(/\/u\/.*\/art/);
await expect(page.getByText(/pending moderator approval/i)).toBeVisible();
await impersonate(page);
await navigate({ page, url: "/upload/admin" });
await expect(page.locator("img").first()).toBeVisible();
await page.getByRole("button", { name: /All .* above ok/ }).click();
await expect(page.getByText("All validated!")).toBeVisible();
await navigate({ page, url: "/u/nzap/art" });
const artImage = page.locator("img").first();
await expect(artImage).toBeVisible();
const box = await artImage.boundingBox();
expect(box).not.toBeNull();
expect(box!.width).toBeGreaterThan(0);
expect(box!.height).toBeGreaterThan(0);
});
});

BIN
e2e/fixtures/test-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

View File

@@ -6,11 +6,57 @@ import { E2E_BASE_PORT } from "~/utils/playwright";
const WORKER_COUNT = Number(process.env.E2E_WORKERS) || 4;
const DEBUG = process.env.E2E_DEBUG === "true";
const SERVER_PROCESSES: ChildProcess[] = [];
const MINIO_MARKER_FILE = ".e2e-minio-started";
declare global {
var __E2E_SERVERS__: ChildProcess[];
}
async function waitForMinio(timeout = 60000): Promise<boolean> {
const start = Date.now();
while (Date.now() - start < timeout) {
try {
const response = await fetch("http://127.0.0.1:9000/minio/health/live");
if (response.ok) {
return true;
}
} catch {
// MinIO not ready yet
}
await new Promise((resolve) => setTimeout(resolve, 1000));
}
return false;
}
async function ensureMinioRunning(): Promise<boolean> {
// Check if MinIO is already running
try {
const response = await fetch("http://127.0.0.1:9000/minio/health/live");
if (response.ok) {
// biome-ignore lint/suspicious/noConsole: CLI script output
console.log("MinIO is already running");
return false;
}
} catch {
// MinIO not running, we need to start it
}
// biome-ignore lint/suspicious/noConsole: CLI script output
console.log("Starting MinIO...");
execSync("docker compose up -d minio", { stdio: "inherit" });
const isReady = await waitForMinio();
if (!isReady) {
throw new Error("MinIO failed to start within timeout");
}
// biome-ignore lint/suspicious/noConsole: CLI script output
console.log("MinIO is ready");
fs.writeFileSync(MINIO_MARKER_FILE, "");
return true;
}
function killProcessOnPort(port: number): void {
try {
// Try to find and kill any process on this port (macOS/Linux)
@@ -43,6 +89,9 @@ async function globalSetup(_config: FullConfig) {
// biome-ignore lint/suspicious/noConsole: CLI script output
console.log(`\nStarting e2e test setup with ${WORKER_COUNT} workers...`);
// Start MinIO if not already running
await ensureMinioRunning();
// Build the app once with E2E test flag so VITE_E2E_TEST_RUN is embedded
// Use port 6173 as the base - tests will rewrite URLs as needed
// biome-ignore lint/suspicious/noConsole: CLI script output
@@ -92,6 +141,11 @@ async function globalSetup(_config: FullConfig) {
SESSION_SECRET: "secret",
VITE_SITE_DOMAIN: `http://localhost:${port}`,
VITE_E2E_TEST_RUN: "true",
STORAGE_END_POINT: "http://127.0.0.1:9000",
STORAGE_ACCESS_KEY: "minio-user",
STORAGE_SECRET: "minio-password",
STORAGE_REGION: "us-east-1",
STORAGE_BUCKET: "sendou",
},
detached: false,
});

View File

@@ -1,5 +1,9 @@
import { execSync } from "node:child_process";
import fs from "node:fs";
import type { FullConfig } from "@playwright/test";
const MINIO_MARKER_FILE = ".e2e-minio-started";
declare global {
var __E2E_SERVERS__: import("node:child_process").ChildProcess[];
}
@@ -19,6 +23,18 @@ async function globalTeardown(_config: FullConfig) {
// Give processes a moment to clean up
await new Promise((resolve) => setTimeout(resolve, 1000));
// Stop MinIO if we started it (check for marker file)
if (fs.existsSync(MINIO_MARKER_FILE)) {
// biome-ignore lint/suspicious/noConsole: CLI script output
console.log("Stopping MinIO...");
try {
execSync("docker compose stop minio", { stdio: "inherit" });
} catch {
// Ignore errors - MinIO might already be stopped
}
fs.unlinkSync(MINIO_MARKER_FILE);
}
// biome-ignore lint/suspicious/noConsole: CLI script output
console.log("All servers stopped.\n");
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.